diff --git a/.tests/sharry-bf/config.yaml b/.tests/sharry-bf/config.yaml new file mode 100644 index 00000000000..fa36d02e88c --- /dev/null +++ b/.tests/sharry-bf/config.yaml @@ -0,0 +1,11 @@ +parsers: + - crowdsecurity/syslog-logs + - crowdsecurity/dateparse-enrich + - ./parsers/s01-parse/plague-doctor/sharry-logs.yaml +scenarios: + - ./scenarios/plague-doctor/sharry-bf.yaml +postoverflows: + - "" +log_file: sharry.log +log_type: sharry +ignore_parsers: true diff --git a/.tests/sharry-bf/parser.assert b/.tests/sharry-bf/parser.assert new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.tests/sharry-bf/scenario.assert b/.tests/sharry-bf/scenario.assert new file mode 100644 index 00000000000..8f68827cbec --- /dev/null +++ b/.tests/sharry-bf/scenario.assert @@ -0,0 +1,44 @@ +len(results) == 1 +"1.136.110.240" in results[0].Overflow.GetSources() +results[0].Overflow.Sources["1.136.110.240"].IP == "1.136.110.240" +results[0].Overflow.Sources["1.136.110.240"].Range == "" +results[0].Overflow.Sources["1.136.110.240"].GetScope() == "Ip" +results[0].Overflow.Sources["1.136.110.240"].GetValue() == "1.136.110.240" +results[0].Overflow.Alert.Events[0].GetMeta("datasource_path") == "sharry.log" +results[0].Overflow.Alert.Events[0].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[0].GetMeta("log_type") == "sharry_failed_auth" +results[0].Overflow.Alert.Events[0].GetMeta("service") == "sharry" +results[0].Overflow.Alert.Events[0].GetMeta("source_ip") == "1.136.110.240" +results[0].Overflow.Alert.Events[0].GetMeta("timestamp") == "2026-02-12T12:50:22+11:00" +results[0].Overflow.Alert.Events[0].GetMeta("username") == "Thgg" +results[0].Overflow.Alert.Events[1].GetMeta("datasource_path") == "sharry.log" +results[0].Overflow.Alert.Events[1].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[1].GetMeta("log_type") == "sharry_failed_auth" +results[0].Overflow.Alert.Events[1].GetMeta("service") == "sharry" +results[0].Overflow.Alert.Events[1].GetMeta("source_ip") == "1.136.110.240" +results[0].Overflow.Alert.Events[1].GetMeta("timestamp") == "2026-02-12T12:50:25+11:00" +results[0].Overflow.Alert.Events[1].GetMeta("username") == "Thgg" +results[0].Overflow.Alert.Events[2].GetMeta("datasource_path") == "sharry.log" +results[0].Overflow.Alert.Events[2].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[2].GetMeta("log_type") == "sharry_failed_auth" +results[0].Overflow.Alert.Events[2].GetMeta("service") == "sharry" +results[0].Overflow.Alert.Events[2].GetMeta("source_ip") == "1.136.110.240" +results[0].Overflow.Alert.Events[2].GetMeta("timestamp") == "2026-02-12T12:50:27+11:00" +results[0].Overflow.Alert.Events[2].GetMeta("username") == "Thgg" +results[0].Overflow.Alert.Events[3].GetMeta("datasource_path") == "sharry.log" +results[0].Overflow.Alert.Events[3].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[3].GetMeta("log_type") == "sharry_failed_auth" +results[0].Overflow.Alert.Events[3].GetMeta("service") == "sharry" +results[0].Overflow.Alert.Events[3].GetMeta("source_ip") == "1.136.110.240" +results[0].Overflow.Alert.Events[3].GetMeta("timestamp") == "2026-02-12T12:50:29+11:00" +results[0].Overflow.Alert.Events[3].GetMeta("username") == "Thgg" +results[0].Overflow.Alert.Events[4].GetMeta("datasource_path") == "sharry.log" +results[0].Overflow.Alert.Events[4].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[4].GetMeta("log_type") == "sharry_failed_auth" +results[0].Overflow.Alert.Events[4].GetMeta("service") == "sharry" +results[0].Overflow.Alert.Events[4].GetMeta("source_ip") == "1.136.110.240" +results[0].Overflow.Alert.Events[4].GetMeta("timestamp") == "2026-02-12T12:50:31+11:00" +results[0].Overflow.Alert.Events[4].GetMeta("username") == "Thgg" +results[0].Overflow.Alert.GetScenario() == "plague-doctor/sharry-bf" +results[0].Overflow.Alert.Remediation == true +results[0].Overflow.Alert.GetEventsCount() == 5 diff --git a/.tests/sharry-bf/sharry.log b/.tests/sharry-bf/sharry.log new file mode 100644 index 00000000000..fe8d40ffec1 --- /dev/null +++ b/.tests/sharry-bf/sharry.log @@ -0,0 +1,5 @@ +2026.02.12 12:50:22:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 +2026.02.12 12:50:25:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 +2026.02.12 12:50:27:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 +2026.02.12 12:50:29:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 +2026.02.12 12:50:31:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 diff --git a/.tests/sharry-logs/config.yaml b/.tests/sharry-logs/config.yaml new file mode 100644 index 00000000000..39c76d56f1e --- /dev/null +++ b/.tests/sharry-logs/config.yaml @@ -0,0 +1,10 @@ +parsers: + - crowdsecurity/syslog-logs + - crowdsecurity/dateparse-enrich + - ./parsers/s01-parse/plague-doctor/sharry-logs.yaml +scenarios: + - "" +postoverflows: + - "" +log_file: sharry.log +log_type: sharry diff --git a/.tests/sharry-logs/parser.assert b/.tests/sharry-logs/parser.assert new file mode 100644 index 00000000000..2b5e6b59a8e --- /dev/null +++ b/.tests/sharry-logs/parser.assert @@ -0,0 +1,81 @@ +len(results) == 4 +len(results["s00-raw"]["crowdsecurity/non-syslog"]) == 3 +results["s00-raw"]["crowdsecurity/non-syslog"][0].Success == true +results["s00-raw"]["crowdsecurity/non-syslog"][0].Evt.Parsed["message"] == "2026.02.12 12:50:22:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240" +results["s00-raw"]["crowdsecurity/non-syslog"][0].Evt.Parsed["program"] == "sharry" +results["s00-raw"]["crowdsecurity/non-syslog"][0].Evt.Meta["datasource_path"] == "sharry.log" +results["s00-raw"]["crowdsecurity/non-syslog"][0].Evt.Meta["datasource_type"] == "file" +results["s00-raw"]["crowdsecurity/non-syslog"][0].Evt.Whitelisted == false +results["s00-raw"]["crowdsecurity/non-syslog"][1].Success == true +results["s00-raw"]["crowdsecurity/non-syslog"][1].Evt.Parsed["message"] == "2026.02.12 12:50:25:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240" +results["s00-raw"]["crowdsecurity/non-syslog"][1].Evt.Parsed["program"] == "sharry" +results["s00-raw"]["crowdsecurity/non-syslog"][1].Evt.Meta["datasource_path"] == "sharry.log" +results["s00-raw"]["crowdsecurity/non-syslog"][1].Evt.Meta["datasource_type"] == "file" +results["s00-raw"]["crowdsecurity/non-syslog"][1].Evt.Whitelisted == false +results["s00-raw"]["crowdsecurity/non-syslog"][2].Success == true +results["s00-raw"]["crowdsecurity/non-syslog"][2].Evt.Parsed["message"] == "2026.02.12 12:45:23:0001 [io-comp...] [INFO ] org.http4s.server.middleware.Logger - HTTP/1.1 200 OK" +results["s00-raw"]["crowdsecurity/non-syslog"][2].Evt.Parsed["program"] == "sharry" +results["s00-raw"]["crowdsecurity/non-syslog"][2].Evt.Meta["datasource_path"] == "sharry.log" +results["s00-raw"]["crowdsecurity/non-syslog"][2].Evt.Meta["datasource_type"] == "file" +results["s00-raw"]["crowdsecurity/non-syslog"][2].Evt.Whitelisted == false +len(results["s00-raw"]["crowdsecurity/syslog-logs"]) == 3 +results["s00-raw"]["crowdsecurity/syslog-logs"][0].Success == false +results["s00-raw"]["crowdsecurity/syslog-logs"][1].Success == false +results["s00-raw"]["crowdsecurity/syslog-logs"][2].Success == false +len(results["s01-parse"]["plague-doctor/sharry-logs"]) == 3 +results["s01-parse"]["plague-doctor/sharry-logs"][0].Success == true +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Parsed["message"] == "2026.02.12 12:50:22:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Parsed["program"] == "sharry" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Parsed["source_ip"] == "1.136.110.240" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Parsed["timestamp"] == "2026.02.12 12:50:22:0000" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Parsed["username"] == "Thgg" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Meta["datasource_path"] == "sharry.log" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Meta["datasource_type"] == "file" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Meta["log_type"] == "sharry_failed_auth" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Meta["service"] == "sharry" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Meta["source_ip"] == "1.136.110.240" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Meta["username"] == "Thgg" +results["s01-parse"]["plague-doctor/sharry-logs"][0].Evt.Whitelisted == false +results["s01-parse"]["plague-doctor/sharry-logs"][1].Success == true +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Parsed["message"] == "2026.02.12 12:50:25:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Parsed["program"] == "sharry" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Parsed["source_ip"] == "1.136.110.240" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Parsed["timestamp"] == "2026.02.12 12:50:25:0000" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Parsed["username"] == "Thgg" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Meta["datasource_path"] == "sharry.log" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Meta["datasource_type"] == "file" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Meta["log_type"] == "sharry_failed_auth" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Meta["service"] == "sharry" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Meta["source_ip"] == "1.136.110.240" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Meta["username"] == "Thgg" +results["s01-parse"]["plague-doctor/sharry-logs"][1].Evt.Whitelisted == false +results["s01-parse"]["plague-doctor/sharry-logs"][2].Success == false +len(results["s02-enrich"]["crowdsecurity/dateparse-enrich"]) == 2 +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Success == true +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Parsed["message"] == "2026.02.12 12:50:22:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Parsed["program"] == "sharry" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Parsed["source_ip"] == "1.136.110.240" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Parsed["timestamp"] == "2026.02.12 12:50:22:0000" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Parsed["username"] == "Thgg" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["datasource_path"] == "sharry.log" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["datasource_type"] == "file" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["log_type"] == "sharry_failed_auth" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["service"] == "sharry" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["source_ip"] == "1.136.110.240" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["timestamp"] == "2026-02-12T12:50:22+11:00" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Meta["username"] == "Thgg" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][0].Evt.Whitelisted == false +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Success == true +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Parsed["message"] == "2026.02.12 12:50:25:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Parsed["program"] == "sharry" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Parsed["source_ip"] == "1.136.110.240" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Parsed["timestamp"] == "2026.02.12 12:50:25:0000" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Parsed["username"] == "Thgg" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["datasource_path"] == "sharry.log" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["datasource_type"] == "file" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["log_type"] == "sharry_failed_auth" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["service"] == "sharry" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["source_ip"] == "1.136.110.240" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["timestamp"] == "2026-02-12T12:50:25+11:00" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Meta["username"] == "Thgg" +results["s02-enrich"]["crowdsecurity/dateparse-enrich"][1].Evt.Whitelisted == false diff --git a/.tests/sharry-logs/sharry.log b/.tests/sharry-logs/sharry.log new file mode 100644 index 00000000000..0255fca0dfd --- /dev/null +++ b/.tests/sharry-logs/sharry.log @@ -0,0 +1,3 @@ +2026.02.12 12:50:22:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 +2026.02.12 12:50:25:0000 [io-comp...] [INFO ] sharry.restserver.routes.LoginRoutes.makeResponse:232 - Authentication attempt failure for username Thgg from ip 1.136.110.240 +2026.02.12 12:45:23:0001 [io-comp...] [INFO ] org.http4s.server.middleware.Logger - HTTP/1.1 200 OK diff --git a/collections/plague-doctor/sharry.md b/collections/plague-doctor/sharry.md new file mode 100644 index 00000000000..9871cf9f996 --- /dev/null +++ b/collections/plague-doctor/sharry.md @@ -0,0 +1,19 @@ +# Description + +A collection to defend [Sharry](https://github.com/eikek/sharry) +deployments against common attacks: + +- Sharry parser +- Sharry bruteforce detection + +## Acquisition template + +Example acquisition for this collection: + +```yaml +--- +filenames: + - /var/log/sharry/*.log +labels: + type: sharry +``` diff --git a/collections/plague-doctor/sharry.yaml b/collections/plague-doctor/sharry.yaml new file mode 100644 index 00000000000..681b30292e5 --- /dev/null +++ b/collections/plague-doctor/sharry.yaml @@ -0,0 +1,10 @@ +parsers: + - plague-doctor/sharry-logs +scenarios: + - plague-doctor/sharry-bf +description: "Sharry: parser and brute-force detection" +author: plague-doctor +tags: + - linux + - brute-force + - sharry diff --git a/parsers/s01-parse/plague-doctor/sharry-logs.md b/parsers/s01-parse/plague-doctor/sharry-logs.md new file mode 100644 index 00000000000..361dd914bea --- /dev/null +++ b/parsers/s01-parse/plague-doctor/sharry-logs.md @@ -0,0 +1,15 @@ +# Description + +A parser that will search for unauthorized access to Sharry. + +## Acquisition template + +Example acquisition for this collection: + +```yaml +--- +filenames: + - /var/log/sharry/*.log +labels: + type: sharry +``` diff --git a/parsers/s01-parse/plague-doctor/sharry-logs.yaml b/parsers/s01-parse/plague-doctor/sharry-logs.yaml new file mode 100644 index 00000000000..b7af53750a3 --- /dev/null +++ b/parsers/s01-parse/plague-doctor/sharry-logs.yaml @@ -0,0 +1,28 @@ +onsuccess: next_stage +#debug: true +filter: "Upper(evt.Parsed.program) == 'SHARRY'" +name: plague-doctor/sharry-logs +description: "Parse Sharry logs" +pattern_syntax: + SHARRY_DATE: "%{YEAR}\\.%{MONTHNUM2}\\.%{MONTHDAY} %{TIME}:%{INT}" + SHARRY_THREAD: "\\[%{DATA:thread}\\]" + SHARRY_LEVEL: "\\[%{LOGLEVEL:level}\\s*\\]" + SHARRY_CLASS: "%{JAVACLASS:class}:%{INT:line}" +nodes: + - grok: + pattern: "^%{SHARRY_DATE:timestamp} %{SHARRY_THREAD} %{SHARRY_LEVEL} %{SHARRY_CLASS} - Authentication attempt failure for username %{USERNAME:username} from ip %{IP:source_ip}" + apply_on: message + statics: + - meta: log_type + value: sharry_failed_auth +statics: + - meta: service + value: sharry + - meta: source_ip + expression: "evt.Parsed.source_ip" + - target: evt.StrTimeFormat + value: "2006.01.02 15:04:05:0000 -0700" + - target: evt.StrTime + expression: evt.Parsed.timestamp + " +0000" + - meta: username + expression: "evt.Parsed.username" diff --git a/scenarios/plague-doctor/sharry-bf.md b/scenarios/plague-doctor/sharry-bf.md new file mode 100644 index 00000000000..220546eb217 --- /dev/null +++ b/scenarios/plague-doctor/sharry-bf.md @@ -0,0 +1,5 @@ +# Description + +Detect failed Sharry authentications: + +- 5 failed authentication attempts within 20 second leakspeed diff --git a/scenarios/plague-doctor/sharry-bf.yaml b/scenarios/plague-doctor/sharry-bf.yaml new file mode 100644 index 00000000000..0cc8056dd58 --- /dev/null +++ b/scenarios/plague-doctor/sharry-bf.yaml @@ -0,0 +1,20 @@ +# Sharry bruteforce +type: leaky +name: plague-doctor/sharry-bf +description: "Detect Sharry bruteforce attacks" +filter: "evt.Meta.service == 'sharry' && evt.Meta.log_type == 'sharry_failed_auth'" +leakspeed: 20s +capacity: 4 +groupby: evt.Meta.source_ip +blackhole: 1m +reprocess: true +labels: + service: sharry + type: bruteforce + classification: + - attack.T1110 + remediation: true + behavior: http:bruteforce + spoofable: 0 + confidence: 3 +