From e21482b4e9045e0624451d08b1021d2f431d2f3f Mon Sep 17 00:00:00 2001 From: Michael Skelton Date: Fri, 19 Oct 2018 00:28:59 +1000 Subject: [PATCH] Fixed broken reference --- Reconnoitre.egg-info/PKG-INFO | 137 +++++++ Reconnoitre.egg-info/SOURCES.txt | 23 ++ Reconnoitre.egg-info/dependency_links.txt | 1 + Reconnoitre.egg-info/entry_points.txt | 3 + Reconnoitre.egg-info/top_level.txt | 1 + .../Reconnoitre/__init__.py | 0 .../Reconnoitre/lib/__init__.py | 0 .../Reconnoitre/lib/config.json | 354 ++++++++++++++++++ .../Reconnoitre/lib/core/__init__.py | 0 .../Reconnoitre/lib/core/__version__.py | 6 + .../Reconnoitre/lib/core/input.py | 118 ++++++ .../Reconnoitre/lib/file_helper.py | 120 ++++++ .../Reconnoitre/lib/find_dns.py | 43 +++ .../Reconnoitre/lib/hostname_scan.py | 48 +++ .../Reconnoitre/lib/ping_sweeper.py | 45 +++ .../Reconnoitre/lib/service_scan.py | 102 +++++ .../Reconnoitre/lib/snmp_walk.py | 73 ++++ .../Reconnoitre/lib/virtual_host_scanner.py | 74 ++++ .../Reconnoitre/reconnoitre.py | 110 ++++++ .../wordlists/virtual-host-scanning.txt | 34 ++ dist/Reconnoitre-1.0-py2.7.egg | Bin 0 -> 31226 bytes setup.py | 2 +- 22 files changed, 1293 insertions(+), 1 deletion(-) create mode 100644 Reconnoitre.egg-info/PKG-INFO create mode 100644 Reconnoitre.egg-info/SOURCES.txt create mode 100644 Reconnoitre.egg-info/dependency_links.txt create mode 100644 Reconnoitre.egg-info/entry_points.txt create mode 100644 Reconnoitre.egg-info/top_level.txt create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/__init__.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/__init__.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/config.json create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__init__.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__version__.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/input.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/file_helper.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/find_dns.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/hostname_scan.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/ping_sweeper.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/service_scan.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/snmp_walk.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/lib/virtual_host_scanner.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/reconnoitre.py create mode 100644 build/lib.linux-x86_64-2.7/Reconnoitre/wordlists/virtual-host-scanning.txt create mode 100644 dist/Reconnoitre-1.0-py2.7.egg diff --git a/Reconnoitre.egg-info/PKG-INFO b/Reconnoitre.egg-info/PKG-INFO new file mode 100644 index 0000000..c0b4600 --- /dev/null +++ b/Reconnoitre.egg-info/PKG-INFO @@ -0,0 +1,137 @@ +Metadata-Version: 1.0 +Name: Reconnoitre +Version: 1.0 +Summary: A reconnaissance tool made for the OSCP labs to automate information gathering, and service enumeration whilst creating a directory structure to store results,findings and exploits used for each host, recommended commands to execute and directory structures for storing loot and flags. +Home-page: https://github.com/codingo/Reconnoitre +Author: codingo +Author-email: codingo@protonmail.com +License: GPLv3 +Description-Content-Type: UNKNOWN +Description: ![Reconnnoitre](https://github.com/codingo/Reconnoitre/blob/master/assets/tank-152362_640.png) + A reconnaissance tool made for the OSCP labs to automate information gathering and service enumeration whilst creating a directory structure to store results, findings and exploits used for each host, recommended commands to execute and directory structures for storing loot and flags. + + Contributions are more than welcome! + + [![Python 3.2|3.6](https://img.shields.io/badge/python-3.2|3.6-green.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPL3-_red.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![Build Status](https://travis-ci.org/codingo/Reconnoitre.svg?branch=master)](https://travis-ci.org/codingo/Reconnoitre) [![Twitter](https://img.shields.io/badge/twitter-@codingo__-blue.svg)](https://twitter.com/codingo_) + + # Credit + + This tool is based heavily upon the work made public in Mike Czumak's (T_v3rn1x) OSCP review ([link](https://www.securitysift.com/offsec-pwb-oscp/)) along with considerable influence and code taken from Re4son's mix-recon ([link](https://whitedome.com.au/re4son/category/re4son/oscpnotes/)). Virtual host scanning is originally adapted from teknogeek's work which is heavily influenced by jobertabma's virtual host discovery script ([link](https://github.com/jobertabma/virtual-host-discovery)). Further Virtual Host scanning code has been adapted from a project by Tim Kent and I, available here ([link](https://github.com/codingo/VHostScan)). + + # Installation + To install Reconnoitre first make a local copy of the repository by performing the following where you wish it to be located: + + ``` + git clone https://github.com/codingo/Reconnoitre.git + ``` + After you have done this run setup.py with the following: + ``` + python setup.py install + ``` + + After setup has run Reconnoitre will now be in your path (as reconnoitre) and you can launch it anywhere using: + ``` + reconnoitre + ``` + + # Usage + + This tool can be used and copied for personal use freely however attribution and credit should be offered to Mike Czumak who originally started the process of automating this work. + + | Argument | Description | + | ------------- |:-------------| + | -h, --help | Display help message and exit | + | -t TARGET_HOSTS | Set either a target range of addresses or a single host to target. May also be a file containing hosts. | + | -o OUTPUT_DIRECTORY | Set the target directory where results should be written. | + | -w WORDLIST | Optionally specify your own wordlist to use for pre-compiled commands, or executed attacks. | + | --pingsweep | Write a new target.txt file in the OUTPUT_DIRECTORY by performing a ping sweep and discovering live hosts. | + | --dns, --dnssweep | Find DNS servers from the list of target(s). | + | --snmp | Find hosts responding to SNMP requests from the list of target(s). | + | --services | Perform a service scan over the target(s) and write recommendations for further commands to execute. | + | --hostnames | Attempt to discover target hostnames and write to hostnames.txt. | + | --virtualhosts | Attempt to discover virtual hosts using the specified wordlist. This can be expended via discovered hostnames. | + | --ignore-http-codes | Comma separated list of http codes to ignore with virtual host scans. | + | --ignore-content-length | Ignore content lengths of specificed amount. This may become useful when a server returns a static page on every virtual host guess. | + | --quiet | Supress banner and headers and limit feedback to grepable results. | + | --quick | Move to the next target after performing a quick scan and writing first-round recommendations. | + | --no-udp | Disable UDP service scanning, which is ON by default. | + + ## Usage Examples + _Note that these are some examples to give you insight into potential use cases for this tool. Command lines can be added or removed based on what you wish to accomplish with your scan._ + + ### Scan a single host, create a file structure and discover services + ``` + python ./reconnoitre.py -t 192.168.1.5 -o /root/Documents/labs/ --services + ``` + + An example output would look like: + + ``` + root@kali:~/Documents/tools/reconnoitre/reconnoitre# python ./reconnoitre.py -t 192.168.1.5 --services -o /root/Documents/labs/ + __ + |"""\-= RECONNOITRE + (____) An OSCP scanner + + [#] Performing service scans + [*] Loaded single target: 192.168.1.5 + [+] Creating directory structure for 192.168.1.5 + [>] Creating scans directory at: /root/Documents/labs/192.168.1.5/scans + [>] Creating exploit directory at: /root/Documents/labs/192.168.1.5/exploit + [>] Creating loot directory at: /root/Documents/labs/192.168.1.5/loot + [>] Creating proof file at: /root/Documents/labs/192.168.1.5/proof.txt + [+] Starting quick nmap scan for 192.168.1.5 + [+] Writing findings for 192.168.1.5 + [>] Found HTTP service on 192.168.1.5:80 + [>] Found MS SMB service on 192.168.1.5:445 + [>] Found RDP service on 192.168.1.5:3389 + [*] TCP quick scan completed for 192.168.1.5 + [+] Starting detailed TCP/UDP nmap scans for 192.168.1.5 + [+] Writing findings for 192.168.1.5 + [>] Found MS SMB service on 192.168.1.5:445 + [>] Found RDP service on 192.168.1.5:3389 + [>] Found HTTP service on 192.168.1.5:80 + [*] TCP/UDP Nmap scans completed for 192.168.1.5 + ``` + Which would also write the following recommendations file in the scans folder for each target: + ``` + [*] Found HTTP service on 192.168.1.50:80 + [>] Use nikto & dirb / dirbuster for service enumeration, e.g + [=] nikto -h 192.168.1.50 -p 80 > /root/Documents/labs/192.168.1.50/scans/192.168.1.50_nikto.txt + [=] dirb http://192.168.1.50:80/ -o /root/Documents/labs/192.168.1.50/scans/192.168.1.50_dirb.txt -r -S -x ./dirb-extensions/php.ext + [=] java -jar /usr/share/dirbuster/DirBuster-1.0-RC1.jar -H -l /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -r /root/Documents/labs/192.168.1.50/scans/192.168.1.50_dirbuster.txt -u http://192.168.1.50:80/ + [=] gobuster -w /usr/share/seclists/Discovery/Web_Content/common.txt -u http://192.168.1.50:80/ -s '200,204,301,302,307,403,500' -e > /root/Documents/labs/192.168.1.50/scans/192.168.1.50_gobuster_common.txt -t 50 + [=] gobuster -w /usr/share/seclists/Discovery/Web_Content/cgis.txt -u http://192.168.1.50:80/ -s '200,204,301,307,403,500' -e > /root/Documents/labs/192.168.1.50/scans/192.168.1.50_gobuster_cgis.txt -t 50 + [>] Use curl to retreive web headers and find host information, e.g + [=] curl -i 192.168.1.50 + [=] curl -i 192.168.1.50/robots.txt -s | html2text + [*] Found MS SMB service on 192.168.1.5:445 + [>] Use nmap scripts or enum4linux for further enumeration, e.g + [=] nmap -sV -Pn -vv -p445 --script="smb-* -oN '/root/Documents/labs/192.168.1.5/nmap/192.168.1.5_smb.nmap' -oX '/root/Documents/labs/192.168.1.5/scans/192.168.1.5_smb_nmap_scan_import.xml' 192.168.1.5 + [=] enum4linux 192.168.1.5 + [*] Found RDP service on 192.168.1.5:3389 + [>] Use ncrackpassword cracking, e.g + [=] ncrack -vv --user administrator -P /root/rockyou.txt rdp://192.168.1.5 + ``` + ### Discover live hosts and hostnames within a range + ``` + python ./reconnoitre.py -t 192.168.1.1-252 -o /root/Documents/testing/ --pingsweep --hostnames + ``` + + ### Discover live hosts within a range and then do a quick probe for services + ``` + python ./reconnoitre.py -t 192.168.1.1-252 -o /root/Documents/testing/ --pingsweep --services --quick + ``` + This will scan all services within a target range to create a file structure of live hosts as well as write recommendations for other commands to be executed based on the services discovered on these machines. Removing --quick will do a further probe but will greatly lengthen execution times. + + ### Discover live hosts within a range and then do probe all ports (UDP and TCP) for services + ``` + python ./reconnoitre.py -t 192.168.1.1-252 -o /root/Documents/testing/ --pingsweep --services + ``` + + # Requirements + + This bare requirement for host and service scanning for this tool is to have both `nbtscan` and `nmap` installed. If you are not using host scanning and only wish to perform a ping sweep and service scan you can get away with only installing `nmap`. The outputted _findings.txt_ will often recommend additional tools which you may not have available in your distribution if not using Kali Linux. All requirements and recommendations are native to Kali Linux which is the recommended (although not required) distribution for using this tool. + + In addition to these requirements outputs will often refer to Wordlists that you may need to find. If you are undertaking OSCP these can be found in the "List of Recommended Tools" thread by g0tmilk. If not then you can find the majority of these online or already within a Kali Linux installation. + +Platform: UNKNOWN diff --git a/Reconnoitre.egg-info/SOURCES.txt b/Reconnoitre.egg-info/SOURCES.txt new file mode 100644 index 0000000..8261f66 --- /dev/null +++ b/Reconnoitre.egg-info/SOURCES.txt @@ -0,0 +1,23 @@ +MANIFEST.in +README.md +setup.py +Reconnoitre/__init__.py +Reconnoitre/reconnoitre.py +Reconnoitre.egg-info/PKG-INFO +Reconnoitre.egg-info/SOURCES.txt +Reconnoitre.egg-info/dependency_links.txt +Reconnoitre.egg-info/entry_points.txt +Reconnoitre.egg-info/top_level.txt +Reconnoitre/lib/__init__.py +Reconnoitre/lib/config.json +Reconnoitre/lib/file_helper.py +Reconnoitre/lib/find_dns.py +Reconnoitre/lib/hostname_scan.py +Reconnoitre/lib/ping_sweeper.py +Reconnoitre/lib/service_scan.py +Reconnoitre/lib/snmp_walk.py +Reconnoitre/lib/virtual_host_scanner.py +Reconnoitre/lib/core/__init__.py +Reconnoitre/lib/core/__version__.py +Reconnoitre/lib/core/input.py +Reconnoitre/wordlists/virtual-host-scanning.txt \ No newline at end of file diff --git a/Reconnoitre.egg-info/dependency_links.txt b/Reconnoitre.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Reconnoitre.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/Reconnoitre.egg-info/entry_points.txt b/Reconnoitre.egg-info/entry_points.txt new file mode 100644 index 0000000..259fb97 --- /dev/null +++ b/Reconnoitre.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +reconnoitre = Reconnoitre.reconnoitre:main + diff --git a/Reconnoitre.egg-info/top_level.txt b/Reconnoitre.egg-info/top_level.txt new file mode 100644 index 0000000..a7da783 --- /dev/null +++ b/Reconnoitre.egg-info/top_level.txt @@ -0,0 +1 @@ +Reconnoitre diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/__init__.py b/build/lib.linux-x86_64-2.7/Reconnoitre/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/__init__.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/config.json b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/config.json new file mode 100644 index 0000000..020cce6 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/config.json @@ -0,0 +1,354 @@ +{ + "services": { + "http/s": { + "description": "Found HTTP/S service on $ip:$port", + "nmap-service-names": [ + "http", + "ssl/http", + "https", + "ssl/http-alt" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "nikto -h $ip -p $port -output $outputdir/$ip_$port_nikto.txt", + "curl -i $ip:$port", + "w3m -dump $ip/robots.txt | tee $outputdir/$ip_$port_robots.txt", + "VHostScan -t $ip -oN $outputdir/$ip_$port_vhosts.txt" + ] + } + ] + }, + "http": { + "description": "Found HTTP service on $ip:$port", + "nmap-service-names": [ + "http" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "dirb http://$ip:$port/ -o $outputdir/$ip_$port_dirb.txt", + "dirbuster -H -u http://$ip:$port/ -l /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -t 20 -s / -v -r $outputdir/$ip_$port_dirbuster_medium.txt", + "gobuster -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://$ip:$port/ -s '200,204,301,302,307,403,500' -e | tee '$outputdir/$ip_$port_gobuster_common.txt'", + "gobuster -w /usr/share/seclists/Discovery/Web-Content/CGIs.txt -u http://$ip:$port/ -s '200,204,301,307,403,500' -e | tee '$outputdir/$ip_$port_gobuster_cgis.txt'" + ] + } + ] + }, + "https": { + "description": "Found HTTPS service on $ip:$port", + "nmap-service-names": [ + "https", + "ssl/http", + "ssl/http-alt" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "dirb https://$ip:$port/ -o $outputdir/$ip_$port_dirb.txt", + "dirbuster -H -u https://$ip:$port/ -l /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -t 20 -s / -v -r $outputdir/$ip_$port_dirbuster_medium.txt", + "gobuster -w /usr/share/seclists/Discovery/Web-Content/common.txt -u https://$ip:$port/ -s '200,204,301,302,307,403,500' -e | tee '$outputdir/$ip_$port_gobuster_common.txt'", + "gobuster -w /usr/share/seclists/Discovery/Web-Content/CGIs.txt -u https://$ip:$port/ -s '200,204,301,307,403,500' -e | tee '$outputdir/$ip_$port_gobuster_cgis.txt'" + ] + } + ] + }, + "ftp": { + "description": "Found FTP service on $ip:$port", + "nmap-service-names": [ + "ftp" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "nmap -sV -Pn -vv -p$port --script=ftp-anon,ftp-bounce,ftp-libopie,ftp-proftpd-backdoor,ftp-syst,ftp-vsftpd-backdoor,ftp-vuln-cve2010-4221 -oA '$outputdir/$ip_$port_ftp' $ip", + "hydra -L USER_LIST -P PASS_LIST -f -o $outputdir/$ip_$port_ftphydra.txt -u $ip -s $port ftp" + ] + } + ] + }, + "mysql": { + "description": "Found MySql service on $ip:$port", + "nmap-service-names": [ + "mysql" + ], + "output": [ + { + "description": "Check out the server for web applications with sqli vulnerabilities", + "commands": [ + "searchsploit mysql" + ] + } + ] + }, + "dns": { + "description": "Found DNS service on $ip:$port", + "nmap-service-names": [ + "dns" + ], + "output": [ + { + "description": "Check out the server for zone transfers", + "commands": [ + "dnsrecon -t axfr -d $ip" + ] + } + ] + }, + "microsoftsql": { + "description": "Found MS SQL service on $ip:$port", + "nmap-service-names": [ + "ms-sql", + "ms-sql-s" + ], + "output": [ + { + "description": "Check out the server for web applications with sqli vulnerabilities", + "commands": [ + "searchsploit mssql" + ] + }, + { + "description": "Use nmap scripts for further enumeration, e.g", + "commands": [ + "nmap -vv -sV -Pn -p $port --script=ms-sql-info,ms-sql-config,ms-sql-dump-hashes --script-args=mssql.instance-port=$port,smsql.username-sa,mssql.password-sa -oA $outputdir/$ip_$port_mssql_nmap_scan $ip" + ] + } + ] + }, + + "telnet": { + "description": "Found telnet service on $ip:$port", + "nmap-service-names": [ + "telnet" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "ncat -nv $ip $port" + ] + } + ] + }, + "smb": { + "description": "Found MS SMB service on $ip:$port", + "nmap-service-names": [ + "microsoft-ds" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "nmap -sV -Pn -vv -p 139,$port --script=smb-vuln* --script-args=unsafe=1 -oA '$outputdir/$ip_$port_smb.nmap' $ip", + "enum4linux -a $ip | tee $outputdir/$ip_$port_enum4linux.txt", + "nmap -sV -Pn -vv -p $port --script=smb-enum-users -oA '$outputdir/$ip_$port_smb_smb-enum-users.nmap' $ip" + ] + } + ] + }, + "remotedesktop": { + "description": "Found RDP service on $ip:$port", + "nmap-service-names": [ + "msrdp", + "ms-wbt-server" + ], + "output": [ + { + "description": "Bruteforcing", + "commands": [ + "ncrack -vv --user administrator -P PASS_LIST rdp://$ip", + "crowbar -b rdp -u -s $ip/32 -U USER_LIST -C PASS_LIST", + "for username in $(cat USER_LIST); do for password in $(cat PASS_LIST) do; rdesktop -u $username -p $password $ip; done; done;" + ] + } + ] + }, + "smtp": { + "description": "Found SMTP service on $ip:$port", + "nmap-service-names": [ + "smtp" + ], + "output": [ + { + "description": "Find users", + "commands": [ + "smtp-user-enum -M VRFY -U /usr/share/seclists/Usernames/top_shortlist.txt -t $ip -p $port" + ] + } + ] + }, + "snmp": { + "description": "Found SNMP service on $ip:$port", + "nmap-service-names": [ + "snmp" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "nmap -sV -Pn -vv -p$port --script=snmp-netstat,snmp-processes -oA '$outputdir/$ip_$port_snmp' $ip", + "onesixtyone $ip > $outputdir/$ip_$port_snmp_onesixtyone.txt", + "snmpwalk -c public -v1 $ip > $outputdir/$ip_$port_snmpwalk.txt" + ] + } + ] + }, + "ssh": { + "description": "Found SSH service on $ip:$port", + "nmap-service-names": [ + "ssh" + ], + "output": [ + { + "description": "Bruteforcing", + "commands": [ + "medusa -u root -P /usr/share/wordlists/rockyou.txt -e ns -h $ip - $port -M ssh", + "hydra -f -V -t 1 -l root -P /usr/share/wordlists/rockyou.txt -s $port $ip ssh", + "ncrack -vv -p $port --user root -P PASS_LIST $ip" + ] + }, + { + "description": "Use nmap to automate banner grabbing and key fingerprints, e.g.", + "commands": [ + "nmap $ip -p $port -sV --script=ssh-hostkey -oA '$outputdir/$ip_$port_ssh-hostkey'" + ] + } + ] + }, + "msrpc": { + "description": "Found MSRPC service on $ip:$port", + "nmap-service-names": [ + "msrpc", + "rpcbind" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "rpcclient -U \"\" $ip" + ] + }, + { + "description": "Bruteforce", + "commands": [ + "rpcclient -U \"\" $ip" + ] + } + ] + }, + "netbios-ssn": { + "description": "Found NetBIOS service on $ip:$port", + "nmap-service-names": [ + "netbios-ssn" + ], + "output": [ + { + "description": "Enumeration", + "commands": [ + "nmblookup -A $ip", + "smbclient //MOUNT/share -I $ip N", + "smbclient -L //$ip", + "enum4linux -a $ip", + "rpcclient -U \"\" $ip" + ] + } + ] + }, + "CUPS": { + "description": "Found CUPS service on $ip:$port", + "nmap-service-names": [ + "ipp" + ], + "output": [ + { + "description": "Find public exploits", + "commands": [ + "searchsploit cups" + ] + } + ] + }, + "java-rmi": { + "description": "Found CUPS service on $ip:$port", + "nmap-service-names": [ + "java-rmi" + ], + "output": [ + { + "description": "Find public exploits", + "commands": [ + "searchsploit java rmi" + ] + } + ] + }, + "vnc": { + "description": "Found VNC service on $ip:$port", + "nmap-service-names": [ + "vnc", + "vnc-http" + ], + "output": [ + { + "description": "Find public exploits", + "commands": [ + "searchsploit vnc" + ] + }, + { + "description": "Bruteforcing", + "commands": [ + "crowbar -b vnckey -s $ip/32 -p IP -k PASS_FILE" + ] + } + ] + }, + "oracle": { + "description": "Found Oracle service on $ip:$port", + "nmap-service-names": [ + "oracle-tns" + ], + "output": [ + { + "description": "Find public exploits", + "commands": [ + "searchsploit Oracle TNS" + ] + } + ] + }, + "kerberos": { + "description": "Found Kerberos service on $ip:$port", + "nmap-service-names": [ + "kerberos-sec" + ], + "output": [ + { + "description": "Find public exploits", + "commands": [ + "searchsploit kerberos" + ] + } + ] + }, + "ldap": { + "description": "Found LDAP service on $ip:$port", + "nmap-service-names": [ + "ldap" + ], + "output": [ + { + "description": "Find public exploits", + "commands": [ + "searchsploit ldap" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__init__.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__version__.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__version__.py new file mode 100644 index 0000000..19d8875 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/__version__.py @@ -0,0 +1,6 @@ +# +# |"""\-= RECONNOITRE +# (____) An OSCP scanner by @codingo_ +# https://github.com/codingo/VHostScan + +__version__ = '1.0' diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/input.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/input.py new file mode 100644 index 0000000..ce2a754 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/core/input.py @@ -0,0 +1,118 @@ +from argparse import ArgumentParser +import os.path + + +class CliHelper(object): + @staticmethod + def readable_file(parser, arg): + if not os.path.exists(arg): + parser.error("The file %s does not exist!" % arg) + else: + return open(arg, 'r') # return an open file handle + + +class CliArgumentParser(object): + def __init__(self): + self._parser = self.setup_parser() + + def parse(self, argv): + return self._parser.parse_args(argv) + + @staticmethod + def setup_parser(): + parser = ArgumentParser() + + parser.add_argument("-t", + dest="target_hosts", + required=True, + help="Set a target range of addresses to target. Ex 10.11.1.1-255") + + parser.add_argument("-o", + dest="output_directory", + required=True, + help="Set the output directory. Ex /root/Documents/labs/") + + parser.add_argument("-w", + dest="wordlist", + required=False, + help="Set the wordlist to use for generated commands. Ex /usr/share/wordlist.txt", + default=False) + + parser.add_argument("-p", + dest="port", + required=False, + help="Set the port to use. Leave blank to use discovered ports. " + "Useful to force virtual host scanning on non-standard webserver ports.", + default=80) + + parser.add_argument("--pingsweep", + dest="ping_sweep", + action="store_true", + help="Write a new target.txt by performing a ping sweep and discovering live hosts.", + default=False) + + parser.add_argument("--dns", "--dnssweep", + dest="find_dns_servers", + action="store_true", + help="Find DNS servers from a list of targets.", + default=False) + + parser.add_argument("--services", + dest="perform_service_scan", + action="store_true", + help="Perform service scan over targets.", + default=False) + + parser.add_argument("--hostnames", + dest="hostname_scan", + action="store_true", + help="Attempt to discover target hostnames and write to 0-name.txt and hostnames.txt.", + default=False) + + parser.add_argument("--snmp", + dest="perform_snmp_walk", + action="store_true", + help="Perform service scan over targets.", + default=False) + + parser.add_argument("--quick", + dest="quick", + action="store_true", + required=False, + help="Move to the next target after performing a quick scan and writing " + "first-round recommendations.", + default=False) + + parser.add_argument("--virtualhosts", + dest="virtualhosts", + action="store_true", + required=False, + help="Attempt to discover virtual hosts using the specified wordlist.", + default=False) + + parser.add_argument('--ignore-http-codes', + dest='ignore_http_codes', + type=str, + help='Comma separated list of http codes to ignore with virtual host scans.', + default='404') + + parser.add_argument('--ignore-content-length', + dest='ignore_content_length', + type=int, + help='Ignore content lengths of specificed amount. ' + 'This may become useful when a server returns a static page on ' + 'every virtual host guess.', + default=0) + + parser.add_argument("--quiet", + dest="quiet", + action="store_true", + help="Supress banner and headers to limit to comma dilimeted results only.", + default=False) + + parser.add_argument("--no-udp", + dest="no_udp_service_scan", + action="store_true", + help="Disable UDP services scan over targets.", + default=False) + return parser diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/file_helper.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/file_helper.py new file mode 100644 index 0000000..7991cbd --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/file_helper.py @@ -0,0 +1,120 @@ +import os +import json + + +def check_directory(output_directory): + try: + os.stat(output_directory) + except Exception: + os.mkdir(output_directory) + print("[!] %s didn't exist and has been created." % output_directory) + + +def load_targets(target_hosts, output_directory, quiet): + if (os.path.isdir(target_hosts) or os.path.isfile(target_hosts)): + return target_hosts + elif "-" in target_hosts: + expand_targets(target_hosts, output_directory) + return output_directory + "/targets.txt" + else: + return output_directory + "/targets.txt" + + +def expand_targets(target_hosts, output_directory): + parts = target_hosts.split(".") + target_list = [] + for part in parts: + if "-" in part: + iprange = part.split("-") + for i in range(int(iprange[0]), int(iprange[1])): + target_list.append(parts[0] + "." + parts[1] + "." + parts[2] + "." + str(i)) + with open(output_directory + "/targets.txt", "w") as targets: + for target in target_list: + targets.write("%s\n" % target) + + +def create_dir_structure(ip_address, output_directory): + print("[+] Creating directory structure for " + ip_address) + + hostdir = output_directory + "/" + ip_address + try: + os.stat(hostdir) + except Exception: + os.mkdir(hostdir) + + nmapdir = hostdir + "/scans" + print(" [>] Creating scans directory at: %s" % nmapdir) + try: + os.stat(nmapdir) + except Exception: + os.mkdir(nmapdir) + + exploitdir = hostdir + "/exploit" + print(" [>] Creating exploit directory at: %s" % exploitdir) + try: + os.stat(exploitdir) + except Exception: + os.mkdir(exploitdir) + + lootdir = hostdir + "/loot" + print(" [>] Creating loot directory at: %s" % lootdir) + try: + os.stat(lootdir) + except Exception: + os.mkdir(lootdir) + + prooffile = hostdir + "/proof.txt" + print(" [>] Creating proof file at: %s" % prooffile) + open(prooffile, 'a').close() + + +def write_recommendations(results, ip_address, outputdir): + recommendations_file = outputdir + "/" + ip_address + "_findings.txt" + serv_dict = {} + lines = results.split("\n") + for line in lines: + ports = [] + line = line.strip() + if ("tcp" in line) and ("open" in line) and not ("Discovered" in line): + while " " in line: + line = line.replace(" ", " ") + service = line.split(" ")[2] + port = line.split(" ")[0] + + if service in serv_dict: + ports = serv_dict[service] + + ports.append(port) + serv_dict[service] = ports + + print("[+] Writing findings for %s" % (ip_address)) + + __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + with open(os.path.join(__location__, "config.json"), "r") as config: + c = config.read() + j = json.loads(c.replace("$ip", "%(ip)s").replace("$port", "%(port)s").replace("$outputdir", "%(outputdir)s")) + + f = open(recommendations_file, 'w') + for serv in serv_dict: + ports = serv_dict[serv] + + for service in j["services"]: + if (serv in j["services"][service]["nmap-service-names"]) or (service in serv): + for port in ports: + port = port.split("/")[0] + + description = "[*] " + j["services"][service]["description"] + print(description % {"ip": ip_address, "port": port}) + f.write((description + "\n") % {"ip": ip_address, "port": port}) + + for entry in j["services"][service]["output"]: + f.write(" [*] " + entry["description"] + "\n") + + for cmd in entry["commands"]: + f.write( + (" [=] " + cmd + "\n") % {"ip": ip_address, "port": port, "outputdir": outputdir}) + + f.write("\n") + + f.write("\n\n[*] Always remember to manually go over the portscan report and carefully read between the lines ;)") + f.close() diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/find_dns.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/find_dns.py new file mode 100644 index 0000000..38fb816 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/find_dns.py @@ -0,0 +1,43 @@ +import subprocess + +from file_helper import check_directory +from file_helper import load_targets + + +def find_dns(target_hosts, output_directory, quiet): + check_directory(output_directory) + results = 0 + hostcount = 0 + dnscount = 0 + + output_file = open(output_directory + "/DNS-Detailed.txt", 'w') + output_targets = open(output_directory + "/DNS-targets.txt", 'w') + + targets = load_targets(target_hosts, output_directory, quiet) + target_file = open(targets, 'r') + + print("[*] Loaded targets from: %s" % targets) + print("[+] Enumerating TCP port 53 over targets to find dns servers") + + for ip_address in target_file: + hostcount += 1 + ip_address = ip_address.strip() + ip_address = ip_address.rstrip() + + print(" [>] Testing %s for DNS" % ip_address) + DNSSCAN = "nmap -n -sV -Pn -vv -p53 %s" % (ip_address) + results = subprocess.check_output(DNSSCAN, shell=True).decode("utf-8") + lines = results.split("\n") + + for line in lines: + line = line.strip() + line = line.rstrip() + if ("53/tcp" in line) and ("open" in line) and ("Discovered" not in line): + print(" [=] Found DNS service running on: %s" % (ip_address)) + output_file.write("[*] Found DNS service running on: %s\n" % (ip_address)) + output_file.write(" [>] %s\n" % (line)) + output_targets.write("%s" % (ip_address)) + dnscount += 1 + print("[*] Found %s DNS servers within %s hosts" % (str(dnscount), str(hostcount))) + output_file.close() + output_targets.close() diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/hostname_scan.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/hostname_scan.py new file mode 100644 index 0000000..cee97bf --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/hostname_scan.py @@ -0,0 +1,48 @@ +import os +import subprocess + +from file_helper import check_directory + + +def hostname_scan(target_hosts, output_directory, quiet): + check_directory(output_directory) + output_file = output_directory + "/hostnames.txt" + f = open(output_file, 'w') + print("[+] Writing hostnames to: %s" % output_file) + + hostnames = 0 + SWEEP = '' + + if (os.path.isfile(target_hosts)): + SWEEP = "nbtscan -q -f %s" % (target_hosts) + else: + SWEEP = "nbtscan -q %s" % (target_hosts) + + results = subprocess.check_output(SWEEP, shell=True).decode("utf-8") + lines = results.split("\n") + + for line in lines: + line = line.strip() + line = line.rstrip() + + # Final line is blank which causes list index issues if we don't + # continue past it. + if " " not in line: + continue + + while " " in line: + line = line.replace(" ", " ") + + ip_address = line.split(" ")[0] + host = line.split(" ")[1] + + if (hostnames > 0): + f.write('\n') + + print(" [>] Discovered hostname: %s (%s)" % (host, ip_address)) + f.write("%s - %s" % (host, ip_address)) + hostnames += 1 + + print("[*] Found %s hostnames." % (hostnames)) + print("[*] Created hostname list %s" % (output_file)) + f.close() diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/ping_sweeper.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/ping_sweeper.py new file mode 100644 index 0000000..3bea501 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/ping_sweeper.py @@ -0,0 +1,45 @@ +import subprocess + +from file_helper import check_directory + + +def ping_sweeper(target_hosts, output_directory, quiet): + check_directory(output_directory) + output_file = output_directory + "/targets.txt" + + print("[+] Performing ping sweep over %s" % target_hosts) + + lines = call_nmap_sweep(target_hosts) + live_hosts = parse_nmap_output_for_live_hosts(lines) + write_live_hosts_list_to_file(output_file, live_hosts) + + for ip_address in live_hosts: + print(" [>] Discovered host: %s" % (ip_address)) + + print("[*] Found %s live hosts" % (len(live_hosts))) + print("[*] Created target list %s" % (output_file)) + + +def call_nmap_sweep(target_hosts): + SWEEP = "nmap -n -sP %s" % (target_hosts) + + results = subprocess.check_output(SWEEP, shell=True) + lines = str(results).encode("utf-8").split("\n") + return lines + + +def parse_nmap_output_for_live_hosts(lines): + def get_ip_from_nmap_line(line): + return line.split()[4] + + live_hosts = [get_ip_from_nmap_line(line) + for line in lines + if "Nmap scan report for" in line] + + return live_hosts + + +def write_live_hosts_list_to_file(output_file, live_hosts): + print("[+] Writing discovered targets to: %s" % output_file) + with open(output_file, 'w') as f: + f.write("\n".join(live_hosts)) diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/service_scan.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/service_scan.py new file mode 100644 index 0000000..83702ad --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/service_scan.py @@ -0,0 +1,102 @@ +import multiprocessing +import socket +import subprocess + +from file_helper import check_directory +from file_helper import create_dir_structure +from file_helper import load_targets +from file_helper import write_recommendations + + +def nmap_scan(ip_address, output_directory, dns_server, quick, no_udp_service_scan): + ip_address = ip_address.strip() + + print("[+] Starting quick nmap scan for %s" % (ip_address)) + QUICKSCAN = "nmap -sC -sV %s -oA '%s/%s.quick'" % (ip_address, output_directory, ip_address) + quickresults = subprocess.check_output(QUICKSCAN, shell=True).decode("utf-8") + + write_recommendations(quickresults, ip_address, output_directory) + print("[*] TCP quick scans completed for %s" % ip_address) + + if (quick): + return + + if dns_server: + print("[+] Starting detailed TCP%s nmap scans for %s using DNS Server %s" % ( + ("" if no_udp_service_scan is True else "/UDP"), ip_address, dns_server)) + print("[+] Using DNS server %s" % (dns_server)) + TCPSCAN = "nmap -vv -Pn -sS -A -sC -p- -T 3 -script-args=unsafe=1 " \ + "--dns-servers %s -oN '%s/%s.nmap' -oX '%s/%s_nmap_scan_import.xml' %s" % ( + dns_server, output_directory, ip_address, output_directory, ip_address, ip_address) + UDPSCAN = "nmap -vv -Pn -A -sC -sU -T 4 --top-ports 200 --max-retries 0 " \ + "--dns-servers %s -oN '%s/%sU.nmap' -oX '%s/%sU_nmap_scan_import.xml' %s" % ( + dns_server, output_directory, ip_address, output_directory, ip_address, ip_address) + else: + print("[+] Starting detailed TCP%s nmap scans for %s" % ( + ("" if no_udp_service_scan is True else "/UDP"), ip_address)) + TCPSCAN = "nmap -vv -Pn -sS -A -sC -p- -T 3 " \ + "-script-args=unsafe=1 -n %s -oN '%s/%s.nmap' -oX '%s/%s_nmap_scan_import.xml' %s" % ( + dns_server, output_directory, ip_address, output_directory, ip_address, ip_address) + UDPSCAN = "nmap -sC -sV -sU %s -oA '%s/%s-udp'" % (ip_address, output_directory, ip_address) + + udpresults = "" if no_udp_service_scan is True else subprocess.check_output(UDPSCAN, shell=True).decode("utf-8") + tcpresults = subprocess.check_output(TCPSCAN, shell=True).decode("utf-8") + + write_recommendations(tcpresults + udpresults, ip_address, output_directory) + print("[*] TCP%s scans completed for %s" % (("" if no_udp_service_scan is True else "/UDP"), ip_address)) + + +def valid_ip(address): + try: + socket.inet_aton(address) + return True + except socket.error: + return False + + +def target_file(target_hosts, output_directory, dns_server, quiet, quick, no_udp_service_scan): + targets = load_targets(target_hosts, output_directory, quiet) + target_file = open(targets, 'r') + try: + target_file = open(targets, 'r') + print("[*] Loaded targets from: %s" % targets) + except Exception: + print("[!] Unable to load: %s" % targets) + + for ip_address in target_file: + ip_address = ip_address.strip() + create_dir_structure(ip_address, output_directory) + + host_directory = output_directory + "/" + ip_address + nmap_directory = host_directory + "/scans" + + jobs = [] + p = multiprocessing.Process(target=nmap_scan, + args=(ip_address, nmap_directory, dns_server, quick, no_udp_service_scan)) + jobs.append(p) + p.start() + target_file.close() + + +def target_ip(target_hosts, output_directory, dns_server, quiet, quick, no_udp_service_scan): + print("[*] Loaded single target: %s" % target_hosts) + target_hosts = target_hosts.strip() + create_dir_structure(target_hosts, output_directory) + + host_directory = output_directory + "/" + target_hosts + nmap_directory = host_directory + "/scans" + + jobs = [] + p = multiprocessing.Process(target=nmap_scan, + args=(target_hosts, nmap_directory, dns_server, quick, no_udp_service_scan)) + jobs.append(p) + p.start() + + +def service_scan(target_hosts, output_directory, dns_server, quiet, quick, no_udp_service_scan): + check_directory(output_directory) + + if (valid_ip(target_hosts)): + target_ip(target_hosts, output_directory, dns_server, quiet, quick, no_udp_service_scan) + else: + target_file(target_hosts, output_directory, dns_server, quiet, quick, no_udp_service_scan) diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/snmp_walk.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/snmp_walk.py new file mode 100644 index 0000000..e514352 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/snmp_walk.py @@ -0,0 +1,73 @@ +import multiprocessing +import socket +import subprocess + +from file_helper import check_directory, load_targets + + +def valid_ip(address): + try: + socket.inet_aton(address) + return True + except socket.error: + return False + + +def target_file(target_hosts, output_directory, quiet): + targets = load_targets(target_hosts, output_directory, quiet) + target_file = open(targets, 'r') + try: + target_file = open(targets, 'r') + print("[*] Loaded targets from: %s" % targets) + except Exception: + print("[!] Unable to load: %s" % targets) + + for ip_address in target_file: + ip_address = ip_address.strip() + + snmp_directory = output_directory + '/' + ip_address + '/scans/snmp/' + check_directory(snmp_directory) + + jobs = [] + p = multiprocessing.Process(target=snmp_scans, args=(ip_address, snmp_directory)) + jobs.append(p) + p.start() + target_file.close() + + +def target_ip(target_hosts, output_directory, quiet): + print("[*] Loaded single target: %s" % target_hosts) + target_hosts = target_hosts.strip() + + snmp_directory = output_directory + '/' + target_hosts + '/scans/snmp/' + check_directory(snmp_directory) + + jobs = [] + p = multiprocessing.Process(target=snmp_scans, args=(target_hosts, snmp_directory)) + jobs.append(p) + p.start() + + +def snmp_walk(target_hosts, output_directory, quiet): + check_directory(output_directory) + + if (valid_ip(target_hosts)): + target_ip(target_hosts, output_directory, quiet) + else: + target_file(target_hosts, output_directory, quiet) + + +def snmp_scans(ip_address, output_directory): + print("[+] Performing SNMP scans for %s to %s" % (ip_address, output_directory)) + print(" [>] Performing snmpwalk on public tree for: %s - Checking for System Processes" % (ip_address)) + SCAN = "snmpwalk -c public -v1 %s 1.3.6.1.2.1.25.1.6.0 > '%s%s-systemprocesses.txt'" % ( + ip_address, output_directory, ip_address) + + try: + subprocess.check_output(SCAN, stderr=subprocess.STDOUT, shell=True).decode("utf-8").decode('utf-8') + except Exception as e: + print("[+] No Response from %s" % ip_address) + except subprocess.CalledProcessError as cpe: + print("[+] Subprocess failure during scan of %s" % ip_address) + + print("[+] Completed SNMP scans for %s" % (ip_address)) diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/lib/virtual_host_scanner.py b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/virtual_host_scanner.py new file mode 100644 index 0000000..d18d22f --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/lib/virtual_host_scanner.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +import os +import requests + + +class VirtualHostScanner(object): + """Virtual host scanning class for Reconnoitre + + Virtual host scanner has the following properties: + + Attributes: + wordlist: location to a wordlist file to use with scans + target: the target for scanning + port: the port to scan. Defaults to 80 + ignore_http_codes: commad seperated list of http codes to ignore + ignore_content_length: integer value of content length to ignore + output: folder to write output file to + + """ + + def __init__(self, target, output, port=80, ignore_http_codes='404', ignore_content_length=0, + wordlist="./wordlist/virtual-host-scanning.txt"): + self.target = target + self.output = output + '/' + target + '_virtualhosts.txt' + self.port = port + self.ignore_http_codes = list(map(int, ignore_http_codes.replace(' ', '').split(','))) + self.ignore_content_length = ignore_content_length + self.wordlist = wordlist + + def scan(self): + print("[+] Starting virtual host scan for %s using port %s and wordlist %s" % ( + self.target, str(self.port), self.wordlist)) + print("[>] Ignoring HTTP codes: %s" % (self.ignore_http_codes)) + if (self.ignore_content_length > 0): + print("[>] Ignoring Content length: %s" % (self.ignore_content_length)) + + if not os.path.exists(self.wordlist): + print("[!] Wordlist %s doesn't exist, exiting virtual host scanner." % self.wordlist) + return + + virtual_host_list = open(self.wordlist).read().splitlines() + results = '' + + for virtual_host in virtual_host_list: + hostname = virtual_host.replace('%s', self.target) + + headers = { + 'Host': hostname if self.port == 80 else '{}:{}'.format(hostname, self.port), + 'Accept': '*/*' + } + + dest_url = '{}://{}:{}/'.format('https' if int(self.port) == 443 else 'http', self.target, self.port) + + try: + res = requests.get(dest_url, headers=headers, verify=False) + except requests.exceptions.RequestException: + continue + + if res.status_code in self.ignore_http_codes: + continue + + if self.ignore_content_length > 0 and self.ignore_content_length == int(res.headers.get('content-length')): + continue + + output = 'Found: {} (code: {}, length: {})'.format(hostname, res.status_code, + res.headers.get('content-length')) + results += output + '\n' + + print(output) + for key, val in res.headers.items(): + output = ' {}: {}'.format(key, val) + results += output + '\n' + print(output) diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/reconnoitre.py b/build/lib.linux-x86_64-2.7/Reconnoitre/reconnoitre.py new file mode 100644 index 0000000..ccab11a --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/reconnoitre.py @@ -0,0 +1,110 @@ +#!/usr/bin/python + + +import sys + +from lib.core.input import CliArgumentParser +from lib.find_dns import find_dns +from lib.hostname_scan import hostname_scan +from lib.ping_sweeper import ping_sweeper +from lib.service_scan import service_scan +from lib.snmp_walk import snmp_walk +from lib.virtual_host_scanner import VirtualHostScanner + + +def print_banner(): + print(" __") + print("|\"\"\"\-= RECONNOITRE") + print("(____) An OSCP scanner by @codingo_\n") + + +def util_checks(util=None): + if util is None: + print("[!] Error hit in chktool: None encountered for util.") + sys.exit(1) + + pyvers = sys.version_info + + if (pyvers[0] >= 3) and (pyvers[1] >= 3): # python3.3+ + import shutil + if shutil.which(util) is None: + if util is "nmap": + print( + " [!] nmap was not found on your system. Exiting since we wont be able to scan anything. " + "Please install nmap and try again.") + sys.exit(1) + else: + print(" [-] %s was not found in your system. Scan types using this will fail." % util) + return "Not Found" + else: + return "Found" + else: # less-than python 3.3 + from distutils import spawn + if spawn.find_executable(util) is None: + if util is "nmap": + print( + " [!] nmap was not found on your system. Exiting since we wont be able to scan anything. " + "Please install nmap and try again.") + sys.exit(1) + else: + print(" [-] %s was not found in your system. Scan types using this will fail." % util) + return "Not Found" + else: + return "Found" + + +def main(): + parser = CliArgumentParser() + arguments = parser.parse(sys.argv[1:]) + + if arguments.output_directory.endswith('/' or '\\'): + arguments.output_directory = arguments.output_directory[:-1] + if arguments.target_hosts.endswith('/' or '\\'): + arguments.target_hosts = arguments.target_hosts[:-1] + + if arguments.quiet is not True: + print_banner() + print("[+] Testing for required utilities on your system.") + + utils = ['nmap', 'snmpwalk', 'nbtscan'] # list of utils to check on local system. + for util in utils: + util_checks(util) + + if arguments.ping_sweep is True: + print("[#] Performing ping sweep") + ping_sweeper(arguments.target_hosts, arguments.output_directory, arguments.quiet) + + if arguments.hostname_scan is True: + print("[#] Identifying hostnames") + hostname_scan(arguments.target_hosts, arguments.output_directory, arguments.quiet) + + if arguments.find_dns_servers is True: + print("[#] Identifying DNS Servers") + find_dns(arguments.target_hosts, arguments.output_directory, arguments.quiet) + + if arguments.perform_service_scan is True: + print("[#] Performing service scans") + if arguments.find_dns_servers is True: + service_scan(arguments.target_hosts, arguments.output_directory, arguments.find_dns_servers, + arguments.quiet, arguments.quick, arguments.no_udp_service_scan) + else: + service_scan(arguments.target_hosts, arguments.output_directory, '', arguments.quiet, arguments.quick, + arguments.no_udp_service_scan) + + if arguments.perform_snmp_walk is True: + print("[#] Performing SNMP walks") + snmp_walk(arguments.target_hosts, arguments.output_directory, arguments.quiet) + + if arguments.virtualhosts is True: + print("[#] Performing Virtual host scans") + if arguments.wordlist is False: + print("[!] No wordlist was provided, skipping virtual host scanning.") + else: + scanner = VirtualHostScanner(arguments.target_hosts, arguments.output_directory, arguments.port, + arguments.ignore_http_codes, arguments.ignore_content_length, + arguments.wordlist) + scanner.scan() + + +if __name__ == "__main__": + main() diff --git a/build/lib.linux-x86_64-2.7/Reconnoitre/wordlists/virtual-host-scanning.txt b/build/lib.linux-x86_64-2.7/Reconnoitre/wordlists/virtual-host-scanning.txt new file mode 100644 index 0000000..80c09a6 --- /dev/null +++ b/build/lib.linux-x86_64-2.7/Reconnoitre/wordlists/virtual-host-scanning.txt @@ -0,0 +1,34 @@ +www.%s +dev.%s +local +localhost +status.%s +status +staging.%s +staging +development +development.%s +uat +uat.%s +%s +beta +beta.%s +secure +secure.%s +mobile +mobile.%s +127.0.0.1 +m.%s +m +admin +admin.%s +old +old.%s +v1.%s +v1 +v2.%s +v2 +v3.%s +v3 +alpha +alpha.%s \ No newline at end of file diff --git a/dist/Reconnoitre-1.0-py2.7.egg b/dist/Reconnoitre-1.0-py2.7.egg new file mode 100644 index 0000000000000000000000000000000000000000..1b9aca39b614c783694549c8a223126497525469 GIT binary patch literal 31226 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 T0RSL+{Q-SVF