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.
This commit is contained in:
Tib3rius 2019-04-04 16:46:48 -04:00
parent b580d84a17
commit 31c469e86b
3 changed files with 88 additions and 21 deletions

View File

@ -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}'
]
```

View File

@ -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()

View File

@ -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"'