diff --git a/autorecon.py b/autorecon.py index 05cd4c0..2e35c99 100644 --- a/autorecon.py +++ b/autorecon.py @@ -1297,26 +1297,28 @@ async def main(): keyboard_monitor.cancel() - for plugin in autorecon.plugin_types['report']: - plugin_tag_set = set(plugin.tags) + # If there's only one target we don't need a combined report + if len(autorecon.completed_targets) > 1: + for plugin in autorecon.plugin_types['report']: + plugin_tag_set = set(plugin.tags) - matching_tags = False - for tag_group in autorecon.tags: - if set(tag_group).issubset(plugin_tag_set): - matching_tags = True - break + matching_tags = False + for tag_group in autorecon.tags: + if set(tag_group).issubset(plugin_tag_set): + matching_tags = True + break - excluded_tags = False - for tag_group in autorecon.excluded_tags: - if set(tag_group).issubset(plugin_tag_set): - excluded_tags = True - break + excluded_tags = False + for tag_group in autorecon.excluded_tags: + if set(tag_group).issubset(plugin_tag_set): + excluded_tags = True + break - if matching_tags and not excluded_tags: - pending.add(asyncio.create_task(generate_report(plugin, autorecon.completed_targets))) + if matching_tags and not excluded_tags: + pending.add(asyncio.create_task(generate_report(plugin, autorecon.completed_targets))) - while pending: - done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED, timeout=1) + while pending: + done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED, timeout=1) if timed_out: cancel_all_tasks(None, None) diff --git a/plugins/reporting.py b/plugins/reporting.py index 8c3bfea..e5b87be 100644 --- a/plugins/reporting.py +++ b/plugins/reporting.py @@ -8,12 +8,13 @@ class CherryTree(Report): def __init__(self): super().__init__() self.name = 'CherryTree' + self.tags = [] async def run(self, targets): if len(targets) > 1: - report = os.path.join(config['outdir'], 'cherrytree.xml.ctd') + report = os.path.join(config['outdir'], 'report.xml.ctd') elif len(targets) == 1: - report = os.path.join(targets[0].reportdir, 'cherrytree.xml.ctd') + report = os.path.join(targets[0].reportdir, 'report.xml.ctd') else: return @@ -27,24 +28,9 @@ class CherryTree(Report): if target.scans['ports']: output.writelines('\n') for scan in target.scans['ports'].keys(): - output.writelines('\n') - for command in target.scans['ports'][scan]['commands']: - output.writelines('' + escape(command[0])) - for filename in files: - if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]): - output.writelines('\n\n' + escape(filename) + ':\n\n') - with open(filename, 'r') as file: - output.writelines(escape(file.read()) + '\n') - output.writelines('\n') - output.writelines('\n') - output.writelines('\n') - if target.scans['services']: - output.writelines('\n') - for service in target.scans['services'].keys(): - output.writelines('\n') - for plugin in target.scans['services'][service].keys(): - output.writelines('\n') - for command in target.scans['services'][service][plugin]['commands']: + if len(target.scans['ports'][scan]['commands']) > 0: + output.writelines('\n') + for command in target.scans['ports'][scan]['commands']: output.writelines('' + escape(command[0])) for filename in files: if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]): @@ -53,6 +39,23 @@ class CherryTree(Report): output.writelines(escape(file.read()) + '\n') output.writelines('\n') output.writelines('\n') + output.writelines('\n') + if target.scans['services']: + output.writelines('\n') + for service in target.scans['services'].keys(): + output.writelines('\n') + for plugin in target.scans['services'][service].keys(): + if len(target.scans['services'][service][plugin]['commands']) > 0: + output.writelines('\n') + for command in target.scans['services'][service][plugin]['commands']: + output.writelines('' + escape(command[0])) + for filename in files: + if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]): + output.writelines('\n\n' + escape(filename) + ':\n\n') + with open(filename, 'r') as file: + output.writelines(escape(file.read()) + '\n') + output.writelines('\n') + output.writelines('\n') output.writelines('\n') output.writelines('\n') @@ -86,3 +89,75 @@ class CherryTree(Report): output.writelines('\n') output.writelines('') + +class Markdown(Report): + + def __init__(self): + super().__init__() + self.name = 'Markdown' + + async def run(self, targets): + if len(targets) > 1: + report = os.path.join(config['outdir'], 'report.md') + elif len(targets) == 1: + report = os.path.join(targets[0].reportdir, 'report.md') + else: + return + + os.makedirs(report, exist_ok=True) + + for target in targets: + os.makedirs(os.path.join(report, target.address), exist_ok=True) + + files = [os.path.abspath(filename) for filename in glob.iglob(os.path.join(target.scandir, '**/*'), recursive=True) if os.path.isfile(filename) and filename.endswith(('.txt', '.html'))] + + if target.scans['ports']: + os.makedirs(os.path.join(report, target.address, 'Port Scans'), exist_ok=True) + for scan in target.scans['ports'].keys(): + if len(target.scans['ports'][scan]['commands']) > 0: + with open(os.path.join(report, target.address, 'Port Scans', 'PortScan - ' + target.scans['ports'][scan]['plugin'].name + '.md'), 'w') as output: + for command in target.scans['ports'][scan]['commands']: + output.writelines('```bash\n' + command[0] + '\n```') + for filename in files: + if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]): + output.writelines('\n\n[' + filename + '](file://' + filename + '):\n\n') + with open(filename, 'r') as file: + output.writelines('```\n' + file.read() + '\n```\n') + if target.scans['services']: + os.makedirs(os.path.join(report, target.address, 'Services'), exist_ok=True) + for service in target.scans['services'].keys(): + os.makedirs(os.path.join(report, target.address, 'Services', 'Service - ' + service.tag().replace('/', '-')), exist_ok=True) + for plugin in target.scans['services'][service].keys(): + if len(target.scans['services'][service][plugin]['commands']) > 0: + with open(os.path.join(report, target.address, 'Services', 'Service - ' + service.tag().replace('/', '-'), target.scans['services'][service][plugin]['plugin'].name + '.md'), 'w') as output: + for command in target.scans['services'][service][plugin]['commands']: + output.writelines('```bash\n' + command[0] + '\n```') + for filename in files: + if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]): + output.writelines('\n\n[' + filename + '](file://' + filename + '):\n\n') + with open(filename, 'r') as file: + output.writelines('```\n' + file.read() + '\n```\n') + + manual_commands = os.path.join(target.scandir, '_manual_commands.txt') + if os.path.isfile(manual_commands): + with open(os.path.join(report, target.address, 'Manual Commands' + '.md'), 'w') as output: + with open(manual_commands, 'r') as file: + output.writelines('```bash\n' + file.read() + '\n```') + + patterns = os.path.join(target.scandir, '_patterns.log') + if os.path.isfile(patterns): + with open(os.path.join(report, target.address, 'Patterns' + '.md'), 'w') as output: + with open(patterns, 'r') as file: + output.writelines(file.read()) + + commands = os.path.join(target.scandir, '_commands.log') + if os.path.isfile(commands): + with open(os.path.join(report, target.address, 'Commands' + '.md'), 'w') as output: + with open(commands, 'r') as file: + output.writelines('```bash\n' + file.read() + '\n```') + + errors = os.path.join(target.scandir, '_errors.log') + if os.path.isfile(errors): + with open(os.path.join(report, target.address, 'Errors' + '.md'), 'w') as output: + with open(errors, 'r') as file: + output.writelines('```\n' + file.read() + '\n```')