def setUp(self): """ Setup a Manager and Monitor object The Katana Unit Tests can override this method, but must call the parent. You can use this to create special temporary target files, start a web server, or anything else needed to evaluate your target. """ # This is a nasty hack os.system("rm -rf ./results 2>&1 >/dev/null") # Create the monitor and set the flag format self.monitor = Monitor() self.manager = Manager(monitor=self.monitor) self.manager["manager"]["flag-format"] = self.FLAG_FORMAT self.manager["manager"]["auto"] = "yes" # Ignore annoying resource warnings warnings.simplefilter("ignore", ResourceWarning)
def main(): # Setup basic logging logging.basicConfig(level=logging.INFO) # Build the argument parse object parser = argparse.ArgumentParser( prog="katana", description= "Automatically identify and solve basic Capture the Flag challenges", add_help=False, ) # Parse config option first parser.add_argument("--config", "-c", help="configuration file", default=None) args, remaining_args = parser.parse_known_args() # Build our katana monitor monitor = ReplMonitor() # Create our katana manager manager = Manager(monitor=monitor, config_path=args.config) # Build final parser parser = argparse.ArgumentParser( prog="katana", description= "Automatically identify and solve basic Capture the Flag challenges", ) # Add global arguments parser.add_argument("--config", "-c", help="configuration file", default=None) parser.add_argument( "--manager", "-m", default=None, help= "comma separated manager configurations (e.g. flag-format=FLAG{.*?})", ) parser.add_argument( "--timeout", "-t", type=float, help="timeout for all unit evaluations in seconds", ) parser.add_argument("targets", nargs="*", default=[], help="targets to evaluate") parser.add_argument("--auto", "-a", help="shorthand for `-m auto=True`", action="store_true") parser.add_argument( "--unit", "-u", help="explicitly run a unit on target", action="append", default=[], ) parser.add_argument( "--exclude", "-e", help="exclude a unit from running", action="append", default=[], ) parser.add_argument("--flag", "-f", help="set the flag format") parser.add_argument( "--force", action="store_true", help="Force execution even if results directory exists", ) parser.add_argument( "--imagegui", "-i", action="store_true", help="Display images as katana finds them", default=False, ) # Add options for all the unit specific configurations for unit in manager.finder.units: parser.add_argument( "--" + unit.get_name(), default=None, help="comma separated unit configuration", ) # Parse arguments args = parser.parse_args(remaining_args) # Load configuration file if args.config is not None: result = manager.read(args.config) if len(result) == 0: logging.error(" {0}: no such file or directory".format( args.config)) sys.exit(1) # Apply configurations if args.manager is not None: params = args.manager.split(",") for param in params: name, value = param.split("=") manager["manager"][name] = value # Apply auto shorthand if args.auto is not None: manager["manager"]["auto"] = "yes" # Apply flag format if args.flag is not None: manager["manager"]["flag-format"] = args.flag # Apply requested units units = [u for u in manager["manager"]["units"].split(",") if u] + args.unit manager["manager"]["units"] = ",".join(units) # Apply excluded units excluded = manager["manager"]["exclude"].split(",") + args.exclude manager["manager"]["exclude"] = ",".join(excluded) # Enable results removal if requested if args.force: manager["manager"]["force"] = "yes" # Determine whether to display images or not if args.imagegui: manager["manager"]["imagegui"] = "yes" else: manager["manager"]["imagegui"] = "no" # Apply unit configurations args_dict = vars(args) # We need this because configs have '.' for unit in manager.finder.units: # Ignore if nothing was specified if args_dict[unit.get_name()] is None: continue # Initialize the unit if needed if unit.get_name() not in manager: manager[unit.get_name()] = {} # Parse params params = args_dict[unit.get_name()].split(",") for param in params: name, value = param.split("=") manager[unit.get_name()][name] = value # Queue the specified targets for target in args.targets: manager.queue_target(target, background=True) # Build the REPL and execute it sys.argv = sys.argv[:1] repl = Repl(manager) # Warn the user about missing binary dependencies for unit, dep in manager.finder.missing_deps: repl.pwarning(f"{unit}: missing binary dependency: {dep}") sys.exit(repl.cmdloop())
from katana.monitor import Monitor # A custom monitor which will receive notifications about units # The default monitor prints notifications of flags with logging as well but # this allows you to do fancier status updates if you are building an interface # of some kind class MyMonitor(Monitor): def on_flag(self, manager: Manager, unit: Unit, flag: str): logging.info("found a flag: {0}".format(flag)) # Create your monitor object monitor = MyMonitor() # Create your Manager with your monitor attached manager = Manager(monitor, config_path="./examples/example.ini") # Set some custom configuration parameters manager["auto"] = True manager["outdir"] = "./example-results-{0}".format(time.strftime("%Y%m%d-%H%M%S")) manager["exclude"] = ["crypto"] manager["flag-format"] = r"FLAG{.*?}" # Optionally, manually register unit classes # manager.finder.register(CustomChallengeUnit) # Or load units automatically from a directory # manager.finder.find('./customunits', 'customunits.') # Start the manager (empty queue at the moment) manager.start()
class KatanaTest(TestCase): FLAG_FORMAT = "FLAG{.*?}" def setUp(self): """ Setup a Manager and Monitor object The Katana Unit Tests can override this method, but must call the parent. You can use this to create special temporary target files, start a web server, or anything else needed to evaluate your target. """ # This is a nasty hack os.system("rm -rf ./results 2>&1 >/dev/null") # Create the monitor and set the flag format self.monitor = Monitor() self.manager = Manager(monitor=self.monitor) self.manager["manager"]["flag-format"] = self.FLAG_FORMAT self.manager["manager"]["auto"] = "yes" # Ignore annoying resource warnings warnings.simplefilter("ignore", ResourceWarning) def tearDown(self): """ Tear down any artifacts from the last run The Katana Unit Tests can override this method, but must call the parent. You can use this method to clean up any extra temporary files that may have been created for the target or any threads/services started for unit testing. """ # Tear down our object tree del self.monitor del self.manager # This is a nasty hack os.system("rm -rf ./results 2>&1 >/dev/null") def katana_test( self, config: str, target: str, correct_flag: str, timeout: float = 10 ): """ Perform a test with the given configuration, target and flag :param config: Katana configuration file :type config: str :param target: The target for this katana test (url, filename, raw data, etc) :type target: str :param correct_flag: The correct flag which katana should find. :type correct_flag: str :param timeout: The timeout in seconds for this test, defaults to 10 :type timeout: float, optional """ if isinstance(correct_flag, bytes): correct_flag = correct_flag.decode("utf-8") # Load given configuration self.manager.read_file(io.StringIO(config)) # Queue the target and begin processing self.manager.queue_target(target) self.manager.start() # Ensure wait for completion. Error if we time out self.assertTrue(self.manager.join(timeout=timeout), "manager timed out") # Ensure we have at least 1 flag self.assertGreater(len(self.monitor.flags), 0, "no flags found") # Ensure the flag matches expected output for flag in self.monitor.flags: if flag[1] == correct_flag: return # Fail otherwise self.fail(f"correct flag not found (found: {self.monitor.flags})")
#!/usr/bin/env python3 import logging from katana.manager import Manager from katana.monitor import LoggingMonitor # Configure basic logging logging.basicConfig(level=logging.INFO) # Create a basic monitor which will log results monitor = LoggingMonitor() # Create a manager with a default unit finder and default monitor # and load all default units. The default monitor will using the python logging # module to log important events to the console (or log file) manager = Manager(monitor=monitor, config_path="./examples/example.ini") # Begin the background work monitor manager.start() # Queue the target manager.queue_target("./tests/qrcode.png") # Wait for completion (with a 10-second timeout) if not manager.join(timeout=10): logging.warning("evaluation timed out")