From 8450863f833026efe1b86355ebd7d6c77b7a2733 Mon Sep 17 00:00:00 2001 From: Tib3rius <48113936+Tib3rius@users.noreply.github.com> Date: Thu, 4 Apr 2019 16:46:48 -0400 Subject: [PATCH] Commands now run via bash instead of sh. patterns.log renamed to _patterns.log and is no longer created by default. New "catch all" service scan added, with an sslscan command that only runs if Nmap detects SSL/TLS. Added a new HTTP scan to screenshot the index page. --- README.md | 72 ++++++++++++++++++++++++++++++++++----- autorecon.py | 23 ++++++------- config/service-scans.toml | 14 ++++++++ 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index fd79787..6facad1 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,12 @@ AutoRecon was inspired by three tools which the author used during the OSCP labs ## Features * Supports multiple targets in the form of IP addresses, IP ranges (CIDR notation), and resolvable hostnames. -* Can scan targets concurrently, utilizing multiple processors. +* Can scan targets concurrently, utilizing multiple processors if they are available. * Customizable port scanning profiles for flexibility in your initial scans. * Customizable service enumeration commands and suggested manual follow-up commands. * An intuitive directory structure for results gathering. -* Full logging of commands that were run. +* Full logging of commands that were run, along with errors if they fail. +* Global and per-scan pattern matching so you can highlight/extract important information from the noise. ## Requirements @@ -39,6 +40,28 @@ $ sudo apt install seclists AutoRecon will still run if you do not install SecLists, though several commands may fail, and some manual commands may not run either. +Additionally the following commands may need to be installed, depending on your OS: + +``` +curl +enum4linux +gobuster +nbtscan +nikto +nmap +onesixtyone +oscanner +smbclient +smbmap +smtp-user-enum +snmpwalk +sslscan +svwar +tnscmd10g +whatweb +wkhtmltoimage +``` + ## Usage AutoRecon uses Python 3 specific functionality and does not support Python 2. @@ -157,7 +180,7 @@ AutoRecon supports multiple targets per scan, and will expand IP ranges provided **Scanning multiple targets with advanced options** ``` -python3 autorecon.py -ct 2 -cs 2 -v -o outputdir 192.168.1.100 192.168.1.1/30 localhost +python3 autorecon.py -ct 2 -cs 2 -vv -o outputdir 192.168.1.100 192.168.1.1/30 localhost [*] Scanning target 192.168.1.100 [*] Scanning target 192.168.1.1 [*] Running service detection nmap-quick on 192.168.1.100 with nmap -vv --reason -Pn -sV -sC --version-all -oN "/root/outputdir/192.168.1.100/scans/_quick_tcp_nmap.txt" -oX "/root/outputdir/192.168.1.100/scans/_quick_tcp_nmap.xml" 192.168.1.100 @@ -189,7 +212,15 @@ python3 autorecon.py -ct 2 -cs 2 -v -o outputdir 192.168.1.100 192.168.1.1/30 lo ... ``` -In this example, the -ct option limits the number of concurrent targets to 2, and the -cs option limits the number of concurrent scans per target to 2. The -v option makes the output verbose, showing the output of every scan being run. The -o option sets a custom output directory for scan results to be saved. +In this example, the -ct option limits the number of concurrent targets to 2, and the -cs option limits the number of concurrent scans per target to 2. The -vv option makes the output very verbose, showing the output of every scan being run. The -o option sets a custom output directory for scan results to be saved. + +### Verbosity + +AutoRecon supports three levels of verbosity: + +* (none) Minimal output. AutoRecon will announce when target scans start and finish, as well as which services were identified. +* (-v) Verbose output. AutoRecon will additionally specify the exact commands which are being run, as well as highlighting any patterns which are matched in command output. +* (-vv) Very verbose output. AutoRecon will output everything. Literally every line from all commands which are currently running. When scanning multiple targets concurrently, this can lead to a ridiculous amount of output. It is not advised to use -vv unless you absolutely need to see live output from commands. ### Results @@ -206,7 +237,8 @@ By default, results will be stored in the ./results directory. A new sub directo │   └── screenshots/ └── scans/ ├── _commands.log - └── _manual_commands.txt + ├── _manual_commands.txt + └── xml/ ``` The exploit directory is intended to contain any exploit code you download / write for the target. @@ -225,6 +257,10 @@ The scans directory is where all results from scans performed by AutoRecon will If a scan results in an error, a file called \_errors.log will also appear in the scans directory with some details to alert the user. +If output matches a defined pattern, a file called \_patterns.log will also appear in the scans directory with details about the matched output. + +The scans/xml directory stores any XML output (e.g. from Nmap scans) separately from the main scan outputs, so that the scans directory itself does not get too cluttered. + ### Port Scan profiles The port-scan-profiles.toml file is where you can define the initial port scans / service detection commands. The configuration file uses the TOML format, which is explained here: https://github.com/toml-lang/toml @@ -297,6 +333,10 @@ service-names = [ name = 'nmap-ftp' command = 'nmap {nmap_extra} -sV -p {port} --script="(ftp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ftp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ftp_nmap.xml" {address}' + [[ftp.scan.pattern]] + description = 'Anonymous FTP Enabled!' + pattern = 'Anonymous FTP login allowed' + [[ftp.manual]] description = 'Bruteforce logins:' commands = [ @@ -316,6 +356,8 @@ The ftp.scan section defines a single scan, named nmap-ftp. This scan defines a * {protocol} is the protocol being used (either tcp or udp). * {address} is the address of the target. +A pattern is defined for the nmap-ftp scan, which matches the simple pattern "Anonymous FTP login allowed". In the event that this pattern matches output of the nmap-ftp command, the pattern description ("Anonymous FTP Enabled!") will be saved to the \_patterns.log file in the scans directory. A special reference {match} can be used in the description to reference the entire match, or the first capturing group. + The ftp.manual section defines a group of manual commands. This group contains a description for the user, and a commands array which contains the commands that a user can run. Two new references are defined here: {username_wordlist} and {password_wordlist} which are configured at the very top of the service-scans.toml file, and default to a username and password wordlist provided by SecLists. Here is a more complicated configuration: @@ -331,7 +373,7 @@ service-names = [ [[smb.scan]] name = 'nmap-smb' - command = 'nmap {nmap_extra} -sV -p {port} --script="(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args=unsafe=1 -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}' + command = 'nmap {nmap_extra} -sV -p {port} --script="(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}' [[smb.scan]] name = 'enum4linux' @@ -352,12 +394,24 @@ service-names = [ run_once = true ports.tcp = [139, 445] + [[smb.scan]] + name = 'smbmap-share-permissions' + command = 'smbmap -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"; smbmap -u null -p "" -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"' + + [[smb.scan]] + name = 'smbmap-list-contents' + command = 'smbmap -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"; smbmap -u null -p "" -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"' + + [[smb.scan]] + name = 'smbmap-execute-command' + command = 'smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"; smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"' + [[smb.manual]] description = 'Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:' commands = [ - 'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args=unsafe=1 -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}', - 'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args=unsafe=1 -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}', - 'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args=unsafe=1 -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}' + 'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}', + 'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}', + 'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}' ] ``` diff --git a/autorecon.py b/autorecon.py index 2799ea9..11e3951 100644 --- a/autorecon.py +++ b/autorecon.py @@ -154,14 +154,14 @@ async def read_stream(stream, target, tag='?', patterns=[], color=Fore.BLUE): if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - ' + p['description'] + '\n\n')) else: for match in matches: if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - Matched Pattern: {match}\n\n')) for p in patterns: @@ -171,14 +171,14 @@ async def read_stream(stream, target, tag='?', patterns=[], color=Fore.BLUE): if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - ' + p['description'] + '\n\n')) else: for match in matches: if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - Matched Pattern: {match}\n\n')) else: break @@ -194,7 +194,7 @@ async def run_cmd(semaphore, cmd, target, tag='?', patterns=[]): with open(os.path.join(scandir, '_commands.log'), 'a') as file: file.writelines(e('{cmd}\n\n')) - process = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + process = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash') await asyncio.wait([ read_stream(process.stdout, target, tag=tag, patterns=patterns), @@ -235,14 +235,14 @@ async def parse_port_scan(stream, tag, target, pattern): if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - ' + p['description'] + '\n\n')) else: for match in matches: if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - Matched Pattern: {match}\n\n')) else: break @@ -270,14 +270,14 @@ async def parse_service_detection(stream, tag, target, pattern): if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - ' + p['description'] + '\n\n')) else: for match in matches: if verbose >= 1: info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}') async with target.lock: - with open(os.path.join(target.scandir, 'patterns.log'), 'a') as file: + with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file: file.writelines(e('{tag} - Matched Pattern: {match}\n\n')) else: break @@ -302,7 +302,7 @@ async def run_portscan(semaphore, tag, target, service_detection, port_scan=None with open(os.path.join(scandir, '_commands.log'), 'a') as file: file.writelines(e('{command}\n\n')) - process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash') output = [ parse_port_scan(process.stdout, tag, target, pattern), @@ -337,7 +337,7 @@ async def run_portscan(semaphore, tag, target, service_detection, port_scan=None with open(os.path.join(scandir, '_commands.log'), 'a') as file: file.writelines(e('{command}\n\n')) - process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash') output = [ parse_service_detection(process.stdout, tag, target, pattern), @@ -538,7 +538,6 @@ def scan_host(target, concurrent_scans): os.makedirs(scandir, exist_ok=True) os.makedirs(os.path.abspath(os.path.join(scandir, 'xml')), exist_ok=True) - open(os.path.abspath(os.path.join(scandir, 'patterns.log')), 'a').close() open(os.path.abspath(os.path.join(reportdir, 'local.txt')), 'a').close() open(os.path.abspath(os.path.join(reportdir, 'proof.txt')), 'a').close() diff --git a/config/service-scans.toml b/config/service-scans.toml index 742c679..6cbd172 100644 --- a/config/service-scans.toml +++ b/config/service-scans.toml @@ -2,6 +2,16 @@ username_wordlist = '/usr/share/seclists/Usernames/top-usernames-shortlist.txt' password_wordlist = '/usr/share/seclists/Passwords/darkweb2017-top100.txt' +[all-services] # Define scans here that you want to run against all services. + +service-names = [ + '.+' +] + + [[all-services.scan]] + name = 'sslscan' + command = 'if [ "{secure}" == "True" ]; then sslscan --show-certificate --no-colour {address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_sslscan.txt"; fi' + [cassandra] service-names = [ @@ -107,6 +117,10 @@ ignore-service-names = [ name = 'curl-robots' command = 'curl -sSik {scheme}://{address}:{port}/robots.txt -m 10 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_robots.txt"' + [[http.scan]] + name = 'wkhtmltoimage' + command = 'if hash wkhtmltoimage 2> /dev/null; then wkhtmltoimage --format png {scheme}://{address}:{port}/ {scandir}/{protocol}_{port}_{scheme}_screenshot.png; fi' + [[http.scan]] name = 'whatweb' command = 'whatweb --color=never --no-errors -a 3 -v {scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_whatweb.txt"'