Skip to content

Commit 33943a0

Browse files
authored
Merge pull request #14 from codingo/codingo-output-normal-retry
Added Output Normal Functionality
2 parents 7d7443e + 012fd47 commit 33943a0

File tree

6 files changed

+111
-14
lines changed

6 files changed

+111
-14
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
# VIM temporary files
2+
*.pyc
3+
14
# Byte-compiled / optimized / DLL files
25
__pycache__/
3-
*.py[cod]
46
*$py.class
57

68
# C extensions
@@ -103,4 +105,3 @@ ENV/
103105
*.pyproj
104106
*.sln
105107
*.sqlite
106-
*.json

VHostScan.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import sys
55
from argparse import ArgumentParser
66
from lib.core.virtual_host_scanner import *
7+
from lib.helpers.output_helper import *
78

89

910
def print_banner():
10-
print("+-+-+-+-+-+-+-+-+-+ v. 0.2")
11+
print("+-+-+-+-+-+-+-+-+-+ v. 0.3")
1112
print("|V|H|o|s|t|S|c|a|n| Developed by @codingo_ & @__timk")
1213
print("+-+-+-+-+-+-+-+-+-+ https://github.com/codingo/VHostScan\n")
1314

@@ -25,6 +26,7 @@ def main():
2526
parser.add_argument('--ignore-content-length', dest='ignore_content_length', type=int, help='Ignore content lengths of specificed amount (default 0).', default=0)
2627
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)
2728
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)
29+
parser.add_argument("-oN", dest="output_normal", help="Normal output printed to a file when the -oN option is specified with a filename argument." )
2830
arguments = parser.parse_args()
2931

3032
if not os.path.exists(arguments.wordlist):
@@ -43,13 +45,17 @@ def main():
4345
if(arguments.ignore_content_length > 0):
4446
print("[>] Ignoring Content length: %s" % (arguments.ignore_content_length))
4547

46-
scanner = virtual_host_scanner(arguments.target_hosts, arguments.base_host, arguments.port, arguments.real_port, arguments.ssl, arguments.unique_depth,
47-
arguments.ignore_http_codes, arguments.ignore_content_length, arguments.wordlist)
48+
scanner = virtual_host_scanner(arguments.target_hosts, arguments.base_host, arguments.port, arguments.real_port, arguments.ssl, arguments.unique_depth, arguments.ignore_http_codes, arguments.ignore_content_length, arguments.wordlist)
4849

4950
scanner.scan()
51+
output = output_helper(scanner)
52+
53+
print(output.output_normal_likely())
54+
55+
if(arguments.output_normal):
56+
output.write_normal(arguments.output_normal)
57+
print("\n[+] Writing normal ouptut to %s" % arguments.output_normal)
5058

51-
print("\n[+] Most likely matches with a unique count of %s or less:" % arguments.unique_depth)
52-
for p in scanner.likely_matches(): print(" [>] %s" % p)
5359

5460
if __name__ == "__main__":
5561
main()

lib/core/discovered_host.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class discovered_host(object):
2+
"""
3+
Auxiliary class used for storing discovered hosts
4+
"""
5+
6+
def __init__(self):
7+
self.hostname = ''
8+
self.response_code = 0
9+
self.hash = ''
10+
self.keys = []

lib/core/virtual_host_scanner.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import requests
33
import hashlib
44
import pandas as pd
5+
from lib.core.discovered_host import *
6+
57

