Skip to content

Added Output Normal Functionality #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Sep 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# VIM temporary files
*.pyc

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
Expand Down Expand Up @@ -103,4 +105,3 @@ ENV/
*.pyproj
*.sln
*.sqlite
*.json
16 changes: 11 additions & 5 deletions VHostScan.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import sys
from argparse import ArgumentParser
from lib.core.virtual_host_scanner import *
from lib.helpers.output_helper import *


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

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

if not os.path.exists(arguments.wordlist):
Expand All @@ -43,13 +45,17 @@ 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, arguments.port, arguments.real_port, arguments.ssl, arguments.unique_depth,
arguments.ignore_http_codes, arguments.ignore_content_length, arguments.wordlist)
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)

scanner.scan()
output = output_helper(scanner)

print(output.output_normal_likely())

if(arguments.output_normal):
output.write_normal(arguments.output_normal)
print("\n[+] Writing normal ouptut to %s" % arguments.output_normal)

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

if __name__ == "__main__":
main()
10 changes: 10 additions & 0 deletions lib/core/discovered_host.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class discovered_host(object):
"""
Auxiliary class used for storing discovered hosts
"""

def __init__(self):
self.hostname = ''
self.response_code = 0
self.hash = ''
self.keys = []
28 changes: 21 additions & 7 deletions lib/core/virtual_host_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import requests
import hashlib
import pandas as pd
from lib.core.discovered_host import *


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

# this can be made redundant in future with better exceptions
self.completed_scan=False

# this is maintained until likely-matches is refactored to use new class
self.results = []


# store associated data for discovered hosts in array for oN, oJ, etc'
self.hosts = []

def scan(self):
virtual_host_list = open(self.wordlist).read().splitlines()
Expand Down Expand Up @@ -65,15 +72,22 @@ def scan(self):

# hash the page results to aid in identifing unique content
page_hash = hashlib.sha256(res.text.encode('utf-8')).hexdigest()
output = '[#] Found: {} (code: {}, length: {}, hash: {})'.format(hostname, res.status_code,
res.headers.get('content-length'), page_hash)
output = '[#] Found: {} (code: {}, length: {}, hash: {})\n'.format(hostname, res.status_code,
res.headers.get('content-length'), page_hash)
host = discovered_host()
host.hostname = hostname
host.response_code = res.status_code
host.hash = page_hash

# print current results
print(output)
for key, val in res.headers.items():
output = ' {}: {}'.format(key, val)
print(output)
output += ' {}: {}\n'.format(key, val)
host.keys.append('{}: {}'.format(key, val))

self.hosts.append(host)

# print current results so feedback remains in "realtime"
print(output)

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

Expand Down
28 changes: 28 additions & 0 deletions lib/helpers/file_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os


class file_helper(object):
"""description of class"""
def __init__(self, output_file):
self.output_file = output_file

def check_directory(self):
directory = self.output_file
try:
os.stat(self.directory)
except:
os.mkdir(self.directory)
print("[!] %s didn't exist and has been created." % output_directory)

# placeholder for error checking on -oJ implementation
def is_json(json_file):
try:
with open(json_file, "r") as f:
json_object = json.load(f)
except ValueError:
return False
return True

def write_file(self, contents):
with open(self.output_file, "w") as o:
o.write(contents)
38 changes: 38 additions & 0 deletions lib/helpers/output_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from lib.core.discovered_host import *
from lib.helpers.file_helper import *
import time

class output_helper(object):
def __init__(self, scanner):
self.scanner = scanner

def write_normal(self, filename):

file = file_helper(filename)

# todo: finish check_directory (needs regex to split out filename)
# file.check_directory(filename)
file.write_file(self.generate_header() + self.output_normal_likely() + self.output_normal_detail())

def output_normal_likely(self):
output = "\n[+] Most likely matches with a unique count of {} or less:".format(str(self.scanner.unique_depth))
for p in self.scanner.likely_matches(): output += "\n\t[>] {}".format(p)

return output

def output_normal_detail(self):
output = "\n\n[+] Full scan results"

for p in self.scanner.hosts:
output += "\n\n{} (Code: {}) hash: {}".format(str(p.hostname), str(p.response_code), str(p.hash))
for key in p.keys: output += "\n\t{}".format(key)

return output

def generate_header(self):
output = "VHostScanner Log: {} {}\n".format(time.strftime("%d/%m/%Y"), time.strftime("%H:%M:%S"))
output += "\tTarget: {}\n\tBase Host: {}\n\tPort: {}".format(self.scanner.target, self.scanner.base_host, self.scanner.port)
output += "\n\tReal Port {}\n\tIgnore HTTP Codes: {}".format(self.scanner.real_port,self.scanner.ignore_http_codes)
output += "\n\tIgnore Content Length: {}\n\tWordlist: {}".format(self.scanner.ignore_content_length, self.scanner.wordlist)
output += "\n\tUnique Depth: {}\n\tSSL: {}\n\t".format(self.scanner.unique_depth, self.scanner.ssl)
return output