Skip to content

Commit

Permalink
Merge branch 'master' into ecw/multi-word-list
Browse files Browse the repository at this point in the history
  • Loading branch information
codingo authored Oct 2, 2017
2 parents 87f618a + dbf3b90 commit 7439e19
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 15 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ A virtual host scanner that can be used with pivot tools, detect catch-all scena
* Work over HTTP and HTTPS
* Ability to set the real port of the webserver to use in headers when pivoting through ssh/nc
* Add simple response headers to bypass some WAF products
* Identify new targets by using reverse lookups and append to wordlist

## Product Comparisons

Expand Down Expand Up @@ -40,6 +41,8 @@ $ pip install -r requirements.txt
| --unique-depth UNIQUE_DEPTH | Show likely matches of page content that is found x times (default 1). |
| --ssl | If set then connections will be made over HTTPS instead of HTTP. |
| --fuzzy-logic | If set then all unique content replies are compared and a similarity ratio is given for each pair. This helps to isolate vhosts in situations where a default page isn't static (such as having the time on it). |
| --no-lookups | Disbale reverse lookups (identifies new targets and append to wordlist, on by default). |
| --rate-limit | Amount of time in seconds to delay between each scan (default 0). |
| --waf | If set then simple WAF bypass headers will be sent. |
| -oN OUTPUT_NORMAL | Normal output printed to a file when the -oN option is specified with a filename argument. |
| - | By passing a blank '-' you tell VHostScan to expect input from stdin (pipe). |
Expand Down
18 changes: 15 additions & 3 deletions VHostScan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import os
import sys
from argparse import ArgumentParser
from dns.resolver import Resolver
from socket import gethostbyaddr
from lib.core.virtual_host_scanner import *
from lib.helpers.output_helper import *
from lib.helpers.file_helper import get_combined_word_lists
Expand All @@ -29,10 +31,12 @@ def main():
parser.add_argument('--unique-depth', dest='unique_depth', type=int, help='Show likely matches of page content that is found x times (default 1).', default=1)
parser.add_argument("--ssl", dest="ssl", action="store_true", help="If set then connections will be made over HTTPS instead of HTTP (default http).", default=False)
parser.add_argument("--fuzzy-logic", dest="fuzzy_logic", action="store_true", help="If set then fuzzy match will be performed against unique hosts (default off).", default=False)
parser.add_argument("--no-lookups", dest="no_lookup", action="store_true", help="Disable reverse lookups (identifies new targets and appends to wordlist, on by default).", default=False)
parser.add_argument("--rate-limit", dest="rate_limit", type=int, help='Amount of time in seconds to delay between each scan (default 0).', default=0)
parser.add_argument("--waf", dest="add_waf_bypass_headers", action="store_true", help="If set then simple WAF bypass headers will be sent.", default=False)
parser.add_argument("-oN", dest="output_normal", help="Normal output printed to a file when the -oN option is specified with a filename argument." )
parser.add_argument("-", dest="stdin", action="store_true", help="By passing a blank '-' you tell VHostScan to expect input from stdin (pipe).", default=False)

arguments = parser.parse_args()
wordlist = []

Expand Down Expand Up @@ -71,8 +75,16 @@ def main():
if(arguments.ignore_content_length > 0):
print("[>] Ignoring Content length: %s" % (arguments.ignore_content_length))

scanner = virtual_host_scanner( arguments.target_hosts, arguments.base_host, wordlist, arguments.port, arguments.real_port, arguments.ssl,
arguments.unique_depth, arguments.ignore_http_codes, arguments.ignore_content_length, arguments.fuzzy_logic, arguments.add_waf_bypass_headers)
if not arguments.no_lookup:
for ip in Resolver().query(arguments.target_hosts, 'A'):
host, aliases, ips = gethostbyaddr(str(ip))
wordlist.append(str(ip))
wordlist.append(host)
wordlist.extend(aliases)

scanner_args = vars(arguments)
scanner_args.update({'target': arguments.target_hosts, 'wordlist': wordlist})
scanner = virtual_host_scanner(**scanner_args)

scanner.scan()
output = output_helper(scanner, arguments)
Expand Down
37 changes: 25 additions & 12 deletions lib/core/virtual_host_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import requests
import hashlib
import pandas as pd
import time
from lib.core.discovered_host import *



class virtual_host_scanner(object):
"""Virtual host scanning class
Expand All @@ -19,19 +19,21 @@ class virtual_host_scanner(object):
ignore_content_length: integer value of content length to ignore
output: folder to write output file to
"""

def __init__(self, target, base_host, wordlist, port=80, real_port=80, ssl=False, unique_depth=1, ignore_http_codes='404', ignore_content_length=0, fuzzy_logic=False, add_waf_bypass_headers=False):


def __init__(self, target, wordlist, **kwargs):
self.target = target
self.base_host = base_host
self.port = int(port)
self.real_port = int(real_port)
self.ignore_http_codes = list(map(int, ignore_http_codes.replace(' ', '').split(',')))
self.ignore_content_length = ignore_content_length
self.wordlist = wordlist
self.unique_depth = unique_depth
self.ssl = ssl
self.fuzzy_logic = fuzzy_logic
self.add_waf_bypass_headers = add_waf_bypass_headers
self.base_host = kwargs.get('base_host')
self.rate_limit = int(kwargs.get('rate_limit', 0))
self.port = int(kwargs.get('port', 80))
self.real_port = int(kwargs.get('real_port', 80))
self.ignore_content_length = int(kwargs.get('ignore_content_length', 0))
self.ssl = kwargs.get('ssl', False)
self.fuzzy_logic = kwargs.get('fuzzy_logic', False)
self.add_waf_bypass_headers = kwargs.get('add_waf_bypass_headers', False)
self.unique_depth = int(kwargs.get('unique_depth', 1))
self.ignore_http_codes = kwargs.get('ignore_http_codes', '404')

# this can be made redundant in future with better exceptions
self.completed_scan=False
Expand All @@ -42,6 +44,14 @@ def __init__(self, target, base_host, wordlist, port=80, real_port=80, ssl=False
# store associated data for discovered hosts in array for oN, oJ, etc'
self.hosts = []

@property
def ignore_http_codes(self):
return self._ignore_http_codes

@ignore_http_codes.setter
def ignore_http_codes(self, codes):
self._ignore_http_codes = [int(code) for code in codes.replace(' ', '').split(',')]


def scan(self):
if not self.base_host:
Expand Down Expand Up @@ -104,6 +114,9 @@ def scan(self):

# add url and hash into array for likely matches
self.results.append(hostname + ',' + page_hash)

#rate limit the connection, if the int is 0 it is ignored
time.sleep(self.rate_limit)

self.completed_scan=True

Expand Down

0 comments on commit 7439e19

Please sign in to comment.