diff --git a/regex-assembly/932390.ra b/regex-assembly/932390.ra new file mode 100644 index 0000000000..ab299319ae --- /dev/null +++ b/regex-assembly/932390.ra @@ -0,0 +1,23 @@ +##! Please refer to the documentation at +##! https://coreruleset.org/docs/development/regex_assembly/. + +##! Detect shell fork bomb patterns. +##! A fork bomb defines a function whose body recursively calls itself +##! via a pipe or background operator, causing exponential process +##! creation. The structural pattern is: +##! () { <| or &> }; +##! +##! The function name can be any identifier including special bash +##! builtins like : (colon) or . (dot). +##! +##! Either pipe (|) or background (&) is sufficient for a fork bomb: +##! :(){ :|:& };: — classic (pipe + background) +##! f(){ f & f; };f — background only +##! f(){ f() | f(); };f — pipe with explicit calls +##! f(){ f() & f(); };f — background with explicit calls + +##! Function name: word chars plus : and . (bash builtins) +##!> define func-name [\w:.]+ + +##! Match: name() { ... (| or &) ... } +{{func-name}}\s*\(\s*\)\s*\{[^}]+[|&][^}]+\} diff --git a/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf b/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf index bdeef2b1dc..a1e6852281 100644 --- a/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf +++ b/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf @@ -1796,6 +1796,47 @@ SecRule REQUEST_HEADERS:User-Agent|REQUEST_HEADERS:Referer "@pmFromFile unix-she setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ setvar:'tx.inbound_anomaly_score_pl2=+%{tx.critical_anomaly_score}'" +# [ Shell fork bomb detection ] +# +# This rule detects shell fork bomb patterns, where a function is defined +# whose body recursively calls itself via pipe and / or background process, +# causing exponential process creation that exhausts system resources. +# +# Examples: +# :(){ :|:& };: +# f(){ f|f& };f +# test() { test | test & }; test +# +# The detection targets the structural pattern: +# () { <| or &> }; +# where can be any identifier including bash builtins like : or . +# +# Regular expression generated from regex-assembly/932390.ra. +# To update the regular expression run the following shell script +# (consult https://coreruleset.org/docs/development/regex_assembly/ for details): +# crs-toolchain regex update 932390 +# +SecRule REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Cookie|ARGS_NAMES|ARGS|XML:/* "@rx [\.0-:A-Z_a-z]+[\s\x0b]*\([\s\x0b]*\)[\s\x0b]*\{[^\}]+[&\|][^\}]+\}" \ + "id:932390,\ + phase:2,\ + block,\ + capture,\ + t:none,t:urlDecodeUni,\ + msg:'Remote Command Execution: Shell Fork Bomb',\ + logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\ + tag:'application-multi',\ + tag:'language-shell',\ + tag:'platform-unix',\ + tag:'attack-rce',\ + tag:'paranoia-level/2',\ + tag:'OWASP_CRS',\ + tag:'OWASP_CRS/ATTACK-RCE',\ + tag:'capec/1000/152/248/88',\ + ver:'OWASP_CRS/4.25.0-dev',\ + severity:'CRITICAL',\ + setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\ + setvar:'tx.inbound_anomaly_score_pl2=+%{tx.critical_anomaly_score}'" + SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 3" "id:932015,phase:1,pass,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.25.0-dev',skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" SecRule TX:DETECTION_PARANOIA_LEVEL "@lt 3" "id:932016,phase:2,pass,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.25.0-dev',skipAfter:END-REQUEST-932-APPLICATION-ATTACK-RCE" diff --git a/tests/regression/tests/REQUEST-932-APPLICATION-ATTACK-RCE/932390.yaml b/tests/regression/tests/REQUEST-932-APPLICATION-ATTACK-RCE/932390.yaml new file mode 100644 index 0000000000..88ece496d7 --- /dev/null +++ b/tests/regression/tests/REQUEST-932-APPLICATION-ATTACK-RCE/932390.yaml @@ -0,0 +1,274 @@ +--- +meta: + author: "fzipitria" + description: "Shell fork bomb detection" +rule_id: 932390 +tests: + - test_id: 1 + desc: "classic fork bomb with colon function name" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=%3A%28%29%7B+%3A%7C%3A%26+%7D%3B%3A" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 2 + desc: "fork bomb with single letter function name" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=f%28%29%7B+f%7Cf%26+%7D%3Bf" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 3 + desc: "fork bomb with word function name and spaces" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=test%28%29+%7B+test+%7C+test+%26+%7D+test" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 4 + desc: "fork bomb with dot function name (bash source builtin)" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=.%28%29%7B+.%7C.%26+%7D%3B." + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 5 + desc: "fork bomb with no spaces (compact form)" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=%3A%28%29%7B%3A%7C%3A%26%7D%3B%3A" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 6 + desc: "fork bomb in query string via GET" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: GET + port: 80 + uri: "/get?cmd=%3A%28%29%7B+%3A%7C%3A%26+%7D%3B%3A" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 7 + desc: "fork bomb preceded by command separator" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=%3B+test%28%29+%7B+test+%7C+test+%26+%7D+test" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 8 + desc: "fork bomb with longer function name" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=myfunc%28%29%7B+myfunc%7Cmyfunc%26+%7D%3Bmyfunc" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 9 + desc: "fork bomb in Referer header" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + Referer: ":(){ :|:& };:" + method: GET + port: 80 + uri: / + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 10 + desc: "fork bomb in User-Agent header" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "f(){ f|f& };f" + method: GET + port: 80 + uri: / + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 11 + desc: "fork bomb with background only, no pipe" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=f%28%29%7B+f+%26+f%3B+%7D%3Bf" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 12 + desc: "fork bomb with explicit recursive calls using pipe" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=f%28%29%7B+f%28%29+%7C+f%28%29%3B+%7D%3B+f" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 13 + desc: "fork bomb with explicit recursive calls using background" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "cmd=f%28%29%7B+f%28%29+%26+f%28%29%3B+%7D%3B+f" + version: "HTTP/1.1" + output: + log: + expect_ids: [932390] + - test_id: 14 + desc: "negative test - normal parentheses in value" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: GET + port: 80 + uri: "/get?q=hello+world+(test)" + version: "HTTP/1.1" + output: + log: + no_expect_ids: [932390] + - test_id: 15 + desc: "negative test - normal function-like syntax without pipe and background" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: POST + port: 80 + uri: / + data: "code=myfunction%28%29%7B+echo+hello+%7D" + version: "HTTP/1.1" + output: + log: + no_expect_ids: [932390] + - test_id: 16 + desc: "negative test - normal URL with colon" + stages: + - input: + dest_addr: 127.0.0.1 + headers: + Accept: "*/*" + Host: localhost + User-Agent: "OWASP CRS test agent" + method: GET + port: 80 + uri: "/get?url=http%3A%2F%2Fexample.com%3A8080%2Fpath" + version: "HTTP/1.1" + output: + log: + no_expect_ids: [932390]