Skip to content

Commit 7439e19

Browse files
authored
Merge branch 'master' into ecw/multi-word-list
2 parents 87f618a + dbf3b90 commit 7439e19

File tree

3 files changed

+43
-15
lines changed

3 files changed

+43
-15
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ A virtual host scanner that can be used with pivot tools, detect catch-all scena
1212
* Work over HTTP and HTTPS
1313
* Ability to set the real port of the webserver to use in headers when pivoting through ssh/nc
1414
* Add simple response headers to bypass some WAF products
15+
* Identify new targets by using reverse lookups and append to wordlist
1516

1617
## Product Comparisons
1718

@@ -40,6 +41,8 @@ $ pip install -r requirements.txt
4041
| --unique-depth UNIQUE_DEPTH | Show likely matches of page content that is found x times (default 1). |
4142
| --ssl | If set then connections will be made over HTTPS instead of HTTP. |
4243
| --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). |
44+
| --no-lookups | Disbale reverse lookups (identifies new targets and append to wordlist, on by default). |
45+
| --rate-limit | Amount of time in seconds to delay between each scan (default 0). |
4346
| --waf | If set then simple WAF bypass headers will be sent. |
4447
| -oN OUTPUT_NORMAL | Normal output printed to a file when the -oN option is specified with a filename argument. |
4548
| - | By passing a blank '-' you tell VHostScan to expect input from stdin (pipe). |

VHostScan.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import os
44
import sys
55
from argparse import ArgumentParser
6+
from dns.resolver import Resolver
7+
from socket import gethostbyaddr
68
from lib.core.virtual_host_scanner import *
79
from lib.helpers.output_helper import *
810
from lib.helpers.file_helper import get_combined_word_lists
@@ -29,10 +31,12 @@ def main():
2931
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)
3032
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)
3133
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)
34+
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)
35+
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)
3236
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)
3337
parser.add_argument("-oN", dest="output_normal", help="Normal output printed to a file when the -oN option is specified with a filename argument." )
3438
parser.add_argument("-", dest="stdin", action="store_true", help="By passing a blank '-' you tell VHostScan to expect input from stdin (pipe).", default=False)
35-
39+
3640
arguments = parser.parse_args()
3741
wordlist = []
3842

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

74-
scanner = virtual_host_scanner( arguments.target_hosts, arguments.base_host, wordlist, arguments.port, arguments.real_port, arguments.ssl,
75-
arguments.unique_depth, arguments.ignore_http_codes, arguments.ignore_content_length, arguments.fuzzy_logic, arguments.add_waf_bypass_headers)
78+
if not arguments.no_lookup:
79+
for ip in Resolver().query(arguments.target_hosts, 'A'):
80+
host, aliases, ips = gethostbyaddr(str(ip))
81+
wordlist.append(str(ip))
82+
wordlist.append(host)
83+
wordlist.extend(aliases)
84+
85+
scanner_args = vars(arguments)
86+
scanner_args.update({'target': arguments.target_hosts, 'wordlist': wordlist})
87+
scanner = virtual_host_scanner(**scanner_args)
7688

7789
scanner.scan()
7890
output = output_helper(scanner, arguments)

lib/core/virtual_host_scanner.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import requests
33
import hashlib
44
import pandas as pd
5+
import time
56
from lib.core.discovered_host import *
67

78

8-
99
class virtual_host_scanner(object):
1010
"""Virtual host scanning class
1111
@@ -19,19 +19,21 @@ class virtual_host_scanner(object):
1919
ignore_content_length: integer value of content length to ignore
2020
output: folder to write output file to
2121
"""
22-
23-
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):
22+
23+
24+
def __init__(self, target, wordlist, **kwargs):
2425
self.target = target
25-
self.base_host = base_host
26-
self.port = int(port)
27-
self.real_port = int(real_port)
28-
self.ignore_http_codes = list(map(int, ignore_http_codes.replace(' ', '').split(',')))
29-
self.ignore_content_length = ignore_content_length
3026
self.wordlist = wordlist
31-
self.unique_depth = unique_depth
32-
self.ssl = ssl
33-
self.fuzzy_logic = fuzzy_logic
34-
self.add_waf_bypass_headers = add_waf_bypass_headers
27+
self.base_host = kwargs.get('base_host')
28+
self.rate_limit = int(kwargs.get('rate_limit', 0))
29+
self.port = int(kwargs.get('port', 80))
30+
self.real_port = int(kwargs.get('real_port', 80))
31+
self.ignore_content_length = int(kwargs.get('ignore_content_length', 0))
32+
self.ssl = kwargs.get('ssl', False)
33+
self.fuzzy_logic = kwargs.get('fuzzy_logic', False)
34+
self.add_waf_bypass_headers = kwargs.get('add_waf_bypass_headers', False)
35+
self.unique_depth = int(kwargs.get('unique_depth', 1))
36+
self.ignore_http_codes = kwargs.get('ignore_http_codes', '404')
3537

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

47+
@property
48+
def ignore_http_codes(self):
49+
return self._ignore_http_codes
50+
51+
@ignore_http_codes.setter
52+
def ignore_http_codes(self, codes):
53+
self._ignore_http_codes = [int(code) for code in codes.replace(' ', '').split(',')]
54+
4555

4656
def scan(self):
4757
if not self.base_host:
@@ -104,6 +114,9 @@ def scan(self):
104114

105115
# add url and hash into array for likely matches
106116
self.results.append(hostname + ',' + page_hash)
117+
118+
#rate limit the connection, if the int is 0 it is ignored
119+
time.sleep(self.rate_limit)
107120

108121
self.completed_scan=True
109122

0 commit comments

Comments
 (0)