diff --git a/collections/shedowe19/shieldpm.md b/collections/shedowe19/shieldpm.md new file mode 100644 index 00000000000..effe27bff10 --- /dev/null +++ b/collections/shedowe19/shieldpm.md @@ -0,0 +1,33 @@ +## ShieldPM collection + +A collection to defend nginx against common attacks: + - [ShieldPM](https://github.com/shedowe19/ShieldPM) parser + - base http scenarios (crawl, 404 scan, bf) + - modsecurity support + +## Acquisition template + +Example acquisition for this collection: + +```yaml +filenames: + - /data/shieldpm/nginx/*.log +labels: + type: shieldpm +--- +filenames: + - /data/shieldpm/nginx/*.log +labels: + type: modsecurity +--- +listen_addr: 0.0.0.0:7422 +appsec_config: crowdsecurity/appsec-default +name: appsec +source: appsec +labels: + type: appsec +``` + +## Notes +- Depending on your configuration, paths to log files might change +- Please read more at [ShieldPM GitHub](https://github.com/shedowe19/ShieldPM) diff --git a/collections/shedowe19/shieldpm.yaml b/collections/shedowe19/shieldpm.yaml new file mode 100644 index 00000000000..b58393b17b9 --- /dev/null +++ b/collections/shedowe19/shieldpm.yaml @@ -0,0 +1,21 @@ +parsers: + - shedowe19/shieldpm-logs + - crowdsecurity/cri-logs + - crowdsecurity/dateparse-enrich +collections: + - crowdsecurity/appsec-virtual-patching + - crowdsecurity/appsec-generic-rules + - crowdsecurity/base-http-scenarios + - crowdsecurity/http-cve + - crowdsecurity/modsecurity +scenarios: + - crowdsecurity/nginx-req-limit-exceeded +description: "ShieldPM support: parser and generic http scenarios" +author: shedowe19 +tags: + - linux + - nginx + - nginx-proxy + - shieldpm + - crawl + - scan diff --git a/parsers/s01-parse/shedowe19/shieldpm-logs.yaml b/parsers/s01-parse/shedowe19/shieldpm-logs.yaml new file mode 100644 index 00000000000..e94e02be639 --- /dev/null +++ b/parsers/s01-parse/shedowe19/shieldpm-logs.yaml @@ -0,0 +1,107 @@ +filter: "evt.Parsed.program startsWith 'shieldpm'" +onsuccess: next_stage +name: shedowe19/shieldpm-logs +description: "Parse ShieldPM JSON access logs and error logs" +# ShieldPM uses JSON log format for access logs +nodes: + # JSON Access Log Parser + - filter: "evt.Line contains '{' && evt.Line contains 'remote_addr'" + statics: + - meta: log_type + value: http_access-log + - parsed: time_iso8601 + expression: JsonExtract(evt.Line, "time_iso8601") + - target: evt.StrTime + expression: evt.Parsed.time_iso8601 + - parsed: remote_addr + expression: JsonExtract(evt.Line, "remote_addr") + - meta: source_ip + expression: evt.Parsed.remote_addr + - parsed: status + expression: JsonExtract(evt.Line, "status") + - meta: http_status + expression: evt.Parsed.status + - parsed: request_uri + expression: JsonExtract(evt.Line, "request_uri") + - meta: http_path + expression: evt.Parsed.request_uri + - parsed: request_method + expression: JsonExtract(evt.Line, "request_method") + - meta: http_verb + expression: evt.Parsed.request_method + - parsed: http_user_agent + expression: JsonExtract(evt.Line, "http_user_agent") + - meta: http_user_agent + expression: evt.Parsed.http_user_agent + - parsed: http_host + expression: JsonExtract(evt.Line, "http_host") + - meta: target_fqdn + expression: evt.Parsed.http_host + - meta: service + value: http + # Traditional Access Log (alog format fallback) + - grok: + pattern: '\[%{HTTPDATE:time_local}\] %{DATA:target_fqdn} %{IPORHOST:remote_addr} %{NUMBER:request_time} "%{WORD:verb} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:body_bytes_sent} %{NUMBER:bytes_sent} %{DATA:http_referer} %{GREEDYDATA:http_user_agent}' + apply_on: message + statics: + - meta: log_type + value: http_access-log + - target: evt.StrTime + expression: evt.Parsed.time_local + - meta: source_ip + expression: evt.Parsed.remote_addr + - meta: http_status + expression: evt.Parsed.status + - meta: http_path + expression: evt.Parsed.request + - meta: http_verb + expression: evt.Parsed.verb + - meta: http_user_agent + expression: evt.Parsed.http_user_agent + - meta: target_fqdn + expression: evt.Parsed.target_fqdn + - meta: service + value: http + # Error Log Parser + - grok: + pattern: '%{NGINXERRTIME:time} \[%{LOGLEVEL:loglevel}\] %{NONNEGINT:pid}#%{NONNEGINT:tid}: (\*%{NONNEGINT:cid} )?%{GREEDYDATA:message}, client: %{IPORHOST:remote_addr}, server: %{DATA:target_fqdn}, request: "%{WORD:verb} ([^/]+)?%{URIPATHPARAM:request}( HTTP/%{NUMBER:http_version})?", host: "%{IPORHOST}(:%{NONNEGINT})?"' + apply_on: message + statics: + - meta: log_type + value: http_error-log + - target: evt.StrTime + expression: evt.Parsed.time + - meta: source_ip + expression: evt.Parsed.remote_addr + - meta: service + value: http + pattern_syntax: + NO_DOUBLE_QUOTE: '[^"]+' + onsuccess: next_stage + nodes: + - filter: "evt.Parsed.message contains 'was not found in'" + pattern_syntax: + USER_NOT_FOUND: 'user "%{NO_DOUBLE_QUOTE:username}" was not found in "%{NO_DOUBLE_QUOTE}"' + grok: + pattern: '%{USER_NOT_FOUND}' + apply_on: message + statics: + - meta: sub_type + value: "auth_fail" + - meta: username + expression: evt.Parsed.username + - filter: "evt.Parsed.message contains 'password mismatch'" + pattern_syntax: + PASSWORD_MISMATCH: 'user "%{NO_DOUBLE_QUOTE:username}": password mismatch' + grok: + pattern: '%{PASSWORD_MISMATCH}' + apply_on: message + statics: + - meta: sub_type + value: "auth_fail" + - meta: username + expression: evt.Parsed.username + - filter: "evt.Parsed.message contains 'limiting requests, excess'" + statics: + - meta: sub_type + value: "req_limit_exceeded"