aboutsummaryrefslogtreecommitdiff
path: root/cowrieder.py
diff options
context:
space:
mode:
authorAda Isopods <me@bathynomus.xyz>2025-05-16 01:40:11 -0600
committerAda Isopods <me@bathynomus.xyz>2026-10-21 23:04:26 -0600
commitc21ad820902c69b1bffa4c6453c0e1a15f07fe8c (patch)
tree1a71deb573da35e1e3f4e1aa795d0e098327a8e4 /cowrieder.py
parente3ea4c3317516f9fe9d93f277ac613e4bfa1cd45 (diff)
cleaned up some 14-hours-without-sleep-after-a-shift-at-work-ass logic and added the ability to redact IPs
Diffstat (limited to 'cowrieder.py')
-rw-r--r--cowrieder.py73
1 files changed, 38 insertions, 35 deletions
diff --git a/cowrieder.py b/cowrieder.py
index 419dd45..f022d4c 100644
--- a/cowrieder.py
+++ b/cowrieder.py
@@ -3,6 +3,7 @@ import sys
import random
import os
import argparse
+import hashlib
def ccgen(seed=""): # Color code generator
if(not os.isatty(1)): # Do not output color code at all if output is not a TTY that can accept them
@@ -35,10 +36,13 @@ def ttylog(mystring): #mystring
if(os.isatty(1)):
print(mystring)
-def ppip(addr, spacing = True): # Pretty-print an address with colour and, if it's IPv4, a consistent width
- if(len(addr) <= 16 and spacing):
- return ccgen(addr) + addr + ccgen() + (" " * (15 - len(addr)))
- return ccgen(addr) + addr + ccgen()
+def ppip(addr, spacing = False, ttycolor = False): # Pretty-print an address with colour and, if it's IPv4, a consistent width
+ rs = addr
+ if(ttycolor):
+ rs = ccgen(rs) + rs + ccgen()
+ if(len(rs) <= 16 and spacing):
+ rs = rs + (" " * (15 - len(addr)))
+ return rs
parser = argparse.ArgumentParser(
description="Script to parse JSON logs from popular SSH honeypot Cowrie and print them in a more readable format"
@@ -50,14 +54,14 @@ parser.add_argument('-v', '--verbose', action='store_true', help="Make the outpu
parser.add_argument('-vv', '--verboser', action='store_true', help="Make the output even more verbose (print timestamp, event id, and message (if applicable) for all events)")
parser.add_argument('-vvv', '--verbosest', action='store_true', help="Make the output even more verboserer (just group the input log by session id and dump it to pretty-printed JSON)")
-parser.add_argument('-B', '--blank', action='store_true', help="Print nothing whatsoever. Override this with other arguments to filter for only the stuff you care about")
-parser.add_argument('-P', '--pings', action='store_true', help='| With blank mode active, print "empty pings"')
-parser.add_argument('-F', '--fails', action='store_true', help='| With blank mode active, print failed logins')
-parser.add_argument('-L', '--logins', action='store_true', help='| With blank mode active, print "login pings"')
-parser.add_argument('-C', '--commands', action='store_true', help='| With blank mode active, print "command pings"')
-parser.add_argument('-S', '--sessions', action='store_true', help='* With blank mode active, print the cool interesting sessions that aren\'t any of that other stuff.')
+parser.add_argument('-P', '--pings', action='store_true', help='Print only "empty pings"')
+parser.add_argument('-F', '--fails', action='store_true', help='Print only failed logins')
+parser.add_argument('-L', '--logins', action='store_true', help='Print only "login pings"')
+parser.add_argument('-C', '--commands', action='store_true', help='Print only "command pings"')
+parser.add_argument('-S', '--sessions', action='store_true', help='Print only the cool interesting sessions that aren\'t any of that other stuff.')
-parser.add_argument('-i', '--filter-ip', help='Filter data to a specific IP')
+parser.add_argument('-r', '--redact-ip', action='store_true', help='Redact IP addresses by converting them to 16-character hashes')
+parser.add_argument('-i', '--filter-ip', help='Filter data to a specific IP or censored hash thereof')
#parser.add_argument('-a', '--stats', action='store_true', help='Print aggregated statistics')
#parser.add_argument('-j', '--json-stats', action='store_true', help='Dump aggregated statistics as JSON')
@@ -67,17 +71,13 @@ args = parser.parse_args()
filepath = args.filepath
-print_emptypings = True
-print_fails = True
-print_loginpings = True
-print_cmdpings = True
-print_sessions = True
-if(args.blank):
- print_emptypings = args.pings
- print_fails = args.fails
- print_loginpings = args.logins
- print_cmdpings = args.commands
- print_sessions = args.sessions
+blankmode = (args.pings or args.fails or args.logins or args.commands or args.sessions)
+
+print_emptypings = (not blankmode or args.pings)
+print_fails = (not blankmode or args.fails)
+print_loginpings = (not blankmode or args.logins)
+print_cmdpings = (not blankmode or args.commands)
+print_sessions = (not blankmode or args.sessions)
entries = []
sessions = {} # Dict - session_id : list of events in session
@@ -105,7 +105,10 @@ with open(filepath, "r") as logfile:
if(session_id not in sessions):
sessions.update({session_id: [entry]})
- session_ips.update({session_id: entry['src_ip']})
+ if(args.redact_ip):
+ session_ips.update({session_id: hashlib.shake_256(entry['src_ip'].encode("utf-8")).hexdigest(8)})
+ else:
+ session_ips.update({session_id: entry['src_ip']})
else:
sessions[session_id].append(entry)
@@ -149,11 +152,11 @@ if(args.blank):
for session_id in sessions:
if(
- (not print_emptypings and session_id not in session_successful_login) or # Is empty ping and don't print those
- (not print_fails and session_id in session_successful_login and not session_successful_login[session_id]) or # Is failed login and don't print those
- (not print_loginpings and session_id in session_successful_login and session_successful_login[session_id] and session_id not in session_ran_command) or # is login ping and don't print those
- (not print_cmdpings and session_id in session_ran_command and session_id not in session_not_cmdpings) or # is command ping and don't print those
- (not print_sessions and session_id in session_ran_command and session_id in session_not_cmdpings) # is session and don't print those
+ (not print_emptypings and session_id not in session_successful_login) # Is empty ping and don't print those
+ or (not print_fails and session_id in session_successful_login and not session_successful_login[session_id]) # Is failed login and don't print those
+ or (not print_loginpings and session_id in session_successful_login and session_successful_login[session_id] and session_id not in session_ran_command) # is login ping and don't print those
+ or (not print_cmdpings and session_id in session_ran_command and session_id not in session_not_cmdpings) # is command ping and don't print those
+ or (not print_sessions and session_id in session_ran_command and session_id in session_not_cmdpings) # is session and don't print those
):
newsessions.pop(session_id)
@@ -164,7 +167,7 @@ if(args.blank):
if(args.verboser): # High verbosity mode: print timestamp, event type and message (if applicable) for every event. I could probably do better but I'm lazy
for session_id in sessions:
ip = session_ips[session_id]
- print("\n--- Session " + ccgen(session_id) + session_id + ccgen() + " from " + ppip(ip, False) +" ---")
+ print("\n--- Session " + ccgen(session_id) + session_id + ccgen() + " from " + ppip(ip, ttycolor=True) +" ---")
for entry in sessions[session_id]:
if(entry['message']):
eventid = entry['eventid']
@@ -183,9 +186,9 @@ else: # Default (low) verbosity: just the good stuff
if(session_id not in session_successful_login): # Printing logic for empty pings
if(session_id in session_durations):
- print(timestamp + " > " + ppip(ip) + " > " + ccgen("PURPLE") + "Empty ping" + ccgen() + " lasting " + session_durations[session_id] + "s")
+ print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("PURPLE") + "Empty ping" + ccgen() + " lasting " + session_durations[session_id] + "s")
else:
- print(timestamp + " > " + ppip(ip) + " > " + ccgen("PURPLE") + "Empty ping" + ccgen() + " active as of processing")
+ print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("PURPLE") + "Empty ping" + ccgen() + " active as of processing")
elif(not session_successful_login[session_id]): # Printing logic for failed logins
cred_combos = []
@@ -195,7 +198,7 @@ else: # Default (low) verbosity: just the good stuff
if("password" in entry):
pw = entry["password"]
cred_combos.append((entry["username"], pw))
- print(timestamp + " > " + ppip(ip) + " > " + ccgen("RED") +"Failed login" + ccgen() + ": " + str(cred_combos))
+ print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("RED") +"Failed login" + ccgen() + ": " + str(cred_combos))
elif(session_id in session_ran_command): # Printing logic for sessions in which a command was run
@@ -204,10 +207,10 @@ else: # Default (low) verbosity: just the good stuff
for entry in sessions[session_id]:
if(entry["eventid"] == "cowrie.login.failed" or entry["eventid"] == "cowrie.login.success"):
cred_combos.append((entry["username"], entry["password"]))
- print(timestamp + " > " + ppip(ip) + " > " + ccgen("BLUE") + "Command ping" + ccgen() + ": " + str(cred_combos) + " > " + str(session_cmdping_commands[session_id]))
+ print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("BLUE") + "Command ping" + ccgen() + ": " + str(cred_combos) + " > " + str(session_cmdping_commands[session_id]))
else: # Printing logic for un-collapsed sessions
- print(timestamp + " > " + ppip(ip) + " > " + ccgen("CYAN") + "Session" + ccgen() + ":")
+ print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("CYAN") + "Session" + ccgen() + ":")
for entry in sessions[session_id]:
match(entry["eventid"]):
case "cowrie.login.failed":
@@ -233,4 +236,4 @@ else: # Default (low) verbosity: just the good stuff
for entry in sessions[session_id]:
if(entry["eventid"] == "cowrie.login.failed" or entry["eventid"] == "cowrie.login.success"):
cred_combos.append((entry["username"], entry["password"]))
- print(timestamp + " > " + ppip(ip) + " > " + ccgen("GREEN") + "Login ping" + ccgen() + ": " + str(cred_combos))
+ print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("GREEN") + "Login ping" + ccgen() + ": " + str(cred_combos))