From 0deb8ecec9b874d58f6c46f6351d401464d1c061 Mon Sep 17 00:00:00 2001 From: Ada Isopods Date: Fri, 16 May 2025 06:21:20 -0600 Subject: you don't need to see my wacky internal terminology for a simple log analyzing tool --- README.md | 18 ++++++------------ cowrieder.py | 34 +++++++++++++++++----------------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index f0fe695..ee60abf 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,12 @@ Options: -v, --verbose Make the output more verbose (print abridged logs for all logins where commands are run, not just the interesting ones) -V, --verboser Make the output even more verbose (print timestamp, event id, and message (if applicable) for all events) -W, --verbosest Make the output even more verboserer (just group the input log by session id and dump it to pretty-printed JSON) --P, --pings Print only "empty pings" --F, --fails Print only failed logins --L, --logins Print only "login pings" --C, --commands Print only "command pings" --E, --eventful Print only the cool interesting sessions that aren't any of that other stuff. +-P, --pings Include only connections where no authentication attempt was made +-F, --fails Include only failed logins +-L, --logins Include only successful logins where no commands were run +-C, --commands Include only logins where only this list of basic test commands were run: ['uname', 'nproc', 'echo'] +-E, --eventful Include only the cool interesting sessions that aren't any of that other stuff -r, --redact-ip Redact IP addresses by converting them to 16-character hashes -i, --filter-ip FILTER_IP Filter data to a specific IP or censored hash thereof -``` - -# Terminology - -- "Empty ping": Client connected and made no attempt to log in -- "Login ping": Client logged in successfully and did nothing -- "Command ping": Client logged in successfully and only ran `uname`, `nproc`, and/or `echo`. In my experience "command pings" comprise most insances of a client successfully logging in and then bothering to run any commands \ No newline at end of file +``` \ No newline at end of file diff --git a/cowrieder.py b/cowrieder.py index db549bc..75be8c1 100644 --- a/cowrieder.py +++ b/cowrieder.py @@ -36,7 +36,7 @@ def ttylog(mystring): #mystring if(os.isatty(1)): print(mystring) -def ppip(addr, spacing = False, ttycolor = False): # Pretty-print an address with colour and, if it's IPv4, a consistent width +def ppip(addr, spacing = False, ttycolor = False): # Pretty-print an address with colour and, if it's IPv4 or hashed, a consistent width rs = addr if(ttycolor): rs = ccgen(rs) + rs + ccgen() @@ -49,6 +49,8 @@ def hashgen(seed, length=16): raise Warning("Due to strange behaviour on the part of the hexdigest() function, length should be a multiple of 2") return hashlib.shake_256(str(seed).encode("utf-8")).hexdigest(int(length / 2)) +cmdping_commands = ['uname', 'nproc', 'echo'] # List of commands that, if used exclusively, qualify a session as a "command ping" + # ArgParse crap coming in hot parser = argparse.ArgumentParser( @@ -61,11 +63,11 @@ parser.add_argument('-v', '--verbose', action='store_true', help="Make the outpu parser.add_argument('-V', '--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('-W', '--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('-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('-E', '--eventful', action='store_true', help='Print only the cool interesting sessions that aren\'t any of that other stuff.') +parser.add_argument('-P', '--pings', action='store_true', help='Include only connections where no authentication attempt was made') +parser.add_argument('-F', '--fails', action='store_true', help='Include only failed logins') +parser.add_argument('-L', '--logins', action='store_true', help='Include only successful logins where no commands were run') +parser.add_argument('-C', '--commands', action='store_true', help='Include only logins where only this list of basic test commands were run: ' + str(cmdping_commands)) +parser.add_argument('-E', '--eventful', action='store_true', help='Include only the cool interesting sessions that aren\'t any of that other stuff') 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') @@ -129,9 +131,9 @@ def session_cred_combos(session): # List passwords used in login attempt for event in session: if(event['eventid'] == "cowrie.login.failed" or event['eventid'] == "cowrie.login.success"): if('password' in event): - ra.append([event['username'], event['password']]) + ra.append((event['username'], event['password'])) elif('key' in event): - ra.append([event['username'], []]) + ra.append((event['username'], [])) return ra def session_ran_command(session): # Was a command run during the session @@ -146,8 +148,6 @@ def session_saved_file(session): return True return False -cmdping_commands = ['uname', 'nproc', 'echo'] # List of commands that, if used exclusively, qualify a session as a "command ping" - def session_is_cmdping(session): # Determine if a session qualifies as a "command ping" if(session_ran_command(session)): for event in session: @@ -177,7 +177,7 @@ def session_pingcmds(session): # Return array of all "command ping" commands use -with open(filepath, "r") as logfile: # Load da file and collect all possible relevant data about it +with open(filepath, "r") as logfile: # Load da file and populate da master array for line in logfile: entry = json.loads(line) @@ -226,7 +226,7 @@ if(verbosity == 2): # High verbosity mode: print timestamp, event type and messa else: print(entry["timestamp"] + " > " + eventid + (" " * (29 - len(eventid))) + ": " + entry['message']) -elif(verbosity == 3): # Maximum verbosity: literally just pretty print the now-sorted-by-session logs +elif(verbosity == 3): # Maximum verbosity: literally just dump the now-sorted-by-session logs as pretty-printed JSON print(json.dumps(sessions, indent=4)) else: # Default (low) verbosity: just the good stuff @@ -236,20 +236,20 @@ else: # Default (low) verbosity: just the good stuff session = sessions[session_id] ip = session_ip(session) timestamp = session_timestamp(session)[:22].replace("T", " ") # clean up the timestamp text all pretty like - durationstring = " closed after " + str(session_duration(session)) + "s" + durationstring = "closed after " + str(session_duration(session)) + "s" if(session_duration(session) < 0): durationstring = "active as of processing" if(not session_attempted_login(session)): # Printing empty pings - print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("PURPLE") + "Empty ping" + ccgen() + durationstring) + print(timestamp + " > " + ppip(ip, True, True) + " > Connection opened and " + durationstring) elif(session_attempted_login(session) and not session_login_succeeded(session)): # Printing failed logins - print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("RED") +"Failed login" + ccgen() + ": " + str(session_cred_combos(session))) + print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("RED") +"Failed login" + ccgen() + " with credentials: " + str(session_cred_combos(session))) elif(session_ran_command(session)): # Printing logic for sessions in which a command was run if(not verbosity == 1 and session_is_cmdping(session)): # If -v is not enabled, collapse "command pings" to one-liners - print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("BLUE") + "Command ping" + ccgen() + ": " + str(session_cred_combos(session)) + " > " + str(session_pingcmds(session))) + print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("GREEN") + "Login" + ccgen() + " with credentials: " + str(session_cred_combos(session)) + ccgen("BLUE") + " ran commands " + ccgen() + str(session_pingcmds(session))) else: # Printing logic for un-collapsed sessions where commands were run print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("CYAN") + "Session" + ccgen() + ":") @@ -277,7 +277,7 @@ else: # Default (low) verbosity: just the good stuff print(" X Session " + durationstring) else: # Printing logic for successful logins in which no commands were run - print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("GREEN") + "Login ping" + ccgen() + ": " + str(session_cred_combos(session))) + print(timestamp + " > " + ppip(ip, True, True) + " > " + ccgen("GREEN") + "Login" + ccgen() + " with credentials: " + str(session_cred_combos(session))) if(args.stats): print("Aggregate statistics TODO") \ No newline at end of file -- cgit v1.2.3