From 9701b08c5ec4c7c7cd23472b032c7f8c0bdcfa3c Mon Sep 17 00:00:00 2001 From: Pavel Valach <pavel.valach@cesnet.cz> Date: Mon, 2 Dec 2024 18:15:35 +0100 Subject: [PATCH] cowrie/wardenfiler: Store credentials for both successful and unsuccessful attempts First implementation, which uses the aggregation ID (AID) "src_ip,dst_ip" to watch all credentials used during the time window. If the login fails, the credentials are stored under the AID. The credentials are flushed from the AID cache either when the aggregation window expires, or when the login is successful - the unsuccessful credentials from the cache are then sent with the successful ones appended. --- cowrie/wardenfiler.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/cowrie/wardenfiler.py b/cowrie/wardenfiler.py index fc97925..8b9f6c5 100644 --- a/cowrie/wardenfiler.py +++ b/cowrie/wardenfiler.py @@ -87,6 +87,8 @@ class Output(cowrie.core.output.Output): drop_malware = True win_start = None attackers = {} + # aggregated credentials from failed attempts per IP + attackers_creds = {} sessions = {} port_xlat = {} @@ -175,7 +177,7 @@ class Output(cowrie.core.output.Output): if entry.get("dst_port") and self.reported_ssh_port: entry["dst_port"] = self.reported_ssh_port - if entry["eventid"] == 'cowrie.session.connect': + if entry["eventid"] == 'cowrie.session.connect': # Do not track a session for a source # which is not globally routable if not ip_address(entry["src_ip"]).is_global: @@ -203,6 +205,10 @@ class Output(cowrie.core.output.Output): self.sessions[entry["session"]] = entry ws = self.win_start or time() cnt = self.attackers.get(aid, 0) + # aggregated credentials from attempts + if not self.attackers_creds.get(aid): + self.attackers_creds[aid] = [] + creds = self.attackers_creds[aid] if (time() - ws < self.aggr_win): self.attackers[aid] = cnt + 1 @@ -222,10 +228,13 @@ class Output(cowrie.core.output.Output): event["Target"] = [{"Proto": ["tcp", "ssh"], a_af: [a_dst_ip]}] if (self.anon_mask_4 < 32 and a_af == "IP4") or (self.anon_mask_6 < 128): event["Target"][0]["Anonymised"] = True + if creds: + event["Credentials"] = creds self.save_event(event) self.attackers = {} ws = time() self.attackers[aid] = 1 + self.attackers_creds[aid] = [] self.win_start = ws elif entry["session"] not in self.sessions: @@ -235,10 +244,22 @@ class Output(cowrie.core.output.Output): return() elif entry["eventid"] == 'cowrie.login.success': + u, p = entry["username"], entry["password"] s = entry["session"] if s in self.sessions: + aid = self.sessions[s]["aid"] + self.sessions[s]["credentials"] = self.attackers_creds.get(aid, []) + self.sessions[s]["credentials"].append({"Username": u, "Password": p, "Accepted": True}) self.sessions[s]["input"] = [] self.sessions[s]["loggedin"] = True + self.attackers_creds[aid] = [] + + elif entry["eventid"] == "cowrie.login.failed": + u, p = entry["username"], entry["password"] + s = entry["session"] + if s in self.sessions: + aid = self.sessions[s]["aid"] + self.attackers_creds[aid].append({"Username": u, "Password": p}) elif entry["eventid"] == 'cowrie.command.input': s = entry["session"] @@ -381,5 +402,7 @@ class Output(cowrie.core.output.Output): if not plain: attach["ContentEncoding"] = "base64" event["Attach"] = [attach] + if self.sessions[s]["credentials"]: + event["Credentials"] = self.sessions[s]["credentials"] self.save_event(event) self.sessions.pop(s, None) -- GitLab