aikido-zen 1.0.2.beta.10-x86_64-linux → 1.0.2-x86_64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/docs/config.md +1 -1
- data/docs/troubleshooting.md +62 -0
- data/lib/aikido/zen/agent.rb +2 -2
- data/lib/aikido/zen/attack.rb +8 -6
- data/lib/aikido/zen/attack_wave/helpers.rb +457 -0
- data/lib/aikido/zen/attack_wave.rb +88 -0
- data/lib/aikido/zen/cache.rb +91 -0
- data/lib/aikido/zen/capped_collections.rb +22 -4
- data/lib/aikido/zen/collector/event.rb +29 -0
- data/lib/aikido/zen/collector/hosts.rb +16 -1
- data/lib/aikido/zen/collector/stats.rb +17 -3
- data/lib/aikido/zen/collector/users.rb +2 -2
- data/lib/aikido/zen/collector.rb +14 -0
- data/lib/aikido/zen/config.rb +29 -6
- data/lib/aikido/zen/context/rack_request.rb +3 -0
- data/lib/aikido/zen/context/rails_request.rb +3 -0
- data/lib/aikido/zen/context.rb +2 -2
- data/lib/aikido/zen/event.rb +47 -2
- data/lib/aikido/zen/helpers.rb +24 -0
- data/lib/aikido/zen/middleware/{check_allowed_addresses.rb → allowed_address_checker.rb} +1 -1
- data/lib/aikido/zen/middleware/attack_wave_protector.rb +46 -0
- data/lib/aikido/zen/middleware/{set_context.rb → context_setter.rb} +1 -1
- data/lib/aikido/zen/middleware/rack_throttler.rb +3 -1
- data/lib/aikido/zen/middleware/request_tracker.rb +8 -3
- data/lib/aikido/zen/outbound_connection.rb +11 -1
- data/lib/aikido/zen/rails_engine.rb +3 -2
- data/lib/aikido/zen/request/rails_router.rb +17 -2
- data/lib/aikido/zen/request.rb +2 -36
- data/lib/aikido/zen/route.rb +50 -0
- data/lib/aikido/zen/runtime_settings/endpoints.rb +37 -8
- data/lib/aikido/zen/runtime_settings.rb +5 -4
- data/lib/aikido/zen/scanners/path_traversal_scanner.rb +3 -2
- data/lib/aikido/zen/scanners/shell_injection_scanner.rb +3 -2
- data/lib/aikido/zen/scanners/sql_injection_scanner.rb +3 -2
- data/lib/aikido/zen/scanners/ssrf_scanner.rb +2 -1
- data/lib/aikido/zen/scanners/stored_ssrf_scanner.rb +5 -1
- data/lib/aikido/zen/sinks/action_controller.rb +3 -1
- data/lib/aikido/zen/sinks/socket.rb +7 -0
- data/lib/aikido/zen/system_info.rb +1 -5
- data/lib/aikido/zen/version.rb +1 -1
- data/lib/aikido/zen.rb +55 -6
- data/tasklib/bench.rake +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5b80e48e80957e7fe9efeb85266a1991240cae201ad495ec639c6520413da5c8
|
|
4
|
+
data.tar.gz: 58aaded86618538cd0834fac6eef212bd9b959c7f980284104b4a2977a08a2d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ec92e2d2445554322e267d0974a54c3794b9fae83627ca6d8ea6fb870743031d940c4fedfe25e9753a7c59a1e34a1f0febfd5247eec1cab2345a123e4d07798
|
|
7
|
+
data.tar.gz: c13181c81ff4544c23d715d78da3437d00deddb0e2ecc996488e868922024b80cca4f854a2fb420917d205f1ded5d0d7e0d973260fcb9007883bd68f40967410
|
data/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Zen will autonomously protect your Ruby applications against:
|
|
|
23
23
|
* 🛡️ [Command injection attacks](https://www.aikido.dev/blog/command-injection-in-2024-unpacked)
|
|
24
24
|
* 🛡️ [Path traversal attacks](https://www.aikido.dev/blog/path-traversal-in-2024-the-year-unpacked)
|
|
25
25
|
* 🛡️ [NoSQL injection attacks](https://www.aikido.dev/blog/web-application-security-vulnerabilities) (coming soon)
|
|
26
|
+
* 🛡️ [Attack waves](https://help.aikido.dev/zen-firewall/zen-features/attack-wave-protection)
|
|
26
27
|
|
|
27
28
|
Zen operates autonomously on the same server as your Ruby app to:
|
|
28
29
|
|
data/docs/config.md
CHANGED
|
@@ -8,7 +8,7 @@ other Rack-based apps).
|
|
|
8
8
|
## Disable Zen
|
|
9
9
|
|
|
10
10
|
In order to fully turn off Zen and prevent it from intercepting any requests or
|
|
11
|
-
reporting back to the Aikido servers, set `
|
|
11
|
+
reporting back to the Aikido servers, set `AIKIDO_DISABLE=true` in your
|
|
12
12
|
environment, or set `Aikido::Zen.config.disabled = true`.
|
|
13
13
|
|
|
14
14
|
(We recommend the ENV variable as you can normally change this easily without
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
If the Zen Firewall isn't working as expected, follow these steps in order to diagnose common issues.
|
|
4
|
+
|
|
5
|
+
## Review installation steps
|
|
6
|
+
|
|
7
|
+
Double-check your setup against the [installation guide](../README.md#installation).
|
|
8
|
+
|
|
9
|
+
Make sure:
|
|
10
|
+
- Your runtime and framework are supported (see [Supported libraries and frameworks](../README.md#supported-libraries-and-frameworks)).
|
|
11
|
+
- The package installed successfully.
|
|
12
|
+
- Your framework-specific integration matches the example in the docs (for Rails, see the example in [docs/rails.md](../docs/rails.md)).
|
|
13
|
+
|
|
14
|
+
## Check connection to Aikido
|
|
15
|
+
|
|
16
|
+
The firewall must be able to reach Aikido's API endpoints.
|
|
17
|
+
|
|
18
|
+
Test connectivity from the same environment where your app runs, following the instructions on this page: https://help.aikido.dev/zen-firewall/miscellaneous/outbound-network-connections-for-zen
|
|
19
|
+
|
|
20
|
+
## Check logs for errors
|
|
21
|
+
|
|
22
|
+
Common places:
|
|
23
|
+
- Local dev: `cat log/development.log` or `tail -f log/development.log`
|
|
24
|
+
- Docker: `docker logs <your-app-container>`
|
|
25
|
+
- systemd: `journalctl -u <your-app-service> --since "1 hour ago"`
|
|
26
|
+
|
|
27
|
+
Tip: search logs for lines containing `Aikido` or `Zen`.
|
|
28
|
+
|
|
29
|
+
For example:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
grep -Ei 'aikido|zen' log/development.log
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Enable debug output temporarily
|
|
36
|
+
|
|
37
|
+
If you use Rails, set the log level to `info` or `debug` while you investigate.
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# config/environments/development.rb or production.rb
|
|
41
|
+
config.log_level = :info # use :debug if needed
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If you have your own logger, make sure Zen logs go to stdout.
|
|
45
|
+
|
|
46
|
+
You can enable Zen debugging mode as follows.
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
# config/initializers/zen.rb
|
|
50
|
+
Rails.application.config.zen.debugging = true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or set `AIKIDO_DEBUG=true` in your environment.
|
|
54
|
+
|
|
55
|
+
## Contact support
|
|
56
|
+
|
|
57
|
+
If you still can't resolve the problem:
|
|
58
|
+
|
|
59
|
+
- Use the in-app chat to reach our support team directly.
|
|
60
|
+
- Or create an issue on [GitHub](https://github.com/AikidoSec/firewall-ruby/issues) with details about your setup, framework, and logs.
|
|
61
|
+
|
|
62
|
+
Include as much context as possible; this helps us respond faster.
|
data/lib/aikido/zen/agent.rb
CHANGED
|
@@ -43,7 +43,7 @@ module Aikido::Zen
|
|
|
43
43
|
@started_at = Time.now.utc
|
|
44
44
|
@collector.start(at: @started_at)
|
|
45
45
|
|
|
46
|
-
if
|
|
46
|
+
if Aikido::Zen.blocking_mode?
|
|
47
47
|
@config.logger.info("Requests identified as attacks will be blocked")
|
|
48
48
|
else
|
|
49
49
|
@config.logger.warn("Non-blocking mode enabled! No requests will be blocked.")
|
|
@@ -106,7 +106,7 @@ module Aikido::Zen
|
|
|
106
106
|
# @raise [Aikido::Zen::UnderAttackError] if the firewall is configured
|
|
107
107
|
# to block requests.
|
|
108
108
|
def handle_attack(attack)
|
|
109
|
-
attack.will_be_blocked! if
|
|
109
|
+
attack.will_be_blocked! if Aikido::Zen.blocking_mode?
|
|
110
110
|
|
|
111
111
|
@config.logger.error(
|
|
112
112
|
format("Zen has %s a %s: %s", attack.blocked? ? "blocked" : "detected", attack.humanized_name, attack.as_json.to_json)
|
data/lib/aikido/zen/attack.rb
CHANGED
|
@@ -10,10 +10,11 @@ module Aikido::Zen
|
|
|
10
10
|
attr_reader :operation
|
|
11
11
|
attr_accessor :sink
|
|
12
12
|
|
|
13
|
-
def initialize(context:, sink:, operation:)
|
|
13
|
+
def initialize(context:, sink:, operation:, stack: nil)
|
|
14
14
|
@context = context
|
|
15
15
|
@operation = operation
|
|
16
16
|
@sink = sink
|
|
17
|
+
@stack = stack
|
|
17
18
|
@blocked = false
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -46,8 +47,9 @@ module Aikido::Zen
|
|
|
46
47
|
kind: kind,
|
|
47
48
|
blocked: blocked?,
|
|
48
49
|
metadata: metadata,
|
|
49
|
-
operation: @operation
|
|
50
|
-
|
|
50
|
+
operation: @operation,
|
|
51
|
+
stack: @stack
|
|
52
|
+
}.compact.merge(input.as_json)
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def exception(*)
|
|
@@ -171,7 +173,7 @@ module Aikido::Zen
|
|
|
171
173
|
def metadata
|
|
172
174
|
{
|
|
173
175
|
hostname: @request.uri.hostname,
|
|
174
|
-
port: @request.uri.port
|
|
176
|
+
port: @request.uri.port.to_s
|
|
175
177
|
}
|
|
176
178
|
end
|
|
177
179
|
end
|
|
@@ -197,7 +199,7 @@ module Aikido::Zen
|
|
|
197
199
|
end
|
|
198
200
|
|
|
199
201
|
def kind
|
|
200
|
-
"
|
|
202
|
+
"stored_ssrf"
|
|
201
203
|
end
|
|
202
204
|
|
|
203
205
|
def input
|
|
@@ -207,7 +209,7 @@ module Aikido::Zen
|
|
|
207
209
|
def metadata
|
|
208
210
|
{
|
|
209
211
|
hostname: @hostname,
|
|
210
|
-
|
|
212
|
+
privateIP: @address
|
|
211
213
|
}
|
|
212
214
|
end
|
|
213
215
|
end
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aikido::Zen
|
|
4
|
+
module AttackWave
|
|
5
|
+
module Helpers
|
|
6
|
+
def self.web_scanner?(context)
|
|
7
|
+
return true if suspicious_request?(context)
|
|
8
|
+
|
|
9
|
+
return true if include_suspicious_payload?(context)
|
|
10
|
+
|
|
11
|
+
false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.suspicious_request?(context)
|
|
15
|
+
request = context.request
|
|
16
|
+
|
|
17
|
+
suspicious_method?(request.request_method) || suspicious_path?(request.path_info)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.suspicious_method?(method)
|
|
21
|
+
SUSPICIOUS_METHODS.include?(method.downcase)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.suspicious_path?(path)
|
|
25
|
+
path_parts = path.downcase.split("/")
|
|
26
|
+
|
|
27
|
+
file_name = path_parts.pop if path_parts.length > 0
|
|
28
|
+
|
|
29
|
+
if file_name
|
|
30
|
+
return true if SUSPICIOUS_FILE_NAMES.include?(file_name)
|
|
31
|
+
|
|
32
|
+
file_name_parts = file_name.split(".")
|
|
33
|
+
|
|
34
|
+
file_extension = file_name_parts.pop if file_name_parts.length > 1
|
|
35
|
+
|
|
36
|
+
return true if SUSPICIOUS_FILE_EXTENSIONS.include?(file_extension)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
path_parts.any? do |directory_name|
|
|
40
|
+
SUSPICIOUS_DIRECTORY_NAMES.include?(directory_name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.include_suspicious_payload?(context)
|
|
45
|
+
context.payloads.each do |payload|
|
|
46
|
+
next unless payload.source == :query
|
|
47
|
+
|
|
48
|
+
value = payload.value.downcase
|
|
49
|
+
|
|
50
|
+
length = value.length
|
|
51
|
+
|
|
52
|
+
next if length < 5 || length > 1_000
|
|
53
|
+
|
|
54
|
+
return true if SUSPICIOUS_SQL_KEYWORDS.any? do |keyword|
|
|
55
|
+
value.include?(keyword)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
SUSPICIOUS_METHODS = [
|
|
63
|
+
"BADMETHOD",
|
|
64
|
+
"BADHTTPMETHOD",
|
|
65
|
+
"BADDATA",
|
|
66
|
+
"BADMTHD",
|
|
67
|
+
"BDMTHD"
|
|
68
|
+
].map(&:downcase).freeze
|
|
69
|
+
|
|
70
|
+
SUSPICIOUS_DIRECTORY_NAMES = [
|
|
71
|
+
".",
|
|
72
|
+
"..",
|
|
73
|
+
".anydesk",
|
|
74
|
+
".aptitude",
|
|
75
|
+
".aws",
|
|
76
|
+
".azure",
|
|
77
|
+
".cache",
|
|
78
|
+
".circleci",
|
|
79
|
+
".config",
|
|
80
|
+
".dbus",
|
|
81
|
+
".docker",
|
|
82
|
+
".drush",
|
|
83
|
+
".TODO: gem",
|
|
84
|
+
".git",
|
|
85
|
+
".github",
|
|
86
|
+
".gnupg",
|
|
87
|
+
".gsutil",
|
|
88
|
+
".hg",
|
|
89
|
+
".idea",
|
|
90
|
+
".java",
|
|
91
|
+
".kube",
|
|
92
|
+
".lftp",
|
|
93
|
+
".minikube",
|
|
94
|
+
".npm",
|
|
95
|
+
".nvm",
|
|
96
|
+
".pki",
|
|
97
|
+
".snap",
|
|
98
|
+
".ssh",
|
|
99
|
+
".subversion",
|
|
100
|
+
".svn",
|
|
101
|
+
".tconn",
|
|
102
|
+
".thunderbird",
|
|
103
|
+
".tor",
|
|
104
|
+
".vagrant.d",
|
|
105
|
+
".vidalia",
|
|
106
|
+
".vim",
|
|
107
|
+
".vmware",
|
|
108
|
+
".vscode",
|
|
109
|
+
"apache",
|
|
110
|
+
"apache2",
|
|
111
|
+
"grub",
|
|
112
|
+
"System32",
|
|
113
|
+
"tmp",
|
|
114
|
+
"xampp",
|
|
115
|
+
"cgi-bin",
|
|
116
|
+
"%systemroot%"
|
|
117
|
+
].map(&:downcase).freeze
|
|
118
|
+
|
|
119
|
+
SUSPICIOUS_FILE_NAMES = [
|
|
120
|
+
".addressbook",
|
|
121
|
+
".atom",
|
|
122
|
+
".bashrc",
|
|
123
|
+
".boto",
|
|
124
|
+
".config",
|
|
125
|
+
".config.json",
|
|
126
|
+
".config.xml",
|
|
127
|
+
".config.yaml",
|
|
128
|
+
".config.yml",
|
|
129
|
+
".envrc",
|
|
130
|
+
".eslintignore",
|
|
131
|
+
".fbcindex",
|
|
132
|
+
".forward",
|
|
133
|
+
".gitattributes",
|
|
134
|
+
".gitconfig",
|
|
135
|
+
".gitignore",
|
|
136
|
+
".gitkeep",
|
|
137
|
+
".gitlab-ci.yaml",
|
|
138
|
+
".gitlab-ci.yml",
|
|
139
|
+
".gitmodules",
|
|
140
|
+
".google_authenticator",
|
|
141
|
+
".hgignore",
|
|
142
|
+
".htaccess",
|
|
143
|
+
".htpasswd",
|
|
144
|
+
".htdigest",
|
|
145
|
+
".ksh_history",
|
|
146
|
+
".lesshst",
|
|
147
|
+
".lhistory",
|
|
148
|
+
".lighttpdpassword",
|
|
149
|
+
".lldb-history",
|
|
150
|
+
".lynx_cookies",
|
|
151
|
+
".my.cnf",
|
|
152
|
+
".mysql_history",
|
|
153
|
+
".nano_history",
|
|
154
|
+
".netrc",
|
|
155
|
+
".node_repl_history",
|
|
156
|
+
".npmrc",
|
|
157
|
+
".nsconfig",
|
|
158
|
+
".nsr",
|
|
159
|
+
".password-store",
|
|
160
|
+
".pearrc",
|
|
161
|
+
".pgpass",
|
|
162
|
+
".php_history",
|
|
163
|
+
".pinerc",
|
|
164
|
+
".proclog",
|
|
165
|
+
".procmailrc",
|
|
166
|
+
".profile",
|
|
167
|
+
".psql_history",
|
|
168
|
+
".python_history",
|
|
169
|
+
".rediscli_history",
|
|
170
|
+
".rhosts",
|
|
171
|
+
".selected_editor",
|
|
172
|
+
".sh_history",
|
|
173
|
+
".sqlite_history",
|
|
174
|
+
".svnignore",
|
|
175
|
+
".tcshrc",
|
|
176
|
+
".tmux.conf",
|
|
177
|
+
".travis.yaml",
|
|
178
|
+
".travis.yml",
|
|
179
|
+
".viminfo",
|
|
180
|
+
".vimrc",
|
|
181
|
+
".www_acl",
|
|
182
|
+
".wwwacl",
|
|
183
|
+
".xauthority",
|
|
184
|
+
".yarnrc",
|
|
185
|
+
".zhistory",
|
|
186
|
+
".zsh_history",
|
|
187
|
+
".zshenv",
|
|
188
|
+
".zshrc",
|
|
189
|
+
"Dockerfile",
|
|
190
|
+
"aws-key.yaml",
|
|
191
|
+
"aws-key.yml",
|
|
192
|
+
"aws.yaml",
|
|
193
|
+
"aws.yml",
|
|
194
|
+
"docker-compose.yaml",
|
|
195
|
+
"docker-compose.yml",
|
|
196
|
+
"npm-shrinkwrap.json",
|
|
197
|
+
"package-lock.json",
|
|
198
|
+
"package.json",
|
|
199
|
+
"phpinfo.php",
|
|
200
|
+
"wp-config.php",
|
|
201
|
+
"wp-config.php3",
|
|
202
|
+
"wp-config.php4",
|
|
203
|
+
"wp-config.php5",
|
|
204
|
+
"wp-config.phtml",
|
|
205
|
+
"composer.json",
|
|
206
|
+
"composer.lock",
|
|
207
|
+
"composer.phar",
|
|
208
|
+
"yarn.lock",
|
|
209
|
+
".env.local",
|
|
210
|
+
".env.development",
|
|
211
|
+
".env.test",
|
|
212
|
+
".env.production",
|
|
213
|
+
".env.prod",
|
|
214
|
+
".env.dev",
|
|
215
|
+
".env.example",
|
|
216
|
+
"php.ini",
|
|
217
|
+
"wp-settings.php",
|
|
218
|
+
"config.asp",
|
|
219
|
+
"config_dev.asp",
|
|
220
|
+
"config-dev.asp",
|
|
221
|
+
"config.dev.asp",
|
|
222
|
+
"config_prod.asp",
|
|
223
|
+
"config-prod.asp",
|
|
224
|
+
"config.prod.asp",
|
|
225
|
+
"config.sample.asp",
|
|
226
|
+
"config-sample.asp",
|
|
227
|
+
"config_sample.asp",
|
|
228
|
+
"config_test.asp",
|
|
229
|
+
"config-test.asp",
|
|
230
|
+
"config.test.asp",
|
|
231
|
+
"config.ini",
|
|
232
|
+
"config_dev.ini",
|
|
233
|
+
"config-dev.ini",
|
|
234
|
+
"config.dev.ini",
|
|
235
|
+
"config_prod.ini",
|
|
236
|
+
"config-prod.ini",
|
|
237
|
+
"config.prod.ini",
|
|
238
|
+
"config.sample.ini",
|
|
239
|
+
"config-sample.ini",
|
|
240
|
+
"config_sample.ini",
|
|
241
|
+
"config_test.ini",
|
|
242
|
+
"config-test.ini",
|
|
243
|
+
"config.test.ini",
|
|
244
|
+
"config.json",
|
|
245
|
+
"config_dev.json",
|
|
246
|
+
"config-dev.json",
|
|
247
|
+
"config.dev.json",
|
|
248
|
+
"config_prod.json",
|
|
249
|
+
"config-prod.json",
|
|
250
|
+
"config.prod.json",
|
|
251
|
+
"config.sample.json",
|
|
252
|
+
"config-sample.json",
|
|
253
|
+
"config_sample.json",
|
|
254
|
+
"config_test.json",
|
|
255
|
+
"config-test.json",
|
|
256
|
+
"config.test.json",
|
|
257
|
+
"config.php",
|
|
258
|
+
"config_dev.php",
|
|
259
|
+
"config-dev.php",
|
|
260
|
+
"config.dev.php",
|
|
261
|
+
"config_prod.php",
|
|
262
|
+
"config-prod.php",
|
|
263
|
+
"config.prod.php",
|
|
264
|
+
"config.sample.php",
|
|
265
|
+
"config-sample.php",
|
|
266
|
+
"config_sample.php",
|
|
267
|
+
"config_test.php",
|
|
268
|
+
"config-test.php",
|
|
269
|
+
"config.test.php",
|
|
270
|
+
"config.pl",
|
|
271
|
+
"config_dev.pl",
|
|
272
|
+
"config-dev.pl",
|
|
273
|
+
"config.dev.pl",
|
|
274
|
+
"config_prod.pl",
|
|
275
|
+
"config-prod.pl",
|
|
276
|
+
"config.prod.pl",
|
|
277
|
+
"config.sample.pl",
|
|
278
|
+
"config-sample.pl",
|
|
279
|
+
"config_sample.pl",
|
|
280
|
+
"config_test.pl",
|
|
281
|
+
"config-test.pl",
|
|
282
|
+
"config.test.pl",
|
|
283
|
+
"config.py",
|
|
284
|
+
"config_dev.py",
|
|
285
|
+
"config-dev.py",
|
|
286
|
+
"config.dev.py",
|
|
287
|
+
"config_prod.py",
|
|
288
|
+
"config-prod.py",
|
|
289
|
+
"config.prod.py",
|
|
290
|
+
"config.sample.py",
|
|
291
|
+
"config-sample.py",
|
|
292
|
+
"config_sample.py",
|
|
293
|
+
"config_test.py",
|
|
294
|
+
"config-test.py",
|
|
295
|
+
"config.test.py",
|
|
296
|
+
"config.rb",
|
|
297
|
+
"config_dev.rb",
|
|
298
|
+
"config-dev.rb",
|
|
299
|
+
"config.dev.rb",
|
|
300
|
+
"config_prod.rb",
|
|
301
|
+
"config-prod.rb",
|
|
302
|
+
"config.prod.rb",
|
|
303
|
+
"config.sample.rb",
|
|
304
|
+
"config-sample.rb",
|
|
305
|
+
"config_sample.rb",
|
|
306
|
+
"config_test.rb",
|
|
307
|
+
"config-test.rb",
|
|
308
|
+
"config.test.rb",
|
|
309
|
+
"config.toml",
|
|
310
|
+
"config_dev.toml",
|
|
311
|
+
"config-dev.toml",
|
|
312
|
+
"config.dev.toml",
|
|
313
|
+
"config_prod.toml",
|
|
314
|
+
"config-prod.toml",
|
|
315
|
+
"config.prod.toml",
|
|
316
|
+
"config.sample.toml",
|
|
317
|
+
"config-sample.toml",
|
|
318
|
+
"config_sample.toml",
|
|
319
|
+
"config_test.toml",
|
|
320
|
+
"config-test.toml",
|
|
321
|
+
"config.test.toml",
|
|
322
|
+
"config.txt",
|
|
323
|
+
"config_dev.txt",
|
|
324
|
+
"config-dev.txt",
|
|
325
|
+
"config.dev.txt",
|
|
326
|
+
"config_prod.txt",
|
|
327
|
+
"config-prod.txt",
|
|
328
|
+
"config.prod.txt",
|
|
329
|
+
"config.sample.txt",
|
|
330
|
+
"config-sample.txt",
|
|
331
|
+
"config_sample.txt",
|
|
332
|
+
"config_test.txt",
|
|
333
|
+
"config-test.txt",
|
|
334
|
+
"config.test.txt",
|
|
335
|
+
"config.xml",
|
|
336
|
+
"config_dev.xml",
|
|
337
|
+
"config-dev.xml",
|
|
338
|
+
"config.dev.xml",
|
|
339
|
+
"config_prod.xml",
|
|
340
|
+
"config-prod.xml",
|
|
341
|
+
"config.prod.xml",
|
|
342
|
+
"config.sample.xml",
|
|
343
|
+
"config-sample.xml",
|
|
344
|
+
"config_sample.xml",
|
|
345
|
+
"config_test.xml",
|
|
346
|
+
"config-test.xml",
|
|
347
|
+
"config.test.xml",
|
|
348
|
+
"config.yaml",
|
|
349
|
+
"config_dev.yaml",
|
|
350
|
+
"config-dev.yaml",
|
|
351
|
+
"config.dev.yaml",
|
|
352
|
+
"config_prod.yaml",
|
|
353
|
+
"config-prod.yaml",
|
|
354
|
+
"config.prod.yaml",
|
|
355
|
+
"config.sample.yaml",
|
|
356
|
+
"config-sample.yaml",
|
|
357
|
+
"config_sample.yaml",
|
|
358
|
+
"config_test.yaml",
|
|
359
|
+
"config-test.yaml",
|
|
360
|
+
"config.test.yaml",
|
|
361
|
+
"config.yml",
|
|
362
|
+
"config_dev.yml",
|
|
363
|
+
"config-dev.yml",
|
|
364
|
+
"config.dev.yml",
|
|
365
|
+
"config_prod.yml",
|
|
366
|
+
"config-prod.yml",
|
|
367
|
+
"config.prod.yml",
|
|
368
|
+
"config.sample.yml",
|
|
369
|
+
"config-sample.yml",
|
|
370
|
+
"config_sample.yml",
|
|
371
|
+
"config_test.yml",
|
|
372
|
+
"config-test.yml",
|
|
373
|
+
"config.test.yml",
|
|
374
|
+
"boot.ini",
|
|
375
|
+
"gruntfile.js",
|
|
376
|
+
"localsettings.php",
|
|
377
|
+
"my.ini",
|
|
378
|
+
"npm-debug.log",
|
|
379
|
+
"parameters.yml",
|
|
380
|
+
"parameters.yaml",
|
|
381
|
+
"services.yml",
|
|
382
|
+
"services.yaml",
|
|
383
|
+
"web.config",
|
|
384
|
+
"webpack.config.js",
|
|
385
|
+
"config.old",
|
|
386
|
+
"config.inc.php",
|
|
387
|
+
"error.log",
|
|
388
|
+
"access.log",
|
|
389
|
+
".DS_Store",
|
|
390
|
+
"passwd",
|
|
391
|
+
"win.ini",
|
|
392
|
+
"cmd.exe",
|
|
393
|
+
"my.cnf",
|
|
394
|
+
".bash_history",
|
|
395
|
+
"docker-compose-dev.yml",
|
|
396
|
+
"docker-compose.override.yml",
|
|
397
|
+
"docker-compose.dev.yml",
|
|
398
|
+
"Cargo.lock",
|
|
399
|
+
"secrets.yml",
|
|
400
|
+
"secrets.yaml",
|
|
401
|
+
"docker-compose.staging.yml",
|
|
402
|
+
"docker-compose.production.yml",
|
|
403
|
+
"yaws-key.pem",
|
|
404
|
+
"mysql_config.ini",
|
|
405
|
+
"firewall.log",
|
|
406
|
+
"log4j.properties",
|
|
407
|
+
"serviceAccountCredentials.json",
|
|
408
|
+
"haproxy.cfg",
|
|
409
|
+
"service-account-credentials.json",
|
|
410
|
+
"vpn.log",
|
|
411
|
+
"system.log",
|
|
412
|
+
"webuser-auth.xml",
|
|
413
|
+
"fastcgi.conf",
|
|
414
|
+
"smb.conf",
|
|
415
|
+
"iis.log",
|
|
416
|
+
"pom.xml",
|
|
417
|
+
"openapi.json",
|
|
418
|
+
"vim_settings.xml",
|
|
419
|
+
"winscp.ini",
|
|
420
|
+
"ws_ftp.ini"
|
|
421
|
+
].map(&:downcase).freeze
|
|
422
|
+
|
|
423
|
+
SUSPICIOUS_FILE_EXTENSIONS = [
|
|
424
|
+
"env",
|
|
425
|
+
"bak",
|
|
426
|
+
"sql",
|
|
427
|
+
"sqlite",
|
|
428
|
+
"sqlite3",
|
|
429
|
+
"db",
|
|
430
|
+
"old",
|
|
431
|
+
"save",
|
|
432
|
+
"orig",
|
|
433
|
+
"sqlitedb",
|
|
434
|
+
"sqlite3db"
|
|
435
|
+
].map(&:downcase).freeze
|
|
436
|
+
|
|
437
|
+
SUSPICIOUS_SQL_KEYWORDS = [
|
|
438
|
+
"SELECT (CASE WHEN",
|
|
439
|
+
"SELECT COUNT(",
|
|
440
|
+
"SLEEP(",
|
|
441
|
+
"WAITFOR DELAY",
|
|
442
|
+
"SELECT LIKE(CHAR(",
|
|
443
|
+
"INFORMATION_SCHEMA.COLUMNS",
|
|
444
|
+
"INFORMATION_SCHEMA.TABLES",
|
|
445
|
+
"MD5(",
|
|
446
|
+
"DBMS_PIPE.RECEIVE_MESSAGE",
|
|
447
|
+
"SYSIBM.SYSTABLES",
|
|
448
|
+
"RANDOMBLOB(",
|
|
449
|
+
"SELECT * FROM",
|
|
450
|
+
"1'='1",
|
|
451
|
+
"PG_SLEEP(",
|
|
452
|
+
"UNION ALL SELECT",
|
|
453
|
+
"../"
|
|
454
|
+
].map(&:downcase).freeze
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "cache"
|
|
4
|
+
require_relative "attack_wave/helpers"
|
|
5
|
+
|
|
6
|
+
module Aikido::Zen
|
|
7
|
+
module AttackWave
|
|
8
|
+
class Detector
|
|
9
|
+
def initialize(config: Aikido::Zen.config, clock: nil)
|
|
10
|
+
@config = config
|
|
11
|
+
|
|
12
|
+
@event_times = Cache.new(@config.attack_wave_max_cache_entries, ttl: @config.attack_wave_min_time_between_events, clock: clock)
|
|
13
|
+
|
|
14
|
+
@request_counts = Cache.new(@config.attack_wave_max_cache_entries, 0, ttl: @config.attack_wave_min_time_between_requests, clock: clock)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def attack_wave?(context)
|
|
18
|
+
client_ip = context.request.client_ip
|
|
19
|
+
|
|
20
|
+
return false unless client_ip
|
|
21
|
+
|
|
22
|
+
return false if @event_times[client_ip]
|
|
23
|
+
|
|
24
|
+
return false unless AttackWave::Helpers.web_scanner?(context)
|
|
25
|
+
|
|
26
|
+
request_count = @request_counts[client_ip] += 1
|
|
27
|
+
|
|
28
|
+
return false if request_count < @config.attack_wave_threshold
|
|
29
|
+
|
|
30
|
+
@event_times[client_ip] = Time.now.utc
|
|
31
|
+
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Request
|
|
37
|
+
# @return [String]
|
|
38
|
+
attr_reader :ip_address
|
|
39
|
+
|
|
40
|
+
# @return [String]
|
|
41
|
+
attr_reader :user_agent
|
|
42
|
+
|
|
43
|
+
# @return [String]
|
|
44
|
+
attr_reader :source
|
|
45
|
+
|
|
46
|
+
# @param ip_address [String]
|
|
47
|
+
# @param user_agent [String]
|
|
48
|
+
# @param source [String]
|
|
49
|
+
# @return [Aikido::Zen::AttackWave::Request]
|
|
50
|
+
def initialize(ip_address:, user_agent:, source:)
|
|
51
|
+
@ip_address = ip_address
|
|
52
|
+
@user_agent = user_agent
|
|
53
|
+
@source = source
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def as_json
|
|
57
|
+
{
|
|
58
|
+
ipAddress: @ip_address,
|
|
59
|
+
userAgent: @user_agent,
|
|
60
|
+
source: @source
|
|
61
|
+
}.compact
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Attack
|
|
66
|
+
# @return [Hash<String, String>]
|
|
67
|
+
attr_reader :metadata
|
|
68
|
+
|
|
69
|
+
# @return [Aikido::Zen::Actor]
|
|
70
|
+
attr_reader :user
|
|
71
|
+
|
|
72
|
+
# @param metadata [Hash<String, String>]
|
|
73
|
+
# @param metadata [Aikido::Zen::Actor]
|
|
74
|
+
# @return [Aikido::Zen::AttackWave::Attack]
|
|
75
|
+
def initialize(metadata:, user:)
|
|
76
|
+
@metadata = metadata
|
|
77
|
+
@user = user
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def as_json
|
|
81
|
+
{
|
|
82
|
+
metadata: @metadata.as_json,
|
|
83
|
+
user: @user.as_json
|
|
84
|
+
}.compact
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|