From ae0eb0629adaab50ceda53e92dfc90b619757ffa Mon Sep 17 00:00:00 2001 From: Tib3rius <48113936+Tib3rius@users.noreply.github.com> Date: Mon, 14 Feb 2022 22:15:20 -0500 Subject: [PATCH] Plugin Updates Separated plugins out into separate files to make them easier to understand. --- .../{ftp.py => bruteforce-ftp.py} | 13 - autorecon/default-plugins/bruteforce-http.py | 20 ++ .../{rdp.py => bruteforce-rdp.py} | 13 - .../{ssh.py => bruteforce-ssh.py} | 13 - .../default-plugins/curl-known-security.py | 26 ++ autorecon/default-plugins/curl-robots.py | 26 ++ autorecon/default-plugins/curl.py | 18 ++ autorecon/default-plugins/databases.py | 134 --------- .../default-plugins/default-port-scan.py | 102 ------- autorecon/default-plugins/dirbuster.py | 75 +++++ .../default-plugins/dns-reverse-lookup.py | 14 + .../default-plugins/dns-zone-transfer.py | 18 ++ autorecon/default-plugins/dns.py | 143 --------- .../dnsrecon-subdomain-bruteforce.py | 26 ++ autorecon/default-plugins/dnsrecon.py | 29 ++ autorecon/default-plugins/enum4linux.py | 18 ++ autorecon/default-plugins/get-arch.py | 16 + autorecon/default-plugins/http_server.py | 282 ------------------ .../{ldap.py => ldap-search.py} | 13 - autorecon/default-plugins/lookup-sid.py | 16 + autorecon/default-plugins/misc.py | 201 ------------- autorecon/default-plugins/nbtscan.py | 17 ++ autorecon/default-plugins/nfs.py | 40 --- autorecon/default-plugins/nikto.py | 16 + autorecon/default-plugins/nmap-cassandra.py | 14 + autorecon/default-plugins/nmap-cups.py | 14 + autorecon/default-plugins/nmap-distccd.py | 14 + autorecon/default-plugins/nmap-dns.py | 14 + autorecon/default-plugins/nmap-finger.py | 14 + autorecon/default-plugins/nmap-ftp.py | 14 + autorecon/default-plugins/nmap-http.py | 17 ++ autorecon/default-plugins/nmap-imap.py | 14 + autorecon/default-plugins/nmap-irc.py | 14 + .../{kerberos.py => nmap-kerberos.py} | 0 autorecon/default-plugins/nmap-ldap.py | 14 + autorecon/default-plugins/nmap-mongodb.py | 14 + autorecon/default-plugins/nmap-mountd.py | 14 + autorecon/default-plugins/nmap-msrpc.py | 14 + autorecon/default-plugins/nmap-mssql.py | 18 ++ .../default-plugins/nmap-multicast-dns.py | 14 + autorecon/default-plugins/nmap-mysql.py | 18 ++ autorecon/default-plugins/nmap-nfs.py | 14 + autorecon/default-plugins/nmap-nntp.py | 14 + autorecon/default-plugins/nmap-ntp.py | 14 + autorecon/default-plugins/nmap-oracle.py | 17 ++ autorecon/default-plugins/nmap-pop3.py | 14 + autorecon/default-plugins/nmap-rdp.py | 14 + autorecon/default-plugins/nmap-redis.py | 14 + autorecon/default-plugins/nmap-rmi.py | 14 + .../{rsync.py => nmap-rsync.py} | 13 - .../default-plugins/{sip.py => nmap-sip.py} | 14 - autorecon/default-plugins/nmap-smb.py | 14 + autorecon/default-plugins/nmap-smtp.py | 14 + autorecon/default-plugins/nmap-snmp.py | 14 + autorecon/default-plugins/nmap-ssh.py | 14 + autorecon/default-plugins/nmap-telnet.py | 14 + autorecon/default-plugins/nmap-tftp.py | 14 + autorecon/default-plugins/nmap-vnc.py | 14 + autorecon/default-plugins/onesixtyone.py | 18 ++ autorecon/default-plugins/oracle-odat.py | 21 ++ autorecon/default-plugins/oracle-patator.py | 14 + autorecon/default-plugins/oracle-scanner.py | 19 ++ autorecon/default-plugins/oracle-tnscmd.py | 21 ++ .../default-plugins/portscan-all-tcp-ports.py | 41 +++ ...rt-scan.py => portscan-guess-tcp-ports.py} | 0 .../portscan-top-100-udp-ports.py | 40 +++ .../default-plugins/portscan-top-tcp-ports.py | 26 ++ .../{redis.py => redis-cli.py} | 13 - .../{reporting.py => reporting-cherrytree.py} | 72 ----- .../default-plugins/reporting-markdown.py | 75 +++++ autorecon/default-plugins/rpc.py | 57 ---- autorecon/default-plugins/rpcclient.py | 14 + autorecon/default-plugins/rpcdump.py | 16 + autorecon/default-plugins/rsync-list-files.py | 14 + autorecon/default-plugins/showmount.py | 14 + autorecon/default-plugins/sipvicious.py | 15 + autorecon/default-plugins/smb-vuln.py | 24 ++ autorecon/default-plugins/smb.py | 119 -------- autorecon/default-plugins/smbclient.py | 16 + autorecon/default-plugins/smbmap.py | 20 ++ .../{smtp.py => smtp-user-enum.py} | 13 - .../default-plugins/{snmp.py => snmpwalk.py} | 30 -- .../default-plugins/subdomain-enumeration.py | 34 +++ .../virtual-host-enumeration.py | 39 +++ autorecon/default-plugins/whatweb.py | 16 + autorecon/default-plugins/winrm-detection.py | 32 ++ autorecon/default-plugins/wkhtmltoimage.py | 22 ++ autorecon/default-plugins/wpscan.py | 20 ++ autorecon/main.py | 2 +- pyproject.toml | 2 +- 90 files changed, 1356 insertions(+), 1287 deletions(-) rename autorecon/default-plugins/{ftp.py => bruteforce-ftp.py} (67%) create mode 100644 autorecon/default-plugins/bruteforce-http.py rename autorecon/default-plugins/{rdp.py => bruteforce-rdp.py} (66%) rename autorecon/default-plugins/{ssh.py => bruteforce-ssh.py} (68%) create mode 100644 autorecon/default-plugins/curl-known-security.py create mode 100644 autorecon/default-plugins/curl-robots.py create mode 100644 autorecon/default-plugins/curl.py delete mode 100644 autorecon/default-plugins/databases.py delete mode 100644 autorecon/default-plugins/default-port-scan.py create mode 100644 autorecon/default-plugins/dirbuster.py create mode 100644 autorecon/default-plugins/dns-reverse-lookup.py create mode 100644 autorecon/default-plugins/dns-zone-transfer.py delete mode 100644 autorecon/default-plugins/dns.py create mode 100644 autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py create mode 100644 autorecon/default-plugins/dnsrecon.py create mode 100644 autorecon/default-plugins/enum4linux.py create mode 100644 autorecon/default-plugins/get-arch.py delete mode 100644 autorecon/default-plugins/http_server.py rename autorecon/default-plugins/{ldap.py => ldap-search.py} (53%) create mode 100644 autorecon/default-plugins/lookup-sid.py delete mode 100644 autorecon/default-plugins/misc.py create mode 100644 autorecon/default-plugins/nbtscan.py delete mode 100644 autorecon/default-plugins/nfs.py create mode 100644 autorecon/default-plugins/nikto.py create mode 100644 autorecon/default-plugins/nmap-cassandra.py create mode 100644 autorecon/default-plugins/nmap-cups.py create mode 100644 autorecon/default-plugins/nmap-distccd.py create mode 100644 autorecon/default-plugins/nmap-dns.py create mode 100644 autorecon/default-plugins/nmap-finger.py create mode 100644 autorecon/default-plugins/nmap-ftp.py create mode 100644 autorecon/default-plugins/nmap-http.py create mode 100644 autorecon/default-plugins/nmap-imap.py create mode 100644 autorecon/default-plugins/nmap-irc.py rename autorecon/default-plugins/{kerberos.py => nmap-kerberos.py} (100%) create mode 100644 autorecon/default-plugins/nmap-ldap.py create mode 100644 autorecon/default-plugins/nmap-mongodb.py create mode 100644 autorecon/default-plugins/nmap-mountd.py create mode 100644 autorecon/default-plugins/nmap-msrpc.py create mode 100644 autorecon/default-plugins/nmap-mssql.py create mode 100644 autorecon/default-plugins/nmap-multicast-dns.py create mode 100644 autorecon/default-plugins/nmap-mysql.py create mode 100644 autorecon/default-plugins/nmap-nfs.py create mode 100644 autorecon/default-plugins/nmap-nntp.py create mode 100644 autorecon/default-plugins/nmap-ntp.py create mode 100644 autorecon/default-plugins/nmap-oracle.py create mode 100644 autorecon/default-plugins/nmap-pop3.py create mode 100644 autorecon/default-plugins/nmap-rdp.py create mode 100644 autorecon/default-plugins/nmap-redis.py create mode 100644 autorecon/default-plugins/nmap-rmi.py rename autorecon/default-plugins/{rsync.py => nmap-rsync.py} (59%) rename autorecon/default-plugins/{sip.py => nmap-sip.py} (56%) create mode 100644 autorecon/default-plugins/nmap-smb.py create mode 100644 autorecon/default-plugins/nmap-smtp.py create mode 100644 autorecon/default-plugins/nmap-snmp.py create mode 100644 autorecon/default-plugins/nmap-ssh.py create mode 100644 autorecon/default-plugins/nmap-telnet.py create mode 100644 autorecon/default-plugins/nmap-tftp.py create mode 100644 autorecon/default-plugins/nmap-vnc.py create mode 100644 autorecon/default-plugins/onesixtyone.py create mode 100644 autorecon/default-plugins/oracle-odat.py create mode 100644 autorecon/default-plugins/oracle-patator.py create mode 100644 autorecon/default-plugins/oracle-scanner.py create mode 100644 autorecon/default-plugins/oracle-tnscmd.py create mode 100644 autorecon/default-plugins/portscan-all-tcp-ports.py rename autorecon/default-plugins/{guess-port-scan.py => portscan-guess-tcp-ports.py} (100%) create mode 100644 autorecon/default-plugins/portscan-top-100-udp-ports.py create mode 100644 autorecon/default-plugins/portscan-top-tcp-ports.py rename autorecon/default-plugins/{redis.py => redis-cli.py} (69%) rename autorecon/default-plugins/{reporting.py => reporting-cherrytree.py} (53%) create mode 100644 autorecon/default-plugins/reporting-markdown.py delete mode 100644 autorecon/default-plugins/rpc.py create mode 100644 autorecon/default-plugins/rpcclient.py create mode 100644 autorecon/default-plugins/rpcdump.py create mode 100644 autorecon/default-plugins/rsync-list-files.py create mode 100644 autorecon/default-plugins/showmount.py create mode 100644 autorecon/default-plugins/sipvicious.py create mode 100644 autorecon/default-plugins/smb-vuln.py delete mode 100644 autorecon/default-plugins/smb.py create mode 100644 autorecon/default-plugins/smbclient.py create mode 100644 autorecon/default-plugins/smbmap.py rename autorecon/default-plugins/{smtp.py => smtp-user-enum.py} (70%) rename autorecon/default-plugins/{snmp.py => snmpwalk.py} (55%) create mode 100644 autorecon/default-plugins/subdomain-enumeration.py create mode 100644 autorecon/default-plugins/virtual-host-enumeration.py create mode 100644 autorecon/default-plugins/whatweb.py create mode 100644 autorecon/default-plugins/winrm-detection.py create mode 100644 autorecon/default-plugins/wkhtmltoimage.py create mode 100644 autorecon/default-plugins/wpscan.py diff --git a/autorecon/default-plugins/ftp.py b/autorecon/default-plugins/bruteforce-ftp.py similarity index 67% rename from autorecon/default-plugins/ftp.py rename to autorecon/default-plugins/bruteforce-ftp.py index edd470b..24cd7c8 100644 --- a/autorecon/default-plugins/ftp.py +++ b/autorecon/default-plugins/bruteforce-ftp.py @@ -1,18 +1,5 @@ from autorecon.plugins import ServiceScan -class NmapFTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap FTP' - self.tags = ['default', 'safe', 'ftp'] - - def configure(self): - self.match_service_name(['^ftp', '^ftp\-data']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(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}') - class BruteforceFTP(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/bruteforce-http.py b/autorecon/default-plugins/bruteforce-http.py new file mode 100644 index 0000000..d735ac4 --- /dev/null +++ b/autorecon/default-plugins/bruteforce-http.py @@ -0,0 +1,20 @@ +from autorecon.plugins import ServiceScan + +class BruteforceHTTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Bruteforce HTTP" + self.tags = ['default', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + def manual(self, service, plugin_was_run): + service.add_manual_commands('Credential bruteforcing commands (don\'t run these without modifying them):', [ + 'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{http_scheme}_auth_hydra.txt" {http_scheme}-get://{addressv6}/path/to/auth/area', + 'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{http_scheme}_auth_medusa.txt" -M http -h {addressv6} -m DIR:/path/to/auth/area', + 'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{http_scheme}_form_hydra.txt" {http_scheme}-post-form://{addressv6}/path/to/login.php:"username=^USER^&password=^PASS^":"invalid-login-message"', + 'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{http_scheme}_form_medusa.txt" -M web-form -h {addressv6} -m FORM:/path/to/login.php -m FORM-DATA:"post?username=&password=" -m DENY-SIGNAL:"invalid login message"' + ]) diff --git a/autorecon/default-plugins/rdp.py b/autorecon/default-plugins/bruteforce-rdp.py similarity index 66% rename from autorecon/default-plugins/rdp.py rename to autorecon/default-plugins/bruteforce-rdp.py index e99334f..fb38e82 100644 --- a/autorecon/default-plugins/rdp.py +++ b/autorecon/default-plugins/bruteforce-rdp.py @@ -1,18 +1,5 @@ from autorecon.plugins import ServiceScan -class NmapRDP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap RDP" - self.tags = ['default', 'safe', 'rdp'] - - def configure(self): - self.match_service_name(['^rdp', '^ms\-wbt\-server', '^ms\-term\-serv']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rdp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rdp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rdp_nmap.xml" {address}') - class BruteforceRDP(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/ssh.py b/autorecon/default-plugins/bruteforce-ssh.py similarity index 68% rename from autorecon/default-plugins/ssh.py rename to autorecon/default-plugins/bruteforce-ssh.py index 2ecedab..75bb436 100644 --- a/autorecon/default-plugins/ssh.py +++ b/autorecon/default-plugins/bruteforce-ssh.py @@ -1,18 +1,5 @@ from autorecon.plugins import ServiceScan -class NmapSSH(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap SSH" - self.tags = ['default', 'safe', 'ssh'] - - def configure(self): - self.match_service_name('^ssh') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods" -oN "{scandir}/{protocol}_{port}_ssh_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ssh_nmap.xml" {address}') - class BruteforceSSH(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/curl-known-security.py b/autorecon/default-plugins/curl-known-security.py new file mode 100644 index 0000000..554db9e --- /dev/null +++ b/autorecon/default-plugins/curl-known-security.py @@ -0,0 +1,26 @@ +from autorecon.plugins import ServiceScan +from autorecon.io import fformat + +class CurlKnownSecurity(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Known Security" + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + async def run(self, service): + if service.protocol == 'tcp': + process, stdout, _ = await service.execute('curl -sSikf {http_scheme}://{addressv6}:{port}/.well-known/security.txt', future_outfile='{protocol}_{port}_{http_scheme}_known-security.txt') + + lines = await stdout.readlines() + + if process.returncode == 0 and lines: + filename = fformat('{scandir}/{protocol}_{port}_{http_scheme}_known-security.txt') + with open(filename, mode='wt', encoding='utf8') as robots: + robots.write('\n'.join(lines)) + else: + service.info('{bblue}[' + fformat('{tag}') + ']{rst} There did not appear to be a .well-known/security.txt file in the webroot (/).') diff --git a/autorecon/default-plugins/curl-robots.py b/autorecon/default-plugins/curl-robots.py new file mode 100644 index 0000000..7158b25 --- /dev/null +++ b/autorecon/default-plugins/curl-robots.py @@ -0,0 +1,26 @@ +from autorecon.plugins import ServiceScan +from autorecon.io import fformat + +class CurlRobots(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Curl Robots" + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + async def run(self, service): + if service.protocol == 'tcp': + process, stdout, _ = await service.execute('curl -sSikf {http_scheme}://{addressv6}:{port}/robots.txt', future_outfile='{protocol}_{port}_{http_scheme}_curl-robots.txt') + + lines = await stdout.readlines() + + if process.returncode == 0 and lines: + filename = fformat('{scandir}/{protocol}_{port}_{http_scheme}_curl-robots.txt') + with open(filename, mode='wt', encoding='utf8') as robots: + robots.write('\n'.join(lines)) + else: + service.info('{bblue}[' + fformat('{tag}') + ']{rst} There did not appear to be a robots.txt file in the webroot (/).') diff --git a/autorecon/default-plugins/curl.py b/autorecon/default-plugins/curl.py new file mode 100644 index 0000000..5155f50 --- /dev/null +++ b/autorecon/default-plugins/curl.py @@ -0,0 +1,18 @@ +from autorecon.plugins import ServiceScan + +class Curl(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Curl" + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.add_option("path", default="/", help="The path on the web server to curl. Default: %(default)s") + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + self.add_pattern('(?i)powered[ -]by[^\n]+') + + async def run(self, service): + if service.protocol == 'tcp': + await service.execute('curl -sSik {http_scheme}://{addressv6}:{port}' + self.get_option('path'), outfile='{protocol}_{port}_{http_scheme}_curl.html') diff --git a/autorecon/default-plugins/databases.py b/autorecon/default-plugins/databases.py deleted file mode 100644 index 0c8ca47..0000000 --- a/autorecon/default-plugins/databases.py +++ /dev/null @@ -1,134 +0,0 @@ -from autorecon.plugins import ServiceScan -from shutil import which - -class NmapMongoDB(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap MongoDB" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name('^mongod') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(mongodb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mongodb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mongodb_nmap.xml" {address}') - -class NmapMSSQL(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap MSSQL" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name(['^mssql', '^ms\-sql']) - - def manual(self, service, plugin_was_run): - if service.target.ipversion == 'IPv4': - service.add_manual_command('(sqsh) interactive database shell:', 'sqsh -U -P -S {address}:{port}') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ms-sql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="mssql.instance-port={port},mssql.username=sa,mssql.password=sa" -oN "{scandir}/{protocol}_{port}_mssql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mssql_nmap.xml" {address}') - -class NmapMYSQL(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap MYSQL" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name('^mysql') - - def manual(self, service, plugin_was_run): - if service.target.ipversion == 'IPv4': - service.add_manual_command('(sqsh) interactive database shell:', 'sqsh -U -P -S {address}:{port}') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(mysql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mysql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mysql_nmap.xml" {address}') - -class NmapOracle(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap Oracle" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name('^oracle') - - def manual(self, service, plugin_was_run): - service.add_manual_command('Brute-force SIDs using Nmap:', 'nmap {nmap_extra} -sV -p {port} --script="banner,oracle-sid-brute" -oN "{scandir}/{protocol}_{port}_oracle_sid-brute_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_sid-brute_nmap.xml" {address}') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(oracle* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_oracle_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_nmap.xml" {address}') - -class OracleTNScmd(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Oracle TNScmd" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name('^oracle') - - def check(self): - if which('tnscmd10g') is None: - self.error('The tnscmd10g program could not be found. Make sure it is installed. (On Kali, run: sudo apt install tnscmd10g)') - - async def run(self, service): - if service.target.ipversion == 'IPv4': - await service.execute('tnscmd10g ping -h {address} -p {port} 2>&1', outfile='{protocol}_{port}_oracle_tnscmd_ping.txt') - await service.execute('tnscmd10g version -h {address} -p {port} 2>&1', outfile='{protocol}_{port}_oracle_tnscmd_version.txt') - -class OracleScanner(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Oracle Scanner" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name('^oracle') - - def check(self): - if which('oscanner') is None: - self.error('The oscanner program could not be found. Make sure it is installed. (On Kali, run: sudo apt install oscanner)') - - async def run(self, service): - await service.execute('oscanner -v -s {address} -P {port} 2>&1', outfile='{protocol}_{port}_oracle_scanner.txt') - -class OracleODAT(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Oracle ODAT" - self.tags = ['default', 'safe', 'databases'] - - def configure(self): - self.match_service_name('^oracle') - - def manual(self, service, plugin_was_run): - service.add_manual_commands('Install ODAT (https://github.com/quentinhardy/odat) and run the following commands:', [ - 'python odat.py tnscmd -s {address} -p {port} --ping', - 'python odat.py tnscmd -s {address} -p {port} --version', - 'python odat.py tnscmd -s {address} -p {port} --status', - 'python odat.py sidguesser -s {address} -p {port}', - 'python odat.py passwordguesser -s {address} -p {port} -d --accounts-file accounts/accounts_multiple.txt', - 'python odat.py tnspoison -s {address} -p {port} -d --test-module' - ]) - -class OraclePatator(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Oracle Patator" - self.tags = ['default', 'databases'] - - def configure(self): - self.match_service_name('^oracle') - - def manual(self, service, plugin_was_run): - service.add_manual_command('Install Oracle Instant Client (https://github.com/rapid7/metasploit-framework/wiki/How-to-get-Oracle-Support-working-with-Kali-Linux) and then bruteforce with patator:', 'patator oracle_login host={address} port={port} user=COMBO00 password=COMBO01 0=/usr/share/seclists/Passwords/Default-Credentials/oracle-betterdefaultpasslist.txt -x ignore:code=ORA-01017 -x ignore:code=ORA-28000') diff --git a/autorecon/default-plugins/default-port-scan.py b/autorecon/default-plugins/default-port-scan.py deleted file mode 100644 index 1ca99a7..0000000 --- a/autorecon/default-plugins/default-port-scan.py +++ /dev/null @@ -1,102 +0,0 @@ -from autorecon.plugins import PortScan -from autorecon.config import config -import os, re - -class QuickTCPPortScan(PortScan): - - def __init__(self): - super().__init__() - self.name = 'Top TCP Ports' - self.description = 'Performs an Nmap scan of the top 1000 TCP ports.' - self.type = 'tcp' - self.tags = ['default', 'default-port-scan'] - self.priority = 0 - - async def run(self, target): - if target.ports: # Don't run this plugin if there are custom ports. - return [] - - if config['proxychains']: - traceroute_os = '' - else: - traceroute_os = ' -A --osscan-guess' - - process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}', blocking=False) - services = await target.extract_services(stdout) - await process.wait() - return services - -class AllTCPPortScan(PortScan): - - def __init__(self): - super().__init__() - self.name = 'All TCP Ports' - self.description = 'Performs an Nmap scan of all TCP ports.' - self.type = 'tcp' - self.specific_ports = True - self.tags = ['default', 'default-port-scan', 'long'] - - async def run(self, target): - if config['proxychains']: - traceroute_os = '' - else: - traceroute_os = ' -A --osscan-guess' - - if target.ports: - if target.ports['tcp']: - process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -p ' + target.ports['tcp'] + ' -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}', blocking=False) - else: - return [] - else: - process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -p- -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}', blocking=False) - services = [] - while True: - line = await stdout.readline() - if line is not None: - match = re.search('^Discovered open port ([0-9]+)/tcp', line) - if match: - target.info('Discovered open port {bmagenta}tcp/' + match.group(1) + '{rst} on {byellow}' + target.address + '{rst}', verbosity=1) - service = target.extract_service(line) - if service: - services.append(service) - else: - break - await process.wait() - return services - -class Top100UDPPortScan(PortScan): - - def __init__(self): - super().__init__() - self.name = 'Top 100 UDP Ports' - self.description = 'Performs an Nmap scan of the top 100 UDP ports.' - self.type = 'udp' - self.specific_ports = True - self.tags = ['default', 'default-port-scan', 'long'] - - async def run(self, target): - # Only run UDP scan if user is root. - if os.getuid() == 0: - if target.ports: - if target.ports['udp']: - process, stdout, stderr = await target.execute('nmap {nmap_extra} -sU -A --osscan-guess -p ' + target.ports['udp'] + ' -oN "{scandir}/_custom_ports_udp_nmap.txt" -oX "{scandir}/xml/_custom_ports_udp_nmap.xml" {address}', blocking=False) - else: - return [] - else: - process, stdout, stderr = await target.execute('nmap {nmap_extra} -sU -A --top-ports 100 -oN "{scandir}/_top_100_udp_nmap.txt" -oX "{scandir}/xml/_top_100_udp_nmap.xml" {address}', blocking=False) - services = [] - while True: - line = await stdout.readline() - if line is not None: - match = re.search('^Discovered open port ([0-9]+)/udp', line) - if match: - target.info('Discovered open port {bmagenta}udp/' + match.group(1) + '{rst} on {byellow}' + target.address + '{rst}', verbosity=1) - service = target.extract_service(line) - if service: - services.append(service) - else: - break - await process.wait() - return services - else: - target.error('UDP scan requires AutoRecon be run with root privileges.') diff --git a/autorecon/default-plugins/dirbuster.py b/autorecon/default-plugins/dirbuster.py new file mode 100644 index 0000000..f825a67 --- /dev/null +++ b/autorecon/default-plugins/dirbuster.py @@ -0,0 +1,75 @@ +from autorecon.plugins import ServiceScan +from autorecon.config import config +from shutil import which +import os + +class DirBuster(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Directory Buster" + self.slug = 'dirbuster' + self.priority = 0 + self.tags = ['default', 'safe', 'long', 'http'] + + def configure(self): + self.add_choice_option('tool', default='feroxbuster', choices=['feroxbuster', 'gobuster', 'dirsearch', 'ffuf', 'dirb'], help='The tool to use for directory busting. Default: %(default)s') + self.add_list_option('wordlist', default=[os.path.join(config['config_dir'], 'wordlists', 'dirbuster.txt')], help='The wordlist(s) to use when directory busting. Separate multiple wordlists with spaces. Default: %(default)s') + self.add_option('threads', default=10, help='The number of threads to use when directory busting. Default: %(default)s') + self.add_option('ext', default='txt,html,php,asp,aspx,jsp', help='The extensions you wish to fuzz (no dot, comma separated). Default: %(default)s') + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + def check(self): + tool = self.get_option('tool') + if tool == 'feroxbuster': + if which('feroxbuster') is None: + self.error('The feroxbuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install feroxbuster)') + elif tool == 'gobuster': + if which('gobuster') is None: + self.error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)') + elif tool == 'dirsearch': + if which('dirsearch') is None: + self.error('The dirsearch program could not be found. Make sure it is installed. (On Kali, run: sudo apt install dirsearch)') + + async def run(self, service): + dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')]) + for wordlist in self.get_option('wordlist'): + name = os.path.splitext(os.path.basename(wordlist))[0] + if self.get_option('tool') == 'feroxbuster': + await service.execute('feroxbuster -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -x "' + self.get_option('ext') + '" -v -k -n -q -e -o "{scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_' + name + '.txt"') + elif self.get_option('tool') == 'gobuster': + await service.execute('gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e -k -x "' + self.get_option('ext') + '" -z -d -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_' + name + '.txt"') + elif self.get_option('tool') == 'dirsearch': + if service.target.ipversion == 'IPv6': + service.error('dirsearch does not support IPv6.') + else: + await service.execute('dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f -q -w ' + wordlist + ' --format=plain -o "{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_' + name + '.txt"') + elif self.get_option('tool') == 'ffuf': + await service.execute('ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e "' + dot_extensions + '" -v -noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_' + name + '.txt') + elif self.get_option('tool') == 'dirb': + await service.execute('dirb {http_scheme}://{addressv6}:{port}/ ' + wordlist + ' -l -r -S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_' + name + '.txt"') + + def manual(self, service, plugin_was_run): + dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')]) + if self.get_option('tool') == 'feroxbuster': + service.add_manual_command('(feroxbuster) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ + 'feroxbuster -u {http_scheme}://{addressv6}:{port} -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x "' + self.get_option('ext') + '" -v -k -n -o {scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_dirbuster.txt' + ]) + elif self.get_option('tool') == 'gobuster': + service.add_manual_command('(gobuster v3) Multi-threaded directory/file enumeration for web servers using various wordlists:', [ + 'gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -x "' + self.get_option('ext') + '" -z -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_dirbuster.txt"' + ]) + elif self.get_option('tool') == 'dirsearch': + if service.target.ipversion == 'IPv4': + service.add_manual_command('(dirsearch) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ + 'dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -r -e "' + self.get_option('ext') + '" -f -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --format=plain --output="{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_dirbuster.txt"' + ]) + elif self.get_option('tool') == 'ffuf': + service.add_manual_command('(ffuf) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ + 'ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e "' + dot_extensions + '" -v -noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_dirbuster.txt' + ]) + elif self.get_option('tool') == 'dirb': + service.add_manual_command('(dirb) Recursive directory/file enumeration for web servers using various wordlists:', [ + 'dirb {http_scheme}://{addressv6}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l -r -S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_dirbuster.txt"' + ]) diff --git a/autorecon/default-plugins/dns-reverse-lookup.py b/autorecon/default-plugins/dns-reverse-lookup.py new file mode 100644 index 0000000..513cba5 --- /dev/null +++ b/autorecon/default-plugins/dns-reverse-lookup.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class DNSReverseLookup(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'DNS Reverse Lookup' + self.tags = ['default', 'safe', 'dns'] + + def configure(self): + self.match_service_name('^domain') + + async def run(self, service): + await service.execute('dig -p {port} -x {address} @{address}', outfile='{protocol}_{port}_dns_reverse-lookup.txt') diff --git a/autorecon/default-plugins/dns-zone-transfer.py b/autorecon/default-plugins/dns-zone-transfer.py new file mode 100644 index 0000000..49bb9cb --- /dev/null +++ b/autorecon/default-plugins/dns-zone-transfer.py @@ -0,0 +1,18 @@ +from autorecon.plugins import ServiceScan + +class DNSZoneTransfer(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'DNS Zone Transfer' + self.tags = ['default', 'safe', 'dns'] + + def configure(self): + self.match_service_name('^domain') + + async def run(self, service): + if self.get_global('domain'): + await service.execute('dig AXFR -p {port} @{address} ' + self.get_global('domain'), outfile='{protocol}_{port}_dns_zone-transfer-domain.txt') + if service.target.type == 'hostname': + await service.execute('dig AXFR -p {port} @{address} {address}', outfile='{protocol}_{port}_dns_zone-transfer-hostname.txt') + await service.execute('dig AXFR -p {port} @{address}', outfile='{protocol}_{port}_dns_zone-transfer.txt') diff --git a/autorecon/default-plugins/dns.py b/autorecon/default-plugins/dns.py deleted file mode 100644 index cc72915..0000000 --- a/autorecon/default-plugins/dns.py +++ /dev/null @@ -1,143 +0,0 @@ -from autorecon.plugins import ServiceScan -from shutil import which -import os - -class NmapDNS(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap DNS' - self.tags = ['default', 'safe', 'dns'] - - def configure(self): - self.match_service_name('^domain') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_dns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_dns_nmap.xml" {address}') - -class DNSZoneTransfer(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'DNS Zone Transfer' - self.tags = ['default', 'safe', 'dns'] - - def configure(self): - self.match_service_name('^domain') - - async def run(self, service): - if self.get_global('domain'): - await service.execute('dig AXFR -p {port} @{address} ' + self.get_global('domain'), outfile='{protocol}_{port}_dns_zone-transfer-domain.txt') - if service.target.type == 'hostname': - await service.execute('dig AXFR -p {port} @{address} {address}', outfile='{protocol}_{port}_dns_zone-transfer-hostname.txt') - await service.execute('dig AXFR -p {port} @{address}', outfile='{protocol}_{port}_dns_zone-transfer.txt') - -class DNSReverseLookup(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'DNS Reverse Lookup' - self.tags = ['default', 'safe', 'dns'] - - def configure(self): - self.match_service_name('^domain') - - async def run(self, service): - await service.execute('dig -p {port} -x {address} @{address}', outfile='{protocol}_{port}_dns_reverse-lookup.txt') - -class NmapMulticastDNS(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap Multicast DNS' - self.tags = ['default', 'safe', 'dns'] - - def configure(self): - self.match_service_name(['^mdns', '^zeroconf']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_multicastdns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_multicastdns_nmap.xml" {address}') - - -class DnsReconDefault(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "DnsRecon Default Scan" - self.slug = 'dnsrecon' - self.priority = 0 - self.tags = ['default', 'safe', 'dns'] - - def configure(self): - self.match_service_name('^domain') - - def check(self): - if which('dnsrecon') is None: - self.error('The program dnsrecon could not be found. Make sure it is installed. (On Kali, run: sudo apt install dnsrecon)') - - def manual(self, service, plugin_was_run): - service.add_manual_command('Use dnsrecon to automatically query data from the DNS server. You must specify the target domain name.', [ - 'dnsrecon -n {address} -d 2>&1 | tee {scandir}/{protocol}_{port}_dnsrecon_default_manual.txt' - ]) - - async def run(self, service): - if self.get_global('domain'): - await service.execute('dnsrecon -n {address} -d ' + self.get_global('domain') + ' 2>&1', outfile='{protocol}_{port}_dnsrecon_default.txt') - else: - service.error('A domain name was not specified in the command line options (--global.domain). If you know the domain name, look in the _manual_commands.txt file for the dnsrecon command.') - -class DnsReconSubdomainBruteforce(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "DnsRecon Bruteforce Subdomains" - self.slug = 'dnsrecon-brute' - self.priority = 0 - self.tags = ['default', 'safe', 'long', 'dns'] - - def configure(self): - self.match_service_name('^domain') - - def check(self): - if which('dnsrecon') is None: - self.error('The program dnsrecon could not be found. Make sure it is installed. (On Kali, run: sudo apt install dnsrecon)') - - def manual(self, service, plugin_was_run): - domain_name = '' - if self.get_global('domain'): - domain_name = self.get_global('domain') - service.add_manual_command('Use dnsrecon to bruteforce subdomains of a DNS domain.', [ - 'dnsrecon -n {address} -d ' + domain_name + ' -D /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t brt 2>&1 | tee {scandir}/{protocol}_{port}_dnsrecon_subdomain_bruteforce.txt', - ]) - -class SubdomainEnumeration(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Subdomain Enumeration" - self.slug = "subdomain-enum" - self.tags = ['default', 'safe', 'long', 'dns'] - - def configure(self): - self.add_option('domain', help='The domain to use as the base domain (e.g. example.com) for subdomain enumeration. Default: %(default)s') - self.add_list_option('wordlist', default=['/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt'], help='The wordlist(s) to use when enumerating subdomains. Separate multiple wordlists with spaces. Default: %(default)s') - self.add_option('threads', default=10, help='The number of threads to use when enumerating subdomains. Default: %(default)s') - self.match_service_name('^domain') - - async def run(self, service): - domains = [] - - if self.get_option('domain'): - domains.append(self.get_option('domain')) - if service.target.type == 'hostname' and service.target.address not in domains: - domains.append(service.target.address) - if self.get_global('domain') and self.get_global('domain') not in domains: - domains.append(self.get_global('domain')) - - if len(domains) > 0: - for wordlist in self.get_option('wordlist'): - name = os.path.splitext(os.path.basename(wordlist))[0] - for domain in domains: - await service.execute('gobuster dns -d ' + domain + ' -r {addressv6} -w ' + wordlist + ' -o "{scandir}/{protocol}_{port}_' + domain + '_subdomains_' + name + '.txt"') - else: - service.info('The target was not a domain, nor was a domain provided as an option. Skipping subdomain enumeration.') diff --git a/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py b/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py new file mode 100644 index 0000000..b9306fb --- /dev/null +++ b/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py @@ -0,0 +1,26 @@ +from autorecon.plugins import ServiceScan +from shutil import which + +class DnsReconSubdomainBruteforce(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "DnsRecon Bruteforce Subdomains" + self.slug = 'dnsrecon-brute' + self.priority = 0 + self.tags = ['default', 'safe', 'long', 'dns'] + + def configure(self): + self.match_service_name('^domain') + + def check(self): + if which('dnsrecon') is None: + self.error('The program dnsrecon could not be found. Make sure it is installed. (On Kali, run: sudo apt install dnsrecon)') + + def manual(self, service, plugin_was_run): + domain_name = '' + if self.get_global('domain'): + domain_name = self.get_global('domain') + service.add_manual_command('Use dnsrecon to bruteforce subdomains of a DNS domain.', [ + 'dnsrecon -n {address} -d ' + domain_name + ' -D /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t brt 2>&1 | tee {scandir}/{protocol}_{port}_dnsrecon_subdomain_bruteforce.txt', + ]) diff --git a/autorecon/default-plugins/dnsrecon.py b/autorecon/default-plugins/dnsrecon.py new file mode 100644 index 0000000..f53c51d --- /dev/null +++ b/autorecon/default-plugins/dnsrecon.py @@ -0,0 +1,29 @@ +from autorecon.plugins import ServiceScan +from shutil import which + +class DnsRecon(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "DnsRecon Default Scan" + self.slug = 'dnsrecon' + self.priority = 0 + self.tags = ['default', 'safe', 'dns'] + + def configure(self): + self.match_service_name('^domain') + + def check(self): + if which('dnsrecon') is None: + self.error('The program dnsrecon could not be found. Make sure it is installed. (On Kali, run: sudo apt install dnsrecon)') + + def manual(self, service, plugin_was_run): + service.add_manual_command('Use dnsrecon to automatically query data from the DNS server. You must specify the target domain name.', [ + 'dnsrecon -n {address} -d 2>&1 | tee {scandir}/{protocol}_{port}_dnsrecon_default_manual.txt' + ]) + + async def run(self, service): + if self.get_global('domain'): + await service.execute('dnsrecon -n {address} -d ' + self.get_global('domain') + ' 2>&1', outfile='{protocol}_{port}_dnsrecon_default.txt') + else: + service.error('A domain name was not specified in the command line options (--global.domain). If you know the domain name, look in the _manual_commands.txt file for the dnsrecon command.') diff --git a/autorecon/default-plugins/enum4linux.py b/autorecon/default-plugins/enum4linux.py new file mode 100644 index 0000000..9ca0a57 --- /dev/null +++ b/autorecon/default-plugins/enum4linux.py @@ -0,0 +1,18 @@ +from autorecon.plugins import ServiceScan + +class Enum4Linux(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Enum4Linux" + self.tags = ['default', 'safe', 'active-directory'] + + def configure(self): + self.match_service_name(['^ldap', '^smb', '^microsoft\-ds', '^netbios']) + self.match_port('tcp', [139, 389, 445]) + self.match_port('udp', 137) + self.run_once(True) + + async def run(self, service): + if service.target.ipversion == 'IPv4': + await service.execute('enum4linux -a -M -l -d {address} 2>&1', outfile='enum4linux.txt') diff --git a/autorecon/default-plugins/get-arch.py b/autorecon/default-plugins/get-arch.py new file mode 100644 index 0000000..0038859 --- /dev/null +++ b/autorecon/default-plugins/get-arch.py @@ -0,0 +1,16 @@ +from autorecon.plugins import ServiceScan + +class GetArch(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'get-arch' + self.tags = ['default', 'safe', 'rpc'] + + def configure(self): + self.match_service_name(['^msrpc']) + self.match_port('tcp', 135) + self.add_pattern(' is ((32|64)-bit)', description='Identified Architecture: {match1}') + + async def run(self, service): + await service.execute('getArch.py -target {address}', outfile='{protocol}_{port}_rpc_architecture.txt') diff --git a/autorecon/default-plugins/http_server.py b/autorecon/default-plugins/http_server.py deleted file mode 100644 index fefc8d1..0000000 --- a/autorecon/default-plugins/http_server.py +++ /dev/null @@ -1,282 +0,0 @@ -from autorecon.plugins import ServiceScan -from autorecon.io import fformat -from autorecon.config import config -from shutil import which -import os - -class NmapHTTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap HTTP" - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - self.add_pattern('Server: ([^\n]+)', description='Identified HTTP Server: {match1}') - self.add_pattern('WebDAV is ENABLED', description='WebDAV is enabled') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN "{scandir}/{protocol}_{port}_{http_scheme}_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_{http_scheme}_nmap.xml" {address}') - -class BruteforceHTTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Bruteforce HTTP" - self.tags = ['default', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - def manual(self, service, plugin_was_run): - service.add_manual_commands('Credential bruteforcing commands (don\'t run these without modifying them):', [ - 'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{http_scheme}_auth_hydra.txt" {http_scheme}-get://{addressv6}/path/to/auth/area', - 'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{http_scheme}_auth_medusa.txt" -M http -h {addressv6} -m DIR:/path/to/auth/area', - 'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{http_scheme}_form_hydra.txt" {http_scheme}-post-form://{addressv6}/path/to/login.php:"username=^USER^&password=^PASS^":"invalid-login-message"', - 'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{http_scheme}_form_medusa.txt" -M web-form -h {addressv6} -m FORM:/path/to/login.php -m FORM-DATA:"post?username=&password=" -m DENY-SIGNAL:"invalid login message"' - ]) - -class Curl(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Curl" - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.add_option("path", default="/", help="The path on the web server to curl. Default: %(default)s") - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - self.add_pattern('(?i)powered[ -]by[^\n]+') - - async def run(self, service): - if service.protocol == 'tcp': - await service.execute('curl -sSik {http_scheme}://{addressv6}:{port}' + self.get_option('path'), outfile='{protocol}_{port}_{http_scheme}_curl.html') - -class CurlRobots(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Curl Robots" - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - async def run(self, service): - if service.protocol == 'tcp': - process, stdout, _ = await service.execute('curl -sSikf {http_scheme}://{addressv6}:{port}/robots.txt', future_outfile='{protocol}_{port}_{http_scheme}_curl-robots.txt') - - lines = await stdout.readlines() - - if process.returncode == 0 and lines: - filename = fformat('{scandir}/{protocol}_{port}_{http_scheme}_curl-robots.txt') - with open(filename, mode='wt', encoding='utf8') as robots: - robots.write('\n'.join(lines)) - else: - service.info('{bblue}[' + fformat('{tag}') + ']{rst} There did not appear to be a robots.txt file in the webroot (/).') - -class CurlKnownSecurity(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Known Security" - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - async def run(self, service): - if service.protocol == 'tcp': - process, stdout, _ = await service.execute('curl -sSikf {http_scheme}://{addressv6}:{port}/.well-known/security.txt', future_outfile='{protocol}_{port}_{http_scheme}_known-security.txt') - - lines = await stdout.readlines() - - if process.returncode == 0 and lines: - filename = fformat('{scandir}/{protocol}_{port}_{http_scheme}_known-security.txt') - with open(filename, mode='wt', encoding='utf8') as robots: - robots.write('\n'.join(lines)) - else: - service.info('{bblue}[' + fformat('{tag}') + ']{rst} There did not appear to be a .well-known/security.txt file in the webroot (/).') - - -class DirBuster(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Directory Buster" - self.slug = 'dirbuster' - self.priority = 0 - self.tags = ['default', 'safe', 'long', 'http'] - - def configure(self): - self.add_choice_option('tool', default='feroxbuster', choices=['feroxbuster', 'gobuster', 'dirsearch', 'ffuf', 'dirb'], help='The tool to use for directory busting. Default: %(default)s') - self.add_list_option('wordlist', default=[os.path.join(config['config_dir'], 'wordlists', 'dirbuster.txt')], help='The wordlist(s) to use when directory busting. Separate multiple wordlists with spaces. Default: %(default)s') - self.add_option('threads', default=10, help='The number of threads to use when directory busting. Default: %(default)s') - self.add_option('ext', default='txt,html,php,asp,aspx,jsp', help='The extensions you wish to fuzz (no dot, comma separated). Default: %(default)s') - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - def check(self): - tool = self.get_option('tool') - if tool == 'feroxbuster': - if which('feroxbuster') is None: - self.error('The feroxbuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install feroxbuster)') - elif tool == 'gobuster': - if which('gobuster') is None: - self.error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)') - elif tool == 'dirsearch': - if which('dirsearch') is None: - self.error('The dirsearch program could not be found. Make sure it is installed. (On Kali, run: sudo apt install dirsearch)') - - async def run(self, service): - dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')]) - for wordlist in self.get_option('wordlist'): - name = os.path.splitext(os.path.basename(wordlist))[0] - if self.get_option('tool') == 'feroxbuster': - await service.execute('feroxbuster -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -x "' + self.get_option('ext') + '" -v -k -n -q -e -o "{scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_' + name + '.txt"') - elif self.get_option('tool') == 'gobuster': - await service.execute('gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e -k -x "' + self.get_option('ext') + '" -z -d -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_' + name + '.txt"') - elif self.get_option('tool') == 'dirsearch': - if service.target.ipversion == 'IPv6': - service.error('dirsearch does not support IPv6.') - else: - await service.execute('dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f -q -w ' + wordlist + ' --format=plain -o "{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_' + name + '.txt"') - elif self.get_option('tool') == 'ffuf': - await service.execute('ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e "' + dot_extensions + '" -v -noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_' + name + '.txt') - elif self.get_option('tool') == 'dirb': - await service.execute('dirb {http_scheme}://{addressv6}:{port}/ ' + wordlist + ' -l -r -S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_' + name + '.txt"') - - def manual(self, service, plugin_was_run): - dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')]) - if self.get_option('tool') == 'feroxbuster': - service.add_manual_command('(feroxbuster) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ - 'feroxbuster -u {http_scheme}://{addressv6}:{port} -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x "' + self.get_option('ext') + '" -v -k -n -o {scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_dirbuster.txt' - ]) - elif self.get_option('tool') == 'gobuster': - service.add_manual_command('(gobuster v3) Multi-threaded directory/file enumeration for web servers using various wordlists:', [ - 'gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -x "' + self.get_option('ext') + '" -z -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_dirbuster.txt"' - ]) - elif self.get_option('tool') == 'dirsearch': - if service.target.ipversion == 'IPv4': - service.add_manual_command('(dirsearch) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ - 'dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -r -e "' + self.get_option('ext') + '" -f -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --format=plain --output="{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_dirbuster.txt"' - ]) - elif self.get_option('tool') == 'ffuf': - service.add_manual_command('(ffuf) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ - 'ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e "' + dot_extensions + '" -v -noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_dirbuster.txt' - ]) - elif self.get_option('tool') == 'dirb': - service.add_manual_command('(dirb) Recursive directory/file enumeration for web servers using various wordlists:', [ - 'dirb {http_scheme}://{addressv6}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l -r -S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_dirbuster.txt"' - ]) - -class Nikto(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'nikto' - self.tags = ['default', 'safe', 'long', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - def manual(self, service, plugin_was_run): - if service.target.ipversion == 'IPv4': - service.add_manual_command('(nikto) old but generally reliable web server enumeration tool:', 'nikto -ask=no -h {http_scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{http_scheme}_nikto.txt"') - -class VirtualHost(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Virtual Host Enumeration' - self.slug = 'vhost-enum' - self.tags = ['default', 'safe', 'http', 'long'] - - def configure(self): - self.add_option('hostname', help='The hostname to use as the base host (e.g. example.com) for virtual host enumeration. Default: %(default)s') - self.add_list_option('wordlist', default=['/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt'], help='The wordlist(s) to use when enumerating virtual hosts. Separate multiple wordlists with spaces. Default: %(default)s') - self.add_option('threads', default=10, help='The number of threads to use when enumerating virtual hosts. Default: %(default)s') - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - def check(self): - if which('gobuster') is None: - self.error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)') - - async def run(self, service): - hostnames = [] - if self.get_option('hostname'): - hostnames.append(self.get_option('hostname')) - if service.target.type == 'hostname' and service.target.address not in hostnames: - hostnames.append(service.target.address) - if self.get_global('domain') and self.get_global('domain') not in hostnames: - hostnames.append(self.get_global('domain')) - - if len(hostnames) > 0: - for wordlist in self.get_option('wordlist'): - name = os.path.splitext(os.path.basename(wordlist))[0] - for hostname in hostnames: - await service.execute('gobuster vhost -k -u {http_scheme}://' + hostname + ':{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -r -o "{scandir}/{protocol}_{port}_{http_scheme}_' + hostname + '_vhosts_' + name + '.txt"') - else: - service.info('The target was not a hostname, nor was a hostname provided as an option. Skipping virtual host enumeration.') - -class WhatWeb(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "whatweb" - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - async def run(self, service): - if service.protocol == 'tcp' and service.target.ipversion == 'IPv4': - await service.execute('whatweb --color=never --no-errors -a 3 -v {http_scheme}://{address}:{port} 2>&1', outfile='{protocol}_{port}_{http_scheme}_whatweb.txt') - -class WkHTMLToImage(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "wkhtmltoimage" - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - def check(self): - if which('wkhtmltoimage') is None: - self.error('The wkhtmltoimage program could not be found. Make sure it is installed. (On Kali, run: sudo apt install wkhtmltopdf)') - - async def run(self, service): - if which('wkhtmltoimage') is not None: - if service.protocol == 'tcp': - await service.execute('wkhtmltoimage --format png {http_scheme}://{addressv6}:{port}/ {scandir}/{protocol}_{port}_{http_scheme}_screenshot.png') - -class WPScan(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'WPScan' - self.tags = ['default', 'safe', 'http'] - - def configure(self): - self.add_option('api-token', help='An API Token from wpvulndb.com to help search for more vulnerabilities.') - self.match_service_name('^http') - self.match_service_name('^nacn_http$', negative_match=True) - - def manual(self, service, plugin_was_run): - api_token = '' - if self.get_option('api-token'): - api_token = ' --api-token ' + self.get_option('api-token') - - service.add_manual_command('(wpscan) WordPress Security Scanner (useful if WordPress is found):', 'wpscan --url {http_scheme}://{addressv6}:{port}/ --no-update -e vp,vt,tt,cb,dbe,u,m --plugins-detection aggressive --plugins-version-detection aggressive -f cli-no-color' + api_token + ' 2>&1 | tee "{scandir}/{protocol}_{port}_{http_scheme}_wpscan.txt"') diff --git a/autorecon/default-plugins/ldap.py b/autorecon/default-plugins/ldap-search.py similarity index 53% rename from autorecon/default-plugins/ldap.py rename to autorecon/default-plugins/ldap-search.py index 2254fbf..c074d46 100644 --- a/autorecon/default-plugins/ldap.py +++ b/autorecon/default-plugins/ldap-search.py @@ -1,18 +1,5 @@ from autorecon.plugins import ServiceScan -class NmapLDAP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap LDAP" - self.tags = ['default', 'safe', 'ldap', 'active-directory'] - - def configure(self): - self.match_service_name('^ldap') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ldap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ldap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ldap_nmap.xml" {address}') - class LDAPSearch(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/lookup-sid.py b/autorecon/default-plugins/lookup-sid.py new file mode 100644 index 0000000..7136262 --- /dev/null +++ b/autorecon/default-plugins/lookup-sid.py @@ -0,0 +1,16 @@ +from autorecon.plugins import ServiceScan + +class LookupSID(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'lookupsid' + self.tags = ['default', 'safe', 'active-directory'] + + def configure(self): + self.match_service('tcp', 445, '^microsoft\-ds') + + def manual(self, service, plugin_was_run): + service.add_manual_command('Lookup SIDs', [ + 'lookupsid.py [username]:[password]@{address}' + ]) diff --git a/autorecon/default-plugins/misc.py b/autorecon/default-plugins/misc.py deleted file mode 100644 index f848f75..0000000 --- a/autorecon/default-plugins/misc.py +++ /dev/null @@ -1,201 +0,0 @@ -from autorecon.plugins import ServiceScan -from autorecon.io import fformat - -class NmapCassandra(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap Cassandra" - self.tags = ['default', 'safe', 'cassandra'] - - def configure(self): - self.match_service_name('^apani1') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(cassandra* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cassandra_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cassandra_nmap.xml" {address}') - -class NmapCUPS(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap CUPS" - self.tags = ['default', 'safe', 'cups'] - - def configure(self): - self.match_service_name('^ipp') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(cups* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cups_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cups_nmap.xml" {address}') - -class NmapDistccd(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap distccd" - self.tags = ['default', 'safe', 'distccd'] - - def configure(self): - self.match_service_name('^distccd') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,distcc-cve2004-2687" --script-args="distcc-cve2004-2687.cmd=id" -oN "{scandir}/{protocol}_{port}_distcc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_distcc_nmap.xml" {address}') - -class NmapFinger(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap finger" - self.tags = ['default', 'safe', 'finger'] - - def configure(self): - self.match_service_name('^finger') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,finger" -oN "{scandir}/{protocol}_{port}_finger_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_finger_nmap.xml" {address}') - -class NmapIMAP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap IMAP" - self.tags = ['default', 'safe', 'imap', 'email'] - - def configure(self): - self.match_service_name('^imap') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(imap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_imap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_imap_nmap.xml" {address}') - -class NmapIrc(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap IRC' - self.tags = ['default', 'safe', 'irc'] - - def configure(self): - self.match_service_name('^irc') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV --script irc-botnet-channels,irc-info,irc-unrealircd-backdoor -oN "{scandir}/{protocol}_{port}_irc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_irc_nmap.xml" -p {port} {address}') - -class NmapNNTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap NNTP" - self.tags = ['default', 'safe', 'nntp'] - - def configure(self): - self.match_service_name('^nntp') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,nntp-ntlm-info" -oN "{scandir}/{protocol}_{port}_nntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nntp_nmap.xml" {address}') - -class NmapNTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap NTP" - self.tags = ['default', 'safe', 'ntp'] - - def configure(self): - self.match_service_name('^ntp') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ntp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ntp_nmap.xml" {address}') - -class NmapPOP3(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap POP3" - self.tags = ['default', 'safe', 'pop3', 'email'] - - def configure(self): - self.match_service_name('^pop3') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(pop3* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_pop3_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_pop3_nmap.xml" {address}') - -class NmapRMI(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap RMI" - self.tags = ['default', 'safe', 'rmi'] - - def configure(self): - self.match_service_name(['^java\-rmi', '^rmiregistry']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,rmi-vuln-classloader,rmi-dumpregistry" -oN "{scandir}/{protocol}_{port}_rmi_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rmi_nmap.xml" {address}') - -class NmapTelnet(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap Telnet' - self.tags = ['default', 'safe', 'telnet'] - - def configure(self): - self.match_service_name('^telnet') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,telnet-encryption,telnet-ntlm-info" -oN "{scandir}/{protocol}_{port}_telnet-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_telnet_nmap.xml" {address}') - -class NmapTFTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap TFTP' - self.tags = ['default', 'safe', 'tftp'] - - def configure(self): - self.match_service_name('^tftp') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,tftp-enum" -oN "{scandir}/{protocol}_{port}_tftp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_tftp_nmap.xml" {address}') - -class NmapVNC(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap VNC' - self.tags = ['default', 'safe', 'vnc'] - - def configure(self): - self.match_service_name('^vnc') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(vnc* or realvnc* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_vnc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_vnc_nmap.xml" {address}') - -class WinRMDetection(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'WinRM Detection' - self.tags = ['default', 'safe', 'winrm'] - - def configure(self): - self.match_service_name('^wsman') - self.match_service('tcp', [5985, 5986], '^http') - - async def run(self, service): - filename = fformat('{scandir}/{protocol}_{port}_winrm-detection.txt') - with open(filename, mode='wt', encoding='utf8') as winrm: - winrm.write('WinRM was possibly detected running on ' + service.protocol + ' port ' + str(service.port) + '.\nCheck _manual_commands.txt for manual commands you can run against this service.') - - def manual(self, service, plugin_was_run): - service.add_manual_commands('Bruteforce logins:', [ - 'crackmapexec winrm {address} -d ' + self.get_global('domain', default='') + ' -u ' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + ' -p ' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') - ]) - - service.add_manual_commands('Check login (requires credentials):', [ - 'crackmapexec winrm {address} -d ' + self.get_global('domain', default='') + ' -u -p -x "whoami"' - ]) - - service.add_manual_commands('Evil WinRM (gem install evil-winrm):', [ - 'evil-winrm -u -p -i {address}', - 'evil-winrm -u -H -i {address}' - ]) diff --git a/autorecon/default-plugins/nbtscan.py b/autorecon/default-plugins/nbtscan.py new file mode 100644 index 0000000..3438a03 --- /dev/null +++ b/autorecon/default-plugins/nbtscan.py @@ -0,0 +1,17 @@ +from autorecon.plugins import ServiceScan + +class NBTScan(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'nbtscan' + self.tags = ['default', 'safe', 'netbios', 'active-directory'] + + def configure(self): + self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) + self.match_port('udp', 137) + self.run_once(True) + + async def run(self, service): + if service.target.ipversion == 'IPv4': + await service.execute('nbtscan -rvh {address} 2>&1', outfile='nbtscan.txt') diff --git a/autorecon/default-plugins/nfs.py b/autorecon/default-plugins/nfs.py deleted file mode 100644 index 515a2ec..0000000 --- a/autorecon/default-plugins/nfs.py +++ /dev/null @@ -1,40 +0,0 @@ -from autorecon.plugins import ServiceScan - -class NmapNFS(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap NFS" - self.tags = ['default', 'safe', 'nfs'] - - def configure(self): - self.match_service_name(['^nfs', '^rpcbind']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rpcinfo or nfs*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_nfs_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nfs_nmap.xml" {address}') - -class Showmount(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "showmount" - self.tags = ['default', 'safe', 'nfs'] - - def configure(self): - self.match_service_name(['^nfs', '^rpcbind']) - - async def run(self, service): - await service.execute('showmount -e {address} 2>&1', outfile='{protocol}_{port}_showmount.txt') - -class NmapMountd(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap Mountd" - self.tags = ['default', 'safe', 'nfs'] - - def configure(self): - self.match_service_name('^mountd') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,nfs* and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mountd_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mountd_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nikto.py b/autorecon/default-plugins/nikto.py new file mode 100644 index 0000000..b696cb5 --- /dev/null +++ b/autorecon/default-plugins/nikto.py @@ -0,0 +1,16 @@ +from autorecon.plugins import ServiceScan + +class Nikto(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'nikto' + self.tags = ['default', 'safe', 'long', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + def manual(self, service, plugin_was_run): + if service.target.ipversion == 'IPv4': + service.add_manual_command('(nikto) old but generally reliable web server enumeration tool:', 'nikto -ask=no -h {http_scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{http_scheme}_nikto.txt"') diff --git a/autorecon/default-plugins/nmap-cassandra.py b/autorecon/default-plugins/nmap-cassandra.py new file mode 100644 index 0000000..54778a3 --- /dev/null +++ b/autorecon/default-plugins/nmap-cassandra.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapCassandra(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap Cassandra" + self.tags = ['default', 'safe', 'cassandra'] + + def configure(self): + self.match_service_name('^apani1') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(cassandra* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cassandra_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cassandra_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-cups.py b/autorecon/default-plugins/nmap-cups.py new file mode 100644 index 0000000..d8d532f --- /dev/null +++ b/autorecon/default-plugins/nmap-cups.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapCUPS(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap CUPS" + self.tags = ['default', 'safe', 'cups'] + + def configure(self): + self.match_service_name('^ipp') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(cups* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cups_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cups_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-distccd.py b/autorecon/default-plugins/nmap-distccd.py new file mode 100644 index 0000000..ecf99c0 --- /dev/null +++ b/autorecon/default-plugins/nmap-distccd.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapDistccd(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap distccd" + self.tags = ['default', 'safe', 'distccd'] + + def configure(self): + self.match_service_name('^distccd') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,distcc-cve2004-2687" --script-args="distcc-cve2004-2687.cmd=id" -oN "{scandir}/{protocol}_{port}_distcc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_distcc_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-dns.py b/autorecon/default-plugins/nmap-dns.py new file mode 100644 index 0000000..479ac42 --- /dev/null +++ b/autorecon/default-plugins/nmap-dns.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapDNS(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap DNS' + self.tags = ['default', 'safe', 'dns'] + + def configure(self): + self.match_service_name('^domain') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_dns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_dns_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-finger.py b/autorecon/default-plugins/nmap-finger.py new file mode 100644 index 0000000..2f8d38c --- /dev/null +++ b/autorecon/default-plugins/nmap-finger.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapFinger(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap finger" + self.tags = ['default', 'safe', 'finger'] + + def configure(self): + self.match_service_name('^finger') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,finger" -oN "{scandir}/{protocol}_{port}_finger_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_finger_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-ftp.py b/autorecon/default-plugins/nmap-ftp.py new file mode 100644 index 0000000..ea87573 --- /dev/null +++ b/autorecon/default-plugins/nmap-ftp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapFTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap FTP' + self.tags = ['default', 'safe', 'ftp'] + + def configure(self): + self.match_service_name(['^ftp', '^ftp\-data']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(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}') diff --git a/autorecon/default-plugins/nmap-http.py b/autorecon/default-plugins/nmap-http.py new file mode 100644 index 0000000..0dbeb69 --- /dev/null +++ b/autorecon/default-plugins/nmap-http.py @@ -0,0 +1,17 @@ +from autorecon.plugins import ServiceScan + +class NmapHTTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap HTTP" + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + self.add_pattern('Server: ([^\n]+)', description='Identified HTTP Server: {match1}') + self.add_pattern('WebDAV is ENABLED', description='WebDAV is enabled') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN "{scandir}/{protocol}_{port}_{http_scheme}_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_{http_scheme}_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-imap.py b/autorecon/default-plugins/nmap-imap.py new file mode 100644 index 0000000..95d1169 --- /dev/null +++ b/autorecon/default-plugins/nmap-imap.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapIMAP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap IMAP" + self.tags = ['default', 'safe', 'imap', 'email'] + + def configure(self): + self.match_service_name('^imap') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(imap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_imap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_imap_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-irc.py b/autorecon/default-plugins/nmap-irc.py new file mode 100644 index 0000000..ebfbf45 --- /dev/null +++ b/autorecon/default-plugins/nmap-irc.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapIrc(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap IRC' + self.tags = ['default', 'safe', 'irc'] + + def configure(self): + self.match_service_name('^irc') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV --script irc-botnet-channels,irc-info,irc-unrealircd-backdoor -oN "{scandir}/{protocol}_{port}_irc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_irc_nmap.xml" -p {port} {address}') diff --git a/autorecon/default-plugins/kerberos.py b/autorecon/default-plugins/nmap-kerberos.py similarity index 100% rename from autorecon/default-plugins/kerberos.py rename to autorecon/default-plugins/nmap-kerberos.py diff --git a/autorecon/default-plugins/nmap-ldap.py b/autorecon/default-plugins/nmap-ldap.py new file mode 100644 index 0000000..77aab20 --- /dev/null +++ b/autorecon/default-plugins/nmap-ldap.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapLDAP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap LDAP" + self.tags = ['default', 'safe', 'ldap', 'active-directory'] + + def configure(self): + self.match_service_name('^ldap') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ldap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ldap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ldap_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-mongodb.py b/autorecon/default-plugins/nmap-mongodb.py new file mode 100644 index 0000000..833c628 --- /dev/null +++ b/autorecon/default-plugins/nmap-mongodb.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapMongoDB(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap MongoDB" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name('^mongod') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(mongodb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mongodb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mongodb_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-mountd.py b/autorecon/default-plugins/nmap-mountd.py new file mode 100644 index 0000000..848a77a --- /dev/null +++ b/autorecon/default-plugins/nmap-mountd.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapMountd(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap Mountd" + self.tags = ['default', 'safe', 'nfs'] + + def configure(self): + self.match_service_name('^mountd') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,nfs* and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mountd_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mountd_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-msrpc.py b/autorecon/default-plugins/nmap-msrpc.py new file mode 100644 index 0000000..e374412 --- /dev/null +++ b/autorecon/default-plugins/nmap-msrpc.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapRPC(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap MSRPC" + self.tags = ['default', 'rpc'] + + def configure(self): + self.match_service_name(['^msrpc', '^rpcbind', '^erpc']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,msrpc-enum,rpc-grind,rpcinfo" -oN "{scandir}/{protocol}_{port}_rpc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rpc_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-mssql.py b/autorecon/default-plugins/nmap-mssql.py new file mode 100644 index 0000000..6e55d45 --- /dev/null +++ b/autorecon/default-plugins/nmap-mssql.py @@ -0,0 +1,18 @@ +from autorecon.plugins import ServiceScan + +class NmapMSSQL(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap MSSQL" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name(['^mssql', '^ms\-sql']) + + def manual(self, service, plugin_was_run): + if service.target.ipversion == 'IPv4': + service.add_manual_command('(sqsh) interactive database shell:', 'sqsh -U -P -S {address}:{port}') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ms-sql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="mssql.instance-port={port},mssql.username=sa,mssql.password=sa" -oN "{scandir}/{protocol}_{port}_mssql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mssql_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-multicast-dns.py b/autorecon/default-plugins/nmap-multicast-dns.py new file mode 100644 index 0000000..1edb391 --- /dev/null +++ b/autorecon/default-plugins/nmap-multicast-dns.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapMulticastDNS(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap Multicast DNS' + self.tags = ['default', 'safe', 'dns'] + + def configure(self): + self.match_service_name(['^mdns', '^zeroconf']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_multicastdns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_multicastdns_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-mysql.py b/autorecon/default-plugins/nmap-mysql.py new file mode 100644 index 0000000..68a01d5 --- /dev/null +++ b/autorecon/default-plugins/nmap-mysql.py @@ -0,0 +1,18 @@ +from autorecon.plugins import ServiceScan + +class NmapMYSQL(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap MYSQL" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name('^mysql') + + def manual(self, service, plugin_was_run): + if service.target.ipversion == 'IPv4': + service.add_manual_command('(sqsh) interactive database shell:', 'sqsh -U -P -S {address}:{port}') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(mysql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mysql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mysql_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-nfs.py b/autorecon/default-plugins/nmap-nfs.py new file mode 100644 index 0000000..b8fabce --- /dev/null +++ b/autorecon/default-plugins/nmap-nfs.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapNFS(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap NFS" + self.tags = ['default', 'safe', 'nfs'] + + def configure(self): + self.match_service_name(['^nfs', '^rpcbind']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rpcinfo or nfs*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_nfs_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nfs_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-nntp.py b/autorecon/default-plugins/nmap-nntp.py new file mode 100644 index 0000000..1c7db5b --- /dev/null +++ b/autorecon/default-plugins/nmap-nntp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapNNTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap NNTP" + self.tags = ['default', 'safe', 'nntp'] + + def configure(self): + self.match_service_name('^nntp') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,nntp-ntlm-info" -oN "{scandir}/{protocol}_{port}_nntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nntp_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-ntp.py b/autorecon/default-plugins/nmap-ntp.py new file mode 100644 index 0000000..d04679e --- /dev/null +++ b/autorecon/default-plugins/nmap-ntp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapNTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap NTP" + self.tags = ['default', 'safe', 'ntp'] + + def configure(self): + self.match_service_name('^ntp') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ntp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ntp_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-oracle.py b/autorecon/default-plugins/nmap-oracle.py new file mode 100644 index 0000000..d8ff6bd --- /dev/null +++ b/autorecon/default-plugins/nmap-oracle.py @@ -0,0 +1,17 @@ +from autorecon.plugins import ServiceScan + +class NmapOracle(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap Oracle" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name('^oracle') + + def manual(self, service, plugin_was_run): + service.add_manual_command('Brute-force SIDs using Nmap:', 'nmap {nmap_extra} -sV -p {port} --script="banner,oracle-sid-brute" -oN "{scandir}/{protocol}_{port}_oracle_sid-brute_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_sid-brute_nmap.xml" {address}') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(oracle* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_oracle_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-pop3.py b/autorecon/default-plugins/nmap-pop3.py new file mode 100644 index 0000000..f4ed3a6 --- /dev/null +++ b/autorecon/default-plugins/nmap-pop3.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapPOP3(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap POP3" + self.tags = ['default', 'safe', 'pop3', 'email'] + + def configure(self): + self.match_service_name('^pop3') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(pop3* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_pop3_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_pop3_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-rdp.py b/autorecon/default-plugins/nmap-rdp.py new file mode 100644 index 0000000..13a16f6 --- /dev/null +++ b/autorecon/default-plugins/nmap-rdp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapRDP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap RDP" + self.tags = ['default', 'safe', 'rdp'] + + def configure(self): + self.match_service_name(['^rdp', '^ms\-wbt\-server', '^ms\-term\-serv']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rdp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rdp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rdp_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-redis.py b/autorecon/default-plugins/nmap-redis.py new file mode 100644 index 0000000..46fa920 --- /dev/null +++ b/autorecon/default-plugins/nmap-redis.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapRedis(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap Redis' + self.tags = ['default', 'safe', 'redis'] + + def configure(self): + self.match_service_name('^redis$') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,redis-info" -oN "{scandir}/{protocol}_{port}_redis_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_redis_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-rmi.py b/autorecon/default-plugins/nmap-rmi.py new file mode 100644 index 0000000..7c1d277 --- /dev/null +++ b/autorecon/default-plugins/nmap-rmi.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapRMI(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap RMI" + self.tags = ['default', 'safe', 'rmi'] + + def configure(self): + self.match_service_name(['^java\-rmi', '^rmiregistry']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,rmi-vuln-classloader,rmi-dumpregistry" -oN "{scandir}/{protocol}_{port}_rmi_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rmi_nmap.xml" {address}') diff --git a/autorecon/default-plugins/rsync.py b/autorecon/default-plugins/nmap-rsync.py similarity index 59% rename from autorecon/default-plugins/rsync.py rename to autorecon/default-plugins/nmap-rsync.py index da9a4be..3e8251d 100644 --- a/autorecon/default-plugins/rsync.py +++ b/autorecon/default-plugins/nmap-rsync.py @@ -12,16 +12,3 @@ class NmapRsync(ServiceScan): async def run(self, service): await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rsync* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rsync_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rsync_nmap.xml" {address}') - -class RsyncList(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Rsync List Files' - self.tags = ['default', 'safe', 'rsync'] - - def configure(self): - self.match_service_name('^rsync') - - async def run(self, service): - await service.execute('rsync -av --list-only rsync://{addressv6}:{port}', outfile='{protocol}_{port}_rsync_file_list.txt') diff --git a/autorecon/default-plugins/sip.py b/autorecon/default-plugins/nmap-sip.py similarity index 56% rename from autorecon/default-plugins/sip.py rename to autorecon/default-plugins/nmap-sip.py index 878f9e4..837b059 100644 --- a/autorecon/default-plugins/sip.py +++ b/autorecon/default-plugins/nmap-sip.py @@ -12,17 +12,3 @@ class NmapSIP(ServiceScan): async def run(self, service): await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,sip-enum-users,sip-methods" -oN "{scandir}/{protocol}_{port}_sip_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_sip_nmap.xml" {address}') - -class SIPVicious(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "SIPVicious" - self.tags = ['default', 'safe', 'sip'] - - def configure(self): - self.match_service_name(['^asterisk', '^sip']) - - def manual(self, service, plugin_was_run): - if service.target.ipversion == 'IPv4': - service.add_manual_command('svwar:', 'svwar -D -m INVITE -p {port} {address}') diff --git a/autorecon/default-plugins/nmap-smb.py b/autorecon/default-plugins/nmap-smb.py new file mode 100644 index 0000000..5f9bb52 --- /dev/null +++ b/autorecon/default-plugins/nmap-smb.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapSMB(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap SMB" + self.tags = ['default', 'safe', 'smb', 'active-directory'] + + def configure(self): + self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-smtp.py b/autorecon/default-plugins/nmap-smtp.py new file mode 100644 index 0000000..962d87c --- /dev/null +++ b/autorecon/default-plugins/nmap-smtp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapSMTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap SMTP" + self.tags = ['default', 'safe', 'smtp', 'email'] + + def configure(self): + self.match_service_name('^smtp') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(smtp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smtp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smtp_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-snmp.py b/autorecon/default-plugins/nmap-snmp.py new file mode 100644 index 0000000..29305f0 --- /dev/null +++ b/autorecon/default-plugins/nmap-snmp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapSNMP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap SNMP" + self.tags = ['default', 'safe', 'snmp'] + + def configure(self): + self.match_service_name('^snmp') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(snmp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_snmp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_snmp_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-ssh.py b/autorecon/default-plugins/nmap-ssh.py new file mode 100644 index 0000000..6c2acda --- /dev/null +++ b/autorecon/default-plugins/nmap-ssh.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapSSH(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Nmap SSH" + self.tags = ['default', 'safe', 'ssh'] + + def configure(self): + self.match_service_name('^ssh') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods" -oN "{scandir}/{protocol}_{port}_ssh_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ssh_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-telnet.py b/autorecon/default-plugins/nmap-telnet.py new file mode 100644 index 0000000..c989d82 --- /dev/null +++ b/autorecon/default-plugins/nmap-telnet.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapTelnet(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap Telnet' + self.tags = ['default', 'safe', 'telnet'] + + def configure(self): + self.match_service_name('^telnet') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,telnet-encryption,telnet-ntlm-info" -oN "{scandir}/{protocol}_{port}_telnet-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_telnet_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-tftp.py b/autorecon/default-plugins/nmap-tftp.py new file mode 100644 index 0000000..c0c0994 --- /dev/null +++ b/autorecon/default-plugins/nmap-tftp.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapTFTP(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap TFTP' + self.tags = ['default', 'safe', 'tftp'] + + def configure(self): + self.match_service_name('^tftp') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,tftp-enum" -oN "{scandir}/{protocol}_{port}_tftp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_tftp_nmap.xml" {address}') diff --git a/autorecon/default-plugins/nmap-vnc.py b/autorecon/default-plugins/nmap-vnc.py new file mode 100644 index 0000000..806a8ff --- /dev/null +++ b/autorecon/default-plugins/nmap-vnc.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class NmapVNC(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Nmap VNC' + self.tags = ['default', 'safe', 'vnc'] + + def configure(self): + self.match_service_name('^vnc') + + async def run(self, service): + await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(vnc* or realvnc* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_vnc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_vnc_nmap.xml" {address}') diff --git a/autorecon/default-plugins/onesixtyone.py b/autorecon/default-plugins/onesixtyone.py new file mode 100644 index 0000000..c388852 --- /dev/null +++ b/autorecon/default-plugins/onesixtyone.py @@ -0,0 +1,18 @@ +from autorecon.plugins import ServiceScan + +class OneSixtyOne(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "OneSixtyOne" + self.tags = ['default', 'safe', 'snmp'] + + def configure(self): + self.match_service_name('^snmp') + self.match_port('udp', 161) + self.run_once(True) + self.add_option('community-strings', default='/usr/share/seclists/Discovery/SNMP/common-snmp-community-strings-onesixtyone.txt', help='The file containing a list of community strings to try. Default: %(default)s') + + async def run(self, service): + if service.target.ipversion == 'IPv4': + await service.execute('onesixtyone -c ' + service.get_option('community-strings') + ' -dd {address} 2>&1', outfile='{protocol}_{port}_snmp_onesixtyone.txt') diff --git a/autorecon/default-plugins/oracle-odat.py b/autorecon/default-plugins/oracle-odat.py new file mode 100644 index 0000000..ac4613f --- /dev/null +++ b/autorecon/default-plugins/oracle-odat.py @@ -0,0 +1,21 @@ +from autorecon.plugins import ServiceScan + +class OracleODAT(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Oracle ODAT" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name('^oracle') + + def manual(self, service, plugin_was_run): + service.add_manual_commands('Install ODAT (https://github.com/quentinhardy/odat) and run the following commands:', [ + 'python odat.py tnscmd -s {address} -p {port} --ping', + 'python odat.py tnscmd -s {address} -p {port} --version', + 'python odat.py tnscmd -s {address} -p {port} --status', + 'python odat.py sidguesser -s {address} -p {port}', + 'python odat.py passwordguesser -s {address} -p {port} -d --accounts-file accounts/accounts_multiple.txt', + 'python odat.py tnspoison -s {address} -p {port} -d --test-module' + ]) diff --git a/autorecon/default-plugins/oracle-patator.py b/autorecon/default-plugins/oracle-patator.py new file mode 100644 index 0000000..594b531 --- /dev/null +++ b/autorecon/default-plugins/oracle-patator.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class OraclePatator(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Oracle Patator" + self.tags = ['default', 'databases'] + + def configure(self): + self.match_service_name('^oracle') + + def manual(self, service, plugin_was_run): + service.add_manual_command('Install Oracle Instant Client (https://github.com/rapid7/metasploit-framework/wiki/How-to-get-Oracle-Support-working-with-Kali-Linux) and then bruteforce with patator:', 'patator oracle_login host={address} port={port} user=COMBO00 password=COMBO01 0=/usr/share/seclists/Passwords/Default-Credentials/oracle-betterdefaultpasslist.txt -x ignore:code=ORA-01017 -x ignore:code=ORA-28000') diff --git a/autorecon/default-plugins/oracle-scanner.py b/autorecon/default-plugins/oracle-scanner.py new file mode 100644 index 0000000..c610420 --- /dev/null +++ b/autorecon/default-plugins/oracle-scanner.py @@ -0,0 +1,19 @@ +from autorecon.plugins import ServiceScan +from shutil import which + +class OracleScanner(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Oracle Scanner" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name('^oracle') + + def check(self): + if which('oscanner') is None: + self.error('The oscanner program could not be found. Make sure it is installed. (On Kali, run: sudo apt install oscanner)') + + async def run(self, service): + await service.execute('oscanner -v -s {address} -P {port} 2>&1', outfile='{protocol}_{port}_oracle_scanner.txt') diff --git a/autorecon/default-plugins/oracle-tnscmd.py b/autorecon/default-plugins/oracle-tnscmd.py new file mode 100644 index 0000000..b43ec12 --- /dev/null +++ b/autorecon/default-plugins/oracle-tnscmd.py @@ -0,0 +1,21 @@ +from autorecon.plugins import ServiceScan +from shutil import which + +class OracleTNScmd(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Oracle TNScmd" + self.tags = ['default', 'safe', 'databases'] + + def configure(self): + self.match_service_name('^oracle') + + def check(self): + if which('tnscmd10g') is None: + self.error('The tnscmd10g program could not be found. Make sure it is installed. (On Kali, run: sudo apt install tnscmd10g)') + + async def run(self, service): + if service.target.ipversion == 'IPv4': + await service.execute('tnscmd10g ping -h {address} -p {port} 2>&1', outfile='{protocol}_{port}_oracle_tnscmd_ping.txt') + await service.execute('tnscmd10g version -h {address} -p {port} 2>&1', outfile='{protocol}_{port}_oracle_tnscmd_version.txt') diff --git a/autorecon/default-plugins/portscan-all-tcp-ports.py b/autorecon/default-plugins/portscan-all-tcp-ports.py new file mode 100644 index 0000000..5904e09 --- /dev/null +++ b/autorecon/default-plugins/portscan-all-tcp-ports.py @@ -0,0 +1,41 @@ +from autorecon.plugins import PortScan +from autorecon.config import config +import re + +class AllTCPPortScan(PortScan): + + def __init__(self): + super().__init__() + self.name = 'All TCP Ports' + self.description = 'Performs an Nmap scan of all TCP ports.' + self.type = 'tcp' + self.specific_ports = True + self.tags = ['default', 'default-port-scan', 'long'] + + async def run(self, target): + if config['proxychains']: + traceroute_os = '' + else: + traceroute_os = ' -A --osscan-guess' + + if target.ports: + if target.ports['tcp']: + process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -p ' + target.ports['tcp'] + ' -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}', blocking=False) + else: + return [] + else: + process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -p- -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}', blocking=False) + services = [] + while True: + line = await stdout.readline() + if line is not None: + match = re.search('^Discovered open port ([0-9]+)/tcp', line) + if match: + target.info('Discovered open port {bmagenta}tcp/' + match.group(1) + '{rst} on {byellow}' + target.address + '{rst}', verbosity=1) + service = target.extract_service(line) + if service: + services.append(service) + else: + break + await process.wait() + return services diff --git a/autorecon/default-plugins/guess-port-scan.py b/autorecon/default-plugins/portscan-guess-tcp-ports.py similarity index 100% rename from autorecon/default-plugins/guess-port-scan.py rename to autorecon/default-plugins/portscan-guess-tcp-ports.py diff --git a/autorecon/default-plugins/portscan-top-100-udp-ports.py b/autorecon/default-plugins/portscan-top-100-udp-ports.py new file mode 100644 index 0000000..5003e8d --- /dev/null +++ b/autorecon/default-plugins/portscan-top-100-udp-ports.py @@ -0,0 +1,40 @@ +from autorecon.plugins import PortScan +from autorecon.config import config +import os, re + +class Top100UDPPortScan(PortScan): + + def __init__(self): + super().__init__() + self.name = 'Top 100 UDP Ports' + self.description = 'Performs an Nmap scan of the top 100 UDP ports.' + self.type = 'udp' + self.specific_ports = True + self.tags = ['default', 'default-port-scan', 'long'] + + async def run(self, target): + # Only run UDP scan if user is root. + if os.getuid() == 0: + if target.ports: + if target.ports['udp']: + process, stdout, stderr = await target.execute('nmap {nmap_extra} -sU -A --osscan-guess -p ' + target.ports['udp'] + ' -oN "{scandir}/_custom_ports_udp_nmap.txt" -oX "{scandir}/xml/_custom_ports_udp_nmap.xml" {address}', blocking=False) + else: + return [] + else: + process, stdout, stderr = await target.execute('nmap {nmap_extra} -sU -A --top-ports 100 -oN "{scandir}/_top_100_udp_nmap.txt" -oX "{scandir}/xml/_top_100_udp_nmap.xml" {address}', blocking=False) + services = [] + while True: + line = await stdout.readline() + if line is not None: + match = re.search('^Discovered open port ([0-9]+)/udp', line) + if match: + target.info('Discovered open port {bmagenta}udp/' + match.group(1) + '{rst} on {byellow}' + target.address + '{rst}', verbosity=1) + service = target.extract_service(line) + if service: + services.append(service) + else: + break + await process.wait() + return services + else: + target.error('UDP scan requires AutoRecon be run with root privileges.') diff --git a/autorecon/default-plugins/portscan-top-tcp-ports.py b/autorecon/default-plugins/portscan-top-tcp-ports.py new file mode 100644 index 0000000..18bad23 --- /dev/null +++ b/autorecon/default-plugins/portscan-top-tcp-ports.py @@ -0,0 +1,26 @@ +from autorecon.plugins import PortScan +from autorecon.config import config + +class QuickTCPPortScan(PortScan): + + def __init__(self): + super().__init__() + self.name = 'Top TCP Ports' + self.description = 'Performs an Nmap scan of the top 1000 TCP ports.' + self.type = 'tcp' + self.tags = ['default', 'default-port-scan'] + self.priority = 0 + + async def run(self, target): + if target.ports: # Don't run this plugin if there are custom ports. + return [] + + if config['proxychains']: + traceroute_os = '' + else: + traceroute_os = ' -A --osscan-guess' + + process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}', blocking=False) + services = await target.extract_services(stdout) + await process.wait() + return services diff --git a/autorecon/default-plugins/redis.py b/autorecon/default-plugins/redis-cli.py similarity index 69% rename from autorecon/default-plugins/redis.py rename to autorecon/default-plugins/redis-cli.py index 3be34ed..a6f6c79 100644 --- a/autorecon/default-plugins/redis.py +++ b/autorecon/default-plugins/redis-cli.py @@ -1,19 +1,6 @@ from autorecon.plugins import ServiceScan from shutil import which -class NmapRedis(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'Nmap Redis' - self.tags = ['default', 'safe', 'redis'] - - def configure(self): - self.match_service_name('^redis$') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,redis-info" -oN "{scandir}/{protocol}_{port}_redis_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_redis_nmap.xml" {address}') - class RedisCli(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/reporting.py b/autorecon/default-plugins/reporting-cherrytree.py similarity index 53% rename from autorecon/default-plugins/reporting.py rename to autorecon/default-plugins/reporting-cherrytree.py index f4fe429..bd1d4d8 100644 --- a/autorecon/default-plugins/reporting.py +++ b/autorecon/default-plugins/reporting-cherrytree.py @@ -89,75 +89,3 @@ 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['output'], '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```') diff --git a/autorecon/default-plugins/reporting-markdown.py b/autorecon/default-plugins/reporting-markdown.py new file mode 100644 index 0000000..bba0330 --- /dev/null +++ b/autorecon/default-plugins/reporting-markdown.py @@ -0,0 +1,75 @@ +from autorecon.plugins import Report +from autorecon.config import config +import os, glob + +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['output'], '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```') diff --git a/autorecon/default-plugins/rpc.py b/autorecon/default-plugins/rpc.py deleted file mode 100644 index fcf650f..0000000 --- a/autorecon/default-plugins/rpc.py +++ /dev/null @@ -1,57 +0,0 @@ -from autorecon.plugins import ServiceScan - -class NmapRPC(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap MSRPC" - self.tags = ['default', 'rpc'] - - def configure(self): - self.match_service_name(['^msrpc', '^rpcbind', '^erpc']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,msrpc-enum,rpc-grind,rpcinfo" -oN "{scandir}/{protocol}_{port}_rpc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rpc_nmap.xml" {address}') - -class RPCClient(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "rpcclient" - self.tags = ['default', 'safe', 'rpc'] - - def configure(self): - self.match_service_name(['^msrpc', '^rpcbind', '^erpc']) - - def manual(self, service, plugin_was_run): - service.add_manual_command('RPC Client:', 'rpcclient -p {port} -U "" {address}') - -class RPCDump(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'rpcdump' - self.tags = ['default', 'safe', 'rpc'] - - def configure(self): - self.match_service_name(['^msrpc', '^rpcbind', '^erpc', '^ncacn_http$']) - self.match_port('tcp', [135, 139, 443, 445, 593]) - - async def run(self, service): - if service.protocol == 'tcp': - await service.execute('impacket-rpcdump -port {port} {address}', outfile='{protocol}_{port}_rpc_rpcdump.txt') - -class GetArch(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'get-arch' - self.tags = ['default', 'safe', 'rpc'] - - def configure(self): - self.match_service_name(['^msrpc']) - self.match_port('tcp', 135) - self.add_pattern(' is ((32|64)-bit)', description='Identified Architecture: {match1}') - - async def run(self, service): - await service.execute('getArch.py -target {address}', outfile='{protocol}_{port}_rpc_architecture.txt') diff --git a/autorecon/default-plugins/rpcclient.py b/autorecon/default-plugins/rpcclient.py new file mode 100644 index 0000000..052b20a --- /dev/null +++ b/autorecon/default-plugins/rpcclient.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class RPCClient(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "rpcclient" + self.tags = ['default', 'safe', 'rpc'] + + def configure(self): + self.match_service_name(['^msrpc', '^rpcbind', '^erpc']) + + def manual(self, service, plugin_was_run): + service.add_manual_command('RPC Client:', 'rpcclient -p {port} -U "" {address}') diff --git a/autorecon/default-plugins/rpcdump.py b/autorecon/default-plugins/rpcdump.py new file mode 100644 index 0000000..51aa3e7 --- /dev/null +++ b/autorecon/default-plugins/rpcdump.py @@ -0,0 +1,16 @@ +from autorecon.plugins import ServiceScan + +class RPCDump(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'rpcdump' + self.tags = ['default', 'safe', 'rpc'] + + def configure(self): + self.match_service_name(['^msrpc', '^rpcbind', '^erpc', '^ncacn_http$']) + self.match_port('tcp', [135, 139, 443, 445, 593]) + + async def run(self, service): + if service.protocol == 'tcp': + await service.execute('impacket-rpcdump -port {port} {address}', outfile='{protocol}_{port}_rpc_rpcdump.txt') diff --git a/autorecon/default-plugins/rsync-list-files.py b/autorecon/default-plugins/rsync-list-files.py new file mode 100644 index 0000000..a058336 --- /dev/null +++ b/autorecon/default-plugins/rsync-list-files.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class RsyncList(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Rsync List Files' + self.tags = ['default', 'safe', 'rsync'] + + def configure(self): + self.match_service_name('^rsync') + + async def run(self, service): + await service.execute('rsync -av --list-only rsync://{addressv6}:{port}', outfile='{protocol}_{port}_rsync_file_list.txt') diff --git a/autorecon/default-plugins/showmount.py b/autorecon/default-plugins/showmount.py new file mode 100644 index 0000000..6b14951 --- /dev/null +++ b/autorecon/default-plugins/showmount.py @@ -0,0 +1,14 @@ +from autorecon.plugins import ServiceScan + +class Showmount(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "showmount" + self.tags = ['default', 'safe', 'nfs'] + + def configure(self): + self.match_service_name(['^nfs', '^rpcbind']) + + async def run(self, service): + await service.execute('showmount -e {address} 2>&1', outfile='{protocol}_{port}_showmount.txt') diff --git a/autorecon/default-plugins/sipvicious.py b/autorecon/default-plugins/sipvicious.py new file mode 100644 index 0000000..86db374 --- /dev/null +++ b/autorecon/default-plugins/sipvicious.py @@ -0,0 +1,15 @@ +from autorecon.plugins import ServiceScan + +class SIPVicious(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "SIPVicious" + self.tags = ['default', 'safe', 'sip'] + + def configure(self): + self.match_service_name(['^asterisk', '^sip']) + + def manual(self, service, plugin_was_run): + if service.target.ipversion == 'IPv4': + service.add_manual_command('svwar:', 'svwar -D -m INVITE -p {port} {address}') diff --git a/autorecon/default-plugins/smb-vuln.py b/autorecon/default-plugins/smb-vuln.py new file mode 100644 index 0000000..ab1c8d5 --- /dev/null +++ b/autorecon/default-plugins/smb-vuln.py @@ -0,0 +1,24 @@ +from autorecon.plugins import ServiceScan + +class SMBVuln(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "SMB Vulnerabilities" + self.tags = ['unsafe', 'smb', 'active-directory'] + + def configure(self): + self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) + + async def run(self, service): + await service.execute('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}') + await service.execute('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}') + await service.execute('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}') + + def manual(self, service, plugin_was_run): + if not plugin_was_run: # Only suggest these if they weren't run. + service.add_manual_commands('Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:', [ + '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/default-plugins/smb.py b/autorecon/default-plugins/smb.py deleted file mode 100644 index 438e712..0000000 --- a/autorecon/default-plugins/smb.py +++ /dev/null @@ -1,119 +0,0 @@ -from autorecon.plugins import ServiceScan - -class NmapSMB(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap SMB" - self.tags = ['default', 'safe', 'smb', 'active-directory'] - - def configure(self): - self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}') - -class SMBVuln(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "SMB Vulnerabilities" - self.tags = ['unsafe', 'smb', 'active-directory'] - - def configure(self): - self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) - - async def run(self, service): - await service.execute('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}') - await service.execute('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}') - await service.execute('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}') - - def manual(self, service, plugin_was_run): - if not plugin_was_run: # Only suggest these if they weren't run. - service.add_manual_commands('Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:', [ - '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}' - ]) - -class Enum4Linux(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Enum4Linux" - self.tags = ['default', 'safe', 'active-directory'] - - def configure(self): - self.match_service_name(['^ldap', '^smb', '^microsoft\-ds', '^netbios']) - self.match_port('tcp', [139, 389, 445]) - self.match_port('udp', 137) - self.run_once(True) - - async def run(self, service): - if service.target.ipversion == 'IPv4': - await service.execute('enum4linux -a -M -l -d {address} 2>&1', outfile='enum4linux.txt') - -class LookupSID(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'lookupsid' - self.tags = ['default', 'safe', 'active-directory'] - - def configure(self): - self.match_service('tcp', 445, '^microsoft\-ds') - - def manual(self, service, plugin_was_run): - service.add_manual_command('Lookup SIDs', [ - 'lookupsid.py [username]:[password]@{address}' - ]) - -class NBTScan(ServiceScan): - - def __init__(self): - super().__init__() - self.name = 'nbtscan' - self.tags = ['default', 'safe', 'netbios', 'active-directory'] - - def configure(self): - self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) - self.match_port('udp', 137) - self.run_once(True) - - async def run(self, service): - if service.target.ipversion == 'IPv4': - await service.execute('nbtscan -rvh {address} 2>&1', outfile='nbtscan.txt') - -class SMBClient(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "SMBClient" - self.tags = ['default', 'safe', 'smb', 'active-directory'] - - def configure(self): - self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) - self.match_port('tcp', [139, 445]) - self.run_once(True) - - async def run(self, service): - await service.execute('smbclient -L //{address} -N -I {address} 2>&1', outfile='smbclient.txt') - -class SMBMap(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "SMBMap" - self.tags = ['default', 'safe', 'smb', 'active-directory'] - - def configure(self): - self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) - - async def run(self, service): - if service.target.ipversion == 'IPv4': - await service.execute('smbmap -H {address} -P {port} 2>&1', outfile='smbmap-share-permissions.txt') - await service.execute('smbmap -u null -p "" -H {address} -P {port} 2>&1', outfile='smbmap-share-permissions.txt') - await service.execute('smbmap -H {address} -P {port} -R 2>&1', outfile='smbmap-list-contents.txt') - await service.execute('smbmap -u null -p "" -H {address} -P {port} -R 2>&1', outfile='smbmap-list-contents.txt') - await service.execute('smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1', outfile='smbmap-execute-command.txt') - await service.execute('smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1', outfile='smbmap-execute-command.txt') diff --git a/autorecon/default-plugins/smbclient.py b/autorecon/default-plugins/smbclient.py new file mode 100644 index 0000000..64ad0df --- /dev/null +++ b/autorecon/default-plugins/smbclient.py @@ -0,0 +1,16 @@ +from autorecon.plugins import ServiceScan + +class SMBClient(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "SMBClient" + self.tags = ['default', 'safe', 'smb', 'active-directory'] + + def configure(self): + self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) + self.match_port('tcp', [139, 445]) + self.run_once(True) + + async def run(self, service): + await service.execute('smbclient -L //{address} -N -I {address} 2>&1', outfile='smbclient.txt') diff --git a/autorecon/default-plugins/smbmap.py b/autorecon/default-plugins/smbmap.py new file mode 100644 index 0000000..de500f7 --- /dev/null +++ b/autorecon/default-plugins/smbmap.py @@ -0,0 +1,20 @@ +from autorecon.plugins import ServiceScan + +class SMBMap(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "SMBMap" + self.tags = ['default', 'safe', 'smb', 'active-directory'] + + def configure(self): + self.match_service_name(['^smb', '^microsoft\-ds', '^netbios']) + + async def run(self, service): + if service.target.ipversion == 'IPv4': + await service.execute('smbmap -H {address} -P {port} 2>&1', outfile='smbmap-share-permissions.txt') + await service.execute('smbmap -u null -p "" -H {address} -P {port} 2>&1', outfile='smbmap-share-permissions.txt') + await service.execute('smbmap -H {address} -P {port} -R 2>&1', outfile='smbmap-list-contents.txt') + await service.execute('smbmap -u null -p "" -H {address} -P {port} -R 2>&1', outfile='smbmap-list-contents.txt') + await service.execute('smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1', outfile='smbmap-execute-command.txt') + await service.execute('smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1', outfile='smbmap-execute-command.txt') diff --git a/autorecon/default-plugins/smtp.py b/autorecon/default-plugins/smtp-user-enum.py similarity index 70% rename from autorecon/default-plugins/smtp.py rename to autorecon/default-plugins/smtp-user-enum.py index 705c154..5a995b2 100644 --- a/autorecon/default-plugins/smtp.py +++ b/autorecon/default-plugins/smtp-user-enum.py @@ -1,18 +1,5 @@ from autorecon.plugins import ServiceScan -class NmapSMTP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap SMTP" - self.tags = ['default', 'safe', 'smtp', 'email'] - - def configure(self): - self.match_service_name('^smtp') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(smtp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smtp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smtp_nmap.xml" {address}') - class SMTPUserEnum(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/snmp.py b/autorecon/default-plugins/snmpwalk.py similarity index 55% rename from autorecon/default-plugins/snmp.py rename to autorecon/default-plugins/snmpwalk.py index 7b104a0..5263700 100644 --- a/autorecon/default-plugins/snmp.py +++ b/autorecon/default-plugins/snmpwalk.py @@ -1,35 +1,5 @@ from autorecon.plugins import ServiceScan -class NmapSNMP(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "Nmap SNMP" - self.tags = ['default', 'safe', 'snmp'] - - def configure(self): - self.match_service_name('^snmp') - - async def run(self, service): - await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(snmp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_snmp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_snmp_nmap.xml" {address}') - -class OneSixtyOne(ServiceScan): - - def __init__(self): - super().__init__() - self.name = "OneSixtyOne" - self.tags = ['default', 'safe', 'snmp'] - - def configure(self): - self.match_service_name('^snmp') - self.match_port('udp', 161) - self.run_once(True) - self.add_option('community-strings', default='/usr/share/seclists/Discovery/SNMP/common-snmp-community-strings-onesixtyone.txt', help='The file containing a list of community strings to try. Default: %(default)s') - - async def run(self, service): - if service.target.ipversion == 'IPv4': - await service.execute('onesixtyone -c ' + service.get_option('community-strings') + ' -dd {address} 2>&1', outfile='{protocol}_{port}_snmp_onesixtyone.txt') - class SNMPWalk(ServiceScan): def __init__(self): diff --git a/autorecon/default-plugins/subdomain-enumeration.py b/autorecon/default-plugins/subdomain-enumeration.py new file mode 100644 index 0000000..185f5a3 --- /dev/null +++ b/autorecon/default-plugins/subdomain-enumeration.py @@ -0,0 +1,34 @@ +from autorecon.plugins import ServiceScan +import os + +class SubdomainEnumeration(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "Subdomain Enumeration" + self.slug = "subdomain-enum" + self.tags = ['default', 'safe', 'long', 'dns'] + + def configure(self): + self.add_option('domain', help='The domain to use as the base domain (e.g. example.com) for subdomain enumeration. Default: %(default)s') + self.add_list_option('wordlist', default=['/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt'], help='The wordlist(s) to use when enumerating subdomains. Separate multiple wordlists with spaces. Default: %(default)s') + self.add_option('threads', default=10, help='The number of threads to use when enumerating subdomains. Default: %(default)s') + self.match_service_name('^domain') + + async def run(self, service): + domains = [] + + if self.get_option('domain'): + domains.append(self.get_option('domain')) + if service.target.type == 'hostname' and service.target.address not in domains: + domains.append(service.target.address) + if self.get_global('domain') and self.get_global('domain') not in domains: + domains.append(self.get_global('domain')) + + if len(domains) > 0: + for wordlist in self.get_option('wordlist'): + name = os.path.splitext(os.path.basename(wordlist))[0] + for domain in domains: + await service.execute('gobuster dns -d ' + domain + ' -r {addressv6} -w ' + wordlist + ' -o "{scandir}/{protocol}_{port}_' + domain + '_subdomains_' + name + '.txt"') + else: + service.info('The target was not a domain, nor was a domain provided as an option. Skipping subdomain enumeration.') diff --git a/autorecon/default-plugins/virtual-host-enumeration.py b/autorecon/default-plugins/virtual-host-enumeration.py new file mode 100644 index 0000000..b8e0907 --- /dev/null +++ b/autorecon/default-plugins/virtual-host-enumeration.py @@ -0,0 +1,39 @@ +from autorecon.plugins import ServiceScan +from shutil import which +import os + +class VirtualHost(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'Virtual Host Enumeration' + self.slug = 'vhost-enum' + self.tags = ['default', 'safe', 'http', 'long'] + + def configure(self): + self.add_option('hostname', help='The hostname to use as the base host (e.g. example.com) for virtual host enumeration. Default: %(default)s') + self.add_list_option('wordlist', default=['/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt'], help='The wordlist(s) to use when enumerating virtual hosts. Separate multiple wordlists with spaces. Default: %(default)s') + self.add_option('threads', default=10, help='The number of threads to use when enumerating virtual hosts. Default: %(default)s') + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + def check(self): + if which('gobuster') is None: + self.error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)') + + async def run(self, service): + hostnames = [] + if self.get_option('hostname'): + hostnames.append(self.get_option('hostname')) + if service.target.type == 'hostname' and service.target.address not in hostnames: + hostnames.append(service.target.address) + if self.get_global('domain') and self.get_global('domain') not in hostnames: + hostnames.append(self.get_global('domain')) + + if len(hostnames) > 0: + for wordlist in self.get_option('wordlist'): + name = os.path.splitext(os.path.basename(wordlist))[0] + for hostname in hostnames: + await service.execute('gobuster vhost -k -u {http_scheme}://' + hostname + ':{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -r -o "{scandir}/{protocol}_{port}_{http_scheme}_' + hostname + '_vhosts_' + name + '.txt"') + else: + service.info('The target was not a hostname, nor was a hostname provided as an option. Skipping virtual host enumeration.') diff --git a/autorecon/default-plugins/whatweb.py b/autorecon/default-plugins/whatweb.py new file mode 100644 index 0000000..017d80c --- /dev/null +++ b/autorecon/default-plugins/whatweb.py @@ -0,0 +1,16 @@ +from autorecon.plugins import ServiceScan + +class WhatWeb(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "whatweb" + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + async def run(self, service): + if service.protocol == 'tcp' and service.target.ipversion == 'IPv4': + await service.execute('whatweb --color=never --no-errors -a 3 -v {http_scheme}://{address}:{port} 2>&1', outfile='{protocol}_{port}_{http_scheme}_whatweb.txt') diff --git a/autorecon/default-plugins/winrm-detection.py b/autorecon/default-plugins/winrm-detection.py new file mode 100644 index 0000000..a0aa97c --- /dev/null +++ b/autorecon/default-plugins/winrm-detection.py @@ -0,0 +1,32 @@ +from autorecon.plugins import ServiceScan +from autorecon.io import fformat + +class WinRMDetection(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'WinRM Detection' + self.tags = ['default', 'safe', 'winrm'] + + def configure(self): + self.match_service_name('^wsman') + self.match_service('tcp', [5985, 5986], '^http') + + async def run(self, service): + filename = fformat('{scandir}/{protocol}_{port}_winrm-detection.txt') + with open(filename, mode='wt', encoding='utf8') as winrm: + winrm.write('WinRM was possibly detected running on ' + service.protocol + ' port ' + str(service.port) + '.\nCheck _manual_commands.txt for manual commands you can run against this service.') + + def manual(self, service, plugin_was_run): + service.add_manual_commands('Bruteforce logins:', [ + 'crackmapexec winrm {address} -d ' + self.get_global('domain', default='') + ' -u ' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + ' -p ' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + ]) + + service.add_manual_commands('Check login (requires credentials):', [ + 'crackmapexec winrm {address} -d ' + self.get_global('domain', default='') + ' -u -p -x "whoami"' + ]) + + service.add_manual_commands('Evil WinRM (gem install evil-winrm):', [ + 'evil-winrm -u -p -i {address}', + 'evil-winrm -u -H -i {address}' + ]) diff --git a/autorecon/default-plugins/wkhtmltoimage.py b/autorecon/default-plugins/wkhtmltoimage.py new file mode 100644 index 0000000..7c4890a --- /dev/null +++ b/autorecon/default-plugins/wkhtmltoimage.py @@ -0,0 +1,22 @@ +from autorecon.plugins import ServiceScan +from shutil import which + +class WkHTMLToImage(ServiceScan): + + def __init__(self): + super().__init__() + self.name = "wkhtmltoimage" + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + def check(self): + if which('wkhtmltoimage') is None: + self.error('The wkhtmltoimage program could not be found. Make sure it is installed. (On Kali, run: sudo apt install wkhtmltopdf)') + + async def run(self, service): + if which('wkhtmltoimage') is not None: + if service.protocol == 'tcp': + await service.execute('wkhtmltoimage --format png {http_scheme}://{addressv6}:{port}/ {scandir}/{protocol}_{port}_{http_scheme}_screenshot.png') diff --git a/autorecon/default-plugins/wpscan.py b/autorecon/default-plugins/wpscan.py new file mode 100644 index 0000000..a5a9249 --- /dev/null +++ b/autorecon/default-plugins/wpscan.py @@ -0,0 +1,20 @@ +from autorecon.plugins import ServiceScan + +class WPScan(ServiceScan): + + def __init__(self): + super().__init__() + self.name = 'WPScan' + self.tags = ['default', 'safe', 'http'] + + def configure(self): + self.add_option('api-token', help='An API Token from wpvulndb.com to help search for more vulnerabilities.') + self.match_service_name('^http') + self.match_service_name('^nacn_http$', negative_match=True) + + def manual(self, service, plugin_was_run): + api_token = '' + if self.get_option('api-token'): + api_token = ' --api-token ' + self.get_option('api-token') + + service.add_manual_command('(wpscan) WordPress Security Scanner (useful if WordPress is found):', 'wpscan --url {http_scheme}://{addressv6}:{port}/ --no-update -e vp,vt,tt,cb,dbe,u,m --plugins-detection aggressive --plugins-version-detection aggressive -f cli-no-color' + api_token + ' 2>&1 | tee "{scandir}/{protocol}_{port}_{http_scheme}_wpscan.txt"') diff --git a/autorecon/main.py b/autorecon/main.py index bddd5c9..8c08d34 100644 --- a/autorecon/main.py +++ b/autorecon/main.py @@ -17,7 +17,7 @@ from autorecon.io import slugify, e, fformat, cprint, debug, info, warn, error, from autorecon.plugins import Pattern, PortScan, ServiceScan, Report, AutoRecon from autorecon.targets import Target, Service -VERSION = "2.0.15" +VERSION = "2.0.16" if not os.path.exists(config['config_dir']): shutil.rmtree(config['config_dir'], ignore_errors=True, onerror=None) diff --git a/pyproject.toml b/pyproject.toml index af3ab44..7946f0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "autorecon" -version = "2.0.15" +version = "2.0.16" description = "A multi-threaded network reconnaissance tool which performs automated enumeration of services." authors = ["Tib3rius"] license = "GNU GPL v3"