68
class virtual_host_scanner(object):
79
"""Virtual host scanning class
@@ -29,9 +31,14 @@ def __init__(self, target, base_host, port=80, real_port=80, ssl=False, unique_d
2931
self.unique_depth = unique_depth
3032
self.ssl = ssl
3133

34+
# this can be made redundant in future with better exceptions
3235
self.completed_scan=False
36+
37+
# this is maintained until likely-matches is refactored to use new class
3338
self.results = []
34-
39+
40+
# store associated data for discovered hosts in array for oN, oJ, etc'
41+
self.hosts = []
3542

3643
def scan(self):
3744
virtual_host_list = open(self.wordlist).read().splitlines()
@@ -65,15 +72,22 @@ def scan(self):
6572

6673
# hash the page results to aid in identifing unique content
6774
page_hash = hashlib.sha256(res.text.encode('utf-8')).hexdigest()
68-
output = '[#] Found: {} (code: {}, length: {}, hash: {})'.format(hostname, res.status_code,
69-
res.headers.get('content-length'), page_hash)
75+
output = '[#] Found: {} (code: {}, length: {}, hash: {})\n'.format(hostname, res.status_code,
76+
res.headers.get('content-length'), page_hash)
77+
host = discovered_host()
78+
host.hostname = hostname
79+
host.response_code = res.status_code
80+
host.hash = page_hash
7081

71-
# print current results
72-
print(output)
7382
for key, val in res.headers.items():
74-
output = ' {}: {}'.format(key, val)
75-
print(output)
83+
output += ' {}: {}\n'.format(key, val)
84+
host.keys.append('{}: {}'.format(key, val))
85+
86+
self.hosts.append(host)
7687

88+
# print current results so feedback remains in "realtime"
89+
print(output)
90+
7791
# add url and hash into array for likely matches
7892
self.results.append(hostname + ',' + page_hash)
7993

lib/helpers/file_helper.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import os
2+
3+
4+
class file_helper(object):
5+
"""description of class"""
6+
def __init__(self, output_file):
7+
self.output_file = output_file
8+
9+
def check_directory(self):
10+
directory = self.output_file
11+
try:
12+
os.stat(self.directory)
13+
except:
14+
os.mkdir(self.directory)
15+
print("[!] %s didn't exist and has been created." % output_directory)
16+
17+
# placeholder for error checking on -oJ implementation
18+
def is_json(json_file):
19+
try:
20+
with open(json_file, "r") as f:
21+
json_object = json.load(f)
22+
except ValueError:
23+
return False
24+
return True
25+
26+
def write_file(self, contents):
27+
with open(self.output_file, "w") as o:
28+
o.write(contents)

lib/helpers/output_helper.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from lib.core.discovered_host import *
2+
from lib.helpers.file_helper import *
3+
import time
4+
5+
class output_helper(object):
6+
def __init__(self, scanner):
7+
self.scanner = scanner
8+
9+
def write_normal(self, filename):
10+
11+
file = file_helper(filename)
12+
13+
# todo: finish check_directory (needs regex to split out filename)
14+
# file.check_directory(filename)
15+
file.write_file(self.generate_header() + self.output_normal_likely() + self.output_normal_detail())
16+
17+
def output_normal_likely(self):
18+
output = "\n[+] Most likely matches with a unique count of {} or less:".format(str(self.scanner.unique_depth))
19+
for p in self.scanner.likely_matches(): output += "\n\t[>] {}".format(p)
20+
21+
return output
22+
23+
def output_normal_detail(self):
24+
output = "\n\n[+] Full scan results"
25+
26+
for p in self.scanner.hosts:
27+
output += "\n\n{} (Code: {}) hash: {}".format(str(p.hostname), str(p.response_code), str(p.hash))
28+
for key in p.keys: output += "\n\t{}".format(key)
29+
30+
return output
31+
32+
def generate_header(self):
33+
output = "VHostScanner Log: {} {}\n".format(time.strftime("%d/%m/%Y"), time.strftime("%H:%M:%S"))
34+
output += "\tTarget: {}\n\tBase Host: {}\n\tPort: {}".format(self.scanner.target, self.scanner.base_host, self.scanner.port)
35+
output += "\n\tReal Port {}\n\tIgnore HTTP Codes: {}".format(self.scanner.real_port,self.scanner.ignore_http_codes)
36+
output += "\n\tIgnore Content Length: {}\n\tWordlist: {}".format(self.scanner.ignore_content_length, self.scanner.wordlist)
37+
output += "\n\tUnique Depth: {}\n\tSSL: {}\n\t".format(self.scanner.unique_depth, self.scanner.ssl)
38+
return output

0 commit comments

Comments
 (0)