Skip to content

Commit 7350602

Browse files
authored
Merge pull request #63 from prabhath6/first_hit
Add first hit flag to return first successful result.
2 parents b2ec938 + 82dafa0 commit 7350602

File tree

5 files changed

+44
-28
lines changed

5 files changed

+44
-28
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ $ pip install -r requirements.txt
3939
| -r REAL_PORT | The real port of the webserver to use in headers when not 80 (see RFC2616 14.23), useful when pivoting through ssh/nc etc (default to PORT). |
4040
| --ignore-http-codes IGNORE_HTTP_CODES | Comma separated list of http codes to ignore with virtual host scans (default 404). |
4141
| --ignore-content-length IGNORE_CONTENT_LENGTH | Ignore content lengths of specificed amount. |
42+
| --first-hit | Return first successful result. Only use in scenarios where you are sure no catch-all is configured (such as a CTF). |
4243
| --unique-depth UNIQUE_DEPTH | Show likely matches of page content that is found x times (default 1). |
4344
| --ssl | If set then connections will be made over HTTPS instead of HTTP. |
4445
| --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). |
@@ -51,6 +52,7 @@ $ pip install -r requirements.txt
5152
| -oJ OUTPUT_JSON | JSON output printed to a file when the -oJ option is specified with a filename argument. |
5253
| - | By passing a blank '-' you tell VHostScan to expect input from stdin (pipe). |
5354

55+
5456
## Usage Examples
5557

