import json import sys import random import os 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 return "" if(seed and seed != "reset" and seed != "RESET"): # If seed is provided, use it to generate a color. Return color reset code otherwise or if seed is the word "reset" random.seed(seed) return "\x1b[38;2;{r};{g};{b}m".format(r=random.randrange(50, 256), g=random.randrange(50, 256), b=random.randrange(50, 256)) return "\x1b[0m" 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() if not 0 <= 1 < len(sys.argv): # TODO: integrate argparse print("Provide a path to a Cowrie JSON log file") exit(1) filepath = sys.argv[1] verbosity = 0 print_log = True print_stats = True entries = [] sessions = {} session_ips = {} session_timestamps = {} session_durations = {} session_ran_command = {} session_successful_login = {} with open(filepath, "r") as logfile: for line in logfile: entry = json.loads(line) entries.append(entry) session_id = entry['session'] if(session_id not in sessions): sessions.update({session_id: [entry]}) session_ips.update({session_id: entry['src_ip']}) else: sessions[session_id].append(entry) if(entry["eventid"] == "cowrie.session.connect" and session_id not in session_timestamps): # Save the timestamp of the session's initial connection session_timestamps.update({session_id: entry['timestamp']}) if(entry["eventid"] == "cowrie.session.closed" and session_id not in session_durations): # Save the session's duration session_durations.update({session_id: entry['duration']}) if(entry["eventid"] == "cowrie.command.input" and session_id not in session_ran_command): # Save whether any commands were run during the session session_ran_command.update({session_id: True}) if(entry["eventid"] == "cowrie.login.success" and session_id not in session_successful_login): # Save if login was a success session_successful_login.update({session_id: True}) if(entry["eventid"] == "cowrie.login.failed" and session_id not in session_successful_login): # Save if login failed session_successful_login.update({session_id: False}) if(entry["eventid"] == "cowrie.login.success" and session_id in session_successful_login): # Save if login succeeded after failing session_successful_login[session_id] = True filter_ip = "" if 0 <= 2 < len(sys.argv): filter_ip = sys.argv[2] if(verbosity == 2): # 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] if(ip == filter_ip or not filter_ip): print("\n--- Session " + ccgen(session_id) + session_id + ccgen() + " from " + ppip(ip, False) +" ---") for entry in sessions[session_id]: if(entry['message']): eventid = entry['eventid'] print(entry["timestamp"] + " > " + eventid + (" " * (29 - len(eventid))) + ": " + entry['message']) else: print(entry["timestamp"] + " > " + entry['eventid']) elif(verbosity == 3): # Maximum verbosity: literally just pretty print the now-sorted-by-session logs print(json.dumps(sessions, indent=4)) else: # Default (low) verbosity: just the good stuff for session_id in sessions: ip = session_ips[session_id] timestamp = session_timestamps[session_id][:22].replace("T", " ") if(session_id not in session_successful_login): print(timestamp + " > " + ppip(ip) + " > Empty ping lasting " + session_durations[session_id] + "s") elif(not session_successful_login[session_id]): cred_combos = [] for entry in sessions[session_id]: if(entry["eventid"] == "cowrie.login.failed"): pw = [] if("password" in entry): pw = entry["password"] cred_combos.append((entry["username"], pw)) print(timestamp + " > " + ppip(ip) + " > Failed login: " + str(cred_combos)) elif(session_id in session_ran_command): collapse_cmdping = False pingcmds = [] if(verbosity != 1): for entry in sessions[session_id]: if(entry["eventid"] == "cowrie.command.input"): argv0 = entry["input"] if(" " in argv0): argv0 = entry["input"][:entry["input"].index(" ")] if(argv0 in ['uname', 'nproc', 'echo']): pingcmds.append(argv0) collapse_cmdping = True if(collapse_cmdping): cred_combos = [] 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) + " > Command ping: " + str(cred_combos) + " > " + str(pingcmds)) else: print(timestamp + " > " + ppip(ip) + " > Session:") for entry in sessions[session_id]: match(entry["eventid"]): case "cowrie.login.failed": pw = [] if("password" in entry): pw = entry["password"] print(" | Failed login: " + entry["username"] + "/" + str(pw)) case "cowrie.login.success": print(" | Successful login: " + entry["username"] + "/" + entry["password"]) case "cowrie.command.input": print(" | CMD: " + entry["input"]) case "cowrie.session.file_download": df = "" if("url" in entry): df = entry["url"] elif("destfile" in entry): df = entry["destfile"] print(" | Saved file " + df + " to " + entry["outfile"][:40]) print(" X Disconnected after " + session_durations[session_id] + "s") else: cred_combos = [] 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) + " > Login ping: " + str(cred_combos))