From ce0b0a41fe3a1b5695529a371bb6844850f404d0 Mon Sep 17 00:00:00 2001 From: Yun Zheng Hu Date: Fri, 17 Dec 2021 01:04:46 +0100 Subject: [PATCH] Add hostname to output and refactored parts of script --- log4j-finder.py | 103 +++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/log4j-finder.py b/log4j-finder.py index 5ea93e5..9fd5aa8 100755 --- a/log4j-finder.py +++ b/log4j-finder.py @@ -23,6 +23,7 @@ import zipfile import logging import argparse import hashlib +import platform import datetime import functools import itertools @@ -84,6 +85,8 @@ MD5_GOOD = { "ba1cf8f81e7b31c709768561ba8ab558": "log4j 2.16.0", } +HOSTNAME = platform.node() + def md5_digest(fobj): """Calculate the MD5 digest of a file object.""" @@ -179,6 +182,12 @@ def cyan(s): return f"\033[36m{s}\033[0m" +def magenta(s): + if NO_COLOR: + return s + return f"\033[35m{s}\033[0m" + + def bold(s): if NO_COLOR: return s @@ -197,26 +206,36 @@ def check_vulnerable(fobj, path_chain, stats, has_jndilookup=True): md5sum = md5_digest(fobj) first_path = bold(path_chain.pop(0)) path_chain = " -> ".join(str(p) for p in [first_path] + path_chain) - now = datetime.datetime.utcnow() - vulnerable = red("VULNERABLE") - patched = cyan("PATCHED") - good = green("GOOD") - unknown = yellow("UNKNOWN") + comment = collections.ChainMap(MD5_BAD, MD5_GOOD).get(md5sum, "Unknown MD5") + color_map = {"vulnerable": red, "good": green, "patched": cyan, "unknown": yellow} if md5sum in MD5_BAD: - comment = MD5_BAD[md5sum] - if has_jndilookup: - print(f"[{now}] {vulnerable}: {path_chain} [{md5sum}: {comment}]") - stats["vulnerable"] += 1 - else: - print(f"[{now}] {patched}: {path_chain} [{md5sum}: {comment}]") - stats["patched"] += 1 + status = "vulnerable" if has_jndilookup else "patched" elif md5sum in MD5_GOOD: - comment = MD5_GOOD[md5sum] - print(f"[{now}] {good}: {path_chain} [{md5sum}: {comment}]") - stats["good"] += 1 + status = "good" else: - print(f"[{now}] {unknown}: MD5 not known for {path_chain} [{md5sum}]") - stats["unknown"] += 1 + status = "unknown" + stats[status] += 1 + color = color_map.get(status, red) + now = datetime.datetime.utcnow().replace(microsecond=0) + hostname = magenta(HOSTNAME) + status = bold(color(status.upper())) + md5sum = color(md5sum) + comment = bold(color(comment)) + print(f"[{now}] {hostname} {status}: {path_chain} [{md5sum}: {comment}]") + + +def print_summary(stats): + print("\nSummary:") + print(f" Processed {stats['files']} files and {stats['directories']} directories") + print(f" Scanned {stats['scanned']} files") + if stats["vulnerable"]: + print(" Found {} vulnerable files".format(stats["vulnerable"])) + if stats["good"]: + print(" Found {} good files".format(stats["good"])) + if stats["patched"]: + print(" Found {} patched files".format(stats["patched"])) + if stats["unknown"]: + print(" Found {} unknown files".format(stats["unknown"])) def main(): @@ -242,8 +261,16 @@ def main(): parser.add_argument( "-n", "--no-color", action="store_true", help="disable color output" ) + parser.add_argument( + "-q", + "--quiet", + action="store_true", + help="be more quiet, disables banner and summary", + ) parser.add_argument("-b", "--no-banner", action="store_true", help="disable banner") - parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}") + parser.add_argument( + "-V", "--version", action="version", version=f"%(prog)s {__version__}" + ) args = parser.parse_args() logging.basicConfig( format="%(asctime)s %(levelname)s %(message)s", @@ -259,22 +286,16 @@ def main(): global NO_COLOR NO_COLOR = True - stats = { - "scanned": 0, - "files": 0, - "directories": 0, - "vulnerable": 0, - "patched": 0, - "good": 0, - "unknown": 0, - } + stats = collections.Counter() start_time = time.monotonic() + hostname = magenta(HOSTNAME) - if not args.no_banner: + if not args.no_banner and not args.quiet: print(FIGLET) for directory in args.path: - now = datetime.datetime.utcnow() - print(f"[{now}] Scanning: {directory}") + now = datetime.datetime.utcnow().replace(microsecond=0) + if not args.quiet: + print(f"[{now}] {hostname} Scanning: {directory}") for p in iter_scandir(directory, stats=stats): if p.name.lower() in FILENAMES: stats["scanned"] += 1 @@ -306,22 +327,12 @@ def main(): except IOError as e: log.debug(f"{p}: {e}") - elapsed_time = time.monotonic() - start_time - now = datetime.datetime.utcnow() - print(f"[{now}] Finished scan, elapsed time: {elapsed_time:.2f} seconds") - - print("\nSummary:") - print(f" Processed {stats['files']} files and {stats['directories']} directories") - print(f" Scanned {stats['scanned']} files") - if stats["vulnerable"]: - print(" Found {} vulnerable files".format(stats["vulnerable"])) - if stats["good"]: - print(" Found {} good files".format(stats["good"])) - if stats["patched"]: - print(" Found {} patched files".format(stats["patched"])) - if stats["unknown"]: - print(" Found {} unknown files".format(stats["unknown"])) - print(f"\nElapsed time: {elapsed_time:.2f} seconds ") + elapsed = time.monotonic() - start_time + now = datetime.datetime.utcnow().replace(microsecond=0) + if not args.quiet: + print(f"[{now}] {hostname} Finished scan, elapsed time: {elapsed:.2f} seconds") + print_summary(stats) + print(f"\nElapsed time: {elapsed:.2f} seconds") if __name__ == "__main__":