5658
_Note that a number of these examples reference 10.10.10.29. This IP refers to BANK.HTB, a retired target machine from HackTheBox (https://www.hackthebox.eu/)._
@@ -98,4 +100,4 @@ pip install -r test-requirements.txt
98100
pytest
99101
```
100102

101-
If you're thinking of adding a new feature to the project, consider also contributing with a couple of tests. A well-tested codebase is a sane codebase. :)
103+
If you're thinking of adding a new feature to the project, consider also contributing with a couple of tests. A well-tested codebase is a sane codebase. :)

VHostScan.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def main():
2828

2929
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).', default='404')
3030
parser.add_argument('--ignore-content-length', dest='ignore_content_length', type=int, help='Ignore content lengths of specificed amount (default 0).', default=0)
31+
parser.add_argument('--first-hit', dest='first_hit', action='store_true', help='Return first successful result. Only use in scenarios where you are sure no catch-all is configured (such as a CTF).', default=False)
3132
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)
3233
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)
3334
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)
@@ -40,7 +41,7 @@ def main():
4041
parser.add_argument("-oJ", dest="output_json", help="JSON output printed to a file when the -oJ option is specified with a filename argument." )
4142
parser.add_argument("-", dest="stdin", action="store_true", help="By passing a blank '-' you tell VHostScan to expect input from stdin (pipe).", default=False)
4243

43-
arguments = parser.parse_args()
44+
arguments = parser.parse_args()
4445
wordlist = []
4546

4647
word_list_types = []
@@ -69,23 +70,26 @@ def main():
6970

7071
user_agents = []
7172
if arguments.user_agent:
72-
print('[>] User-Agent specified, using it')
73+
print('[>] User-Agent specified, using it.')
7374
user_agents = [arguments.user_agent]
7475
elif arguments.random_agent:
75-
print('[>] Random User-Agent flag set')
76+
print('[>] Random User-Agent flag set.')
7677
user_agents = load_random_user_agents()
7778

7879
if(arguments.ssl):
79-
print("[>] SSL flag set, sending all results over HTTPS")
80+
print("[>] SSL flag set, sending all results over HTTPS.")
8081

8182
if(arguments.add_waf_bypass_headers):
82-
print("[>] WAF flag set, sending simple WAF bypass headers")
83+
print("[>] WAF flag set, sending simple WAF bypass headers.")
8384

8485
print("[>] Ignoring HTTP codes: %s" % (arguments.ignore_http_codes))
85-
86+
8687
if(arguments.ignore_content_length > 0):
8788
print("[>] Ignoring Content length: %s" % (arguments.ignore_content_length))
8889

90+
if arguments.first_hit:
91+
print("[>] First hit is set.")
92+
8993
if not arguments.no_lookup:
9094
for ip in Resolver().query(arguments.target_hosts, 'A'):
9195
host, aliases, ips = gethostbyaddr(str(ip))

lib/core/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
# |V|H|o|s|t|S|c|a|n| Developed by @codingo_ & @__timk
33
# +-+-+-+-+-+-+-+-+-+ https://github.com/codingo/VHostScan
44

5-
__version__ = '1.5.4'
5+
__version__ = '1.6'

lib/core/virtual_host_scanner.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
2424

2525
class virtual_host_scanner(object):
2626
"""Virtual host scanning class
27-
27+
2828
Virtual host scanner has the following properties:
29-
29+
3030
Attributes:
3131
wordlist: location to a wordlist file to use with scans
3232
target: the target for scanning
@@ -50,6 +50,7 @@ def __init__(self, target, wordlist, **kwargs):
5050
self.add_waf_bypass_headers = kwargs.get('add_waf_bypass_headers', False)
5151
self.unique_depth = int(kwargs.get('unique_depth', 1))
5252
self.ignore_http_codes = kwargs.get('ignore_http_codes', '404')
53+
self.first_hit = kwargs.get('first_hit')
5354

5455
# this can be made redundant in future with better exceptions
5556
self.completed_scan=False
@@ -112,26 +113,15 @@ def scan(self):
112113

113114
# hash the page results to aid in identifing unique content
114115
page_hash = hashlib.sha256(res.text.encode('utf-8')).hexdigest()
115-
output = '[#] Found: {} (code: {}, length: {}, hash: {})\n'.format(hostname, res.status_code,
116-
res.headers.get('content-length'), page_hash)
117-
host = discovered_host()
118-
host.hostname = hostname
119-
host.response_code = res.status_code
120-
host.hash = page_hash
121-
host.content = res.content
122-
123-
for key, val in res.headers.items():
124-
output += ' {}: {}\n'.format(key, val)
125-
host.keys.append('{}: {}'.format(key, val))
126-
127-
self.hosts.append(host)
128-
129-
# print current results so feedback remains in "realtime"
130-
print(output)
116+
117+
self.hosts.append(self.create_host(res, hostname, page_hash))
131118

132119
# add url and hash into array for likely matches
133120
self.results.append(hostname + ',' + page_hash)
134-
121+
122+
if len(self.hosts) == 2 and self.first_hit:
123+
break
124+
135125
#rate limit the connection, if the int is 0 it is ignored
136126
time.sleep(self.rate_limit)
137127

@@ -154,3 +144,24 @@ def likely_matches(self):
154144
matches = ((segmented_data["key_col"].values).tolist())
155145

156146
return matches
147+
148+
def create_host(self, response, hostname, page_hash):
149+
"""
150+
Creates a host using the responce and the hash.
151+
Prints current result in real time.
152+
"""
153+
output = '[#] Found: {} (code: {}, length: {}, hash: {})\n'.format(hostname, response.status_code,
154+
response.headers.get('content-length'), page_hash)
155+
host = discovered_host()
156+
host.hostname = hostname
157+
host.response_code = response.status_code
158+
host.hash = page_hash
159+
host.content = response.content
160+
161+
for key, val in response.headers.items():
162+
output += ' {}: {}\n'.format(key, val)
163+
host.keys.append('{}: {}'.format(key, val))
164+
165+
print(output)
166+
167+
return host

tests/helpers/test_file_helper.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,3 @@ def test_get_combined_word_lists(wordlist):
3939

4040
assert wordlist.files == result['file_paths']
4141
assert wordlist.words == result['words']
42-

0 commit comments

Comments
 (0)