Skip to content

Commit 5111bea

Browse files
authored
Merge pull request #22 from codingo/codingo-stdin-wordlists
Added support for piping in wordlists using stdin
2 parents 7adead3 + b709a21 commit 5111bea

File tree

4 files changed

+61
-24
lines changed

4 files changed

+61
-24
lines changed

Diff for: README.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,26 @@ A virtual host scanner that can pivot over hosts, detect catch-all scenarios, al
3333
![VHOSTScan Feature Map](https://github.com/codingo/codingo.github.io/blob/master/assets/featureMap.PNG)
3434

3535
## Examples
36-
36+
### Quick Example
3737
The most straightforward example runs the default wordlist against example.com using the default of port 80:
3838

3939
```bash
4040
$ VHostScan.py -t example.com
4141
```
42-
42+
### Port forwarding
4343
Say you have an SSH port forward listening on port 4444 fowarding traffic to port 80 on example.com's development machine. You could use the following to make VHostScan connect through your SSH tunnel via localhost:4444 but format the header requests to suit connecting straight to port 80:
4444

4545
```bash
4646
$ VHostScan.py -t localhost -b example.com -p 4444 -r 80
4747
```
48+
49+
### STDIN
50+
If you want to pipe information into VHostScan you can use the ```-``` flag:
51+
```bash
52+
$ cat vhostname | VHostScan.py -t localhost -
53+
```
54+
### STDIN and WordList
55+
You can still specify a wordlist to use along with stdin. In these cases wordlist information will be appended to stdin. For example:
56+
```bash
57+
$ cat vhostname | VhostScan.py -t localhost -w ./wordlists/wordlist.txt -
58+
```

Diff for: VHostScan.py

+32-12
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

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

@@ -17,7 +17,7 @@ def main():
1717
print_banner()
1818
parser = ArgumentParser()
1919
parser.add_argument("-t", dest="target_hosts", required=True, help="Set a target range of addresses to target. Ex 10.11.1.1-255" )
20-
parser.add_argument("-w", dest="wordlist", required=False, type=str, help="Set the wordlist to use (default ./wordlists/virtual-host-scanning.txt)", default="./wordlists/virtual-host-scanning.txt")
20+
parser.add_argument("-w", dest="wordlist", required=False, type=str, help="Set the wordlist to use (default ./wordlists/virtual-host-scanning.txt)")
2121
parser.add_argument("-b", dest="base_host", required=False, help="Set host to be used during substitution in wordlist (default to TARGET).", default=False)
2222
parser.add_argument("-p", dest="port", required=False, help="Set the port to use (default 80).", default=80)
2323
parser.add_argument("-r", dest="real_port", required=False, help="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).", default=False)
@@ -27,15 +27,34 @@ def main():
2727
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)
2828
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)
2929
parser.add_argument("-oN", dest="output_normal", help="Normal output printed to a file when the -oN option is specified with a filename argument." )
30-
arguments = parser.parse_args()
31-
32-
if not os.path.exists(arguments.wordlist):
33-
print("[!] Wordlist %s doesn't exist, ending scan." % arguments.wordlistt)
34-
sys.exit()
35-
36-
print("[+] Starting virtual host scan for %s using port %s and wordlist %s" % (arguments.target_hosts,
37-
str(arguments.port),
38-
arguments.wordlist))
30+
parser.add_argument("-", dest="stdin", action="store_true", help="By passing a blank '-' you tell VHostScan to expect input from stdin (pipe).", default=False)
31+
32+
arguments = parser.parse_args()
33+
wordlist = list()
34+
35+
if(arguments.stdin and not arguments.wordlist):
36+
input = list(line for line in sys.stdin.read().splitlines())
37+
wordlist.extend(input)
38+
print("[+] Starting virtual host scan for %s using port %s and stdin data" % (arguments.target_hosts,
39+
str(arguments.port)))
40+
elif(arguments.stdin and arguments.wordlist):
41+
if not os.path.exists(arguments.wordlist):
42+
print("[!] Wordlist %s doesn't exist and can't be appended to stdin." % arguments.wordlist)
43+
print("[+] Starting virtual host scan for %s using port %s and stdin data" % (arguments.target_hosts,
44+
str(arguments.port)))
45+
else:
46+
wordlist_file = open(arguments.wordlist).read().splitlines()
47+
wordlist.extend(wordlist_file)
48+
print("[+] Starting virtual host scan for %s using port %s, stdin data, and wordlist %s" % (arguments.target_hosts,
49+
str(arguments.port),
50+
arguments.wordlist))
51+
else:
52+
# if no stdin, or wordlist pass, open default wordlist location
53+
wordlist_file = open("./wordlists/virtual-host-scanning.txt").read().splitlines()
54+
wordlist.extend(wordlist_file)
55+
print("[+] Starting virtual host scan for %s using port %s and wordlist %s" % (arguments.target_hosts,
56+
str(arguments.port),
57+
"./wordlists/virtual-host-scanning.txt"))
3958

4059
if(arguments.ssl):
4160
print("[>] SSL flag set, sending all results over HTTPS")
@@ -45,7 +64,8 @@ def main():
4564
if(arguments.ignore_content_length > 0):
4665
print("[>] Ignoring Content length: %s" % (arguments.ignore_content_length))
4766

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)
67+
scanner = virtual_host_scanner( arguments.target_hosts, arguments.base_host, wordlist, arguments.port, arguments.real_port, arguments.ssl,
68+
arguments.unique_depth, arguments.ignore_http_codes, arguments.ignore_content_length)
4969

5070
scanner.scan()
5171
output = output_helper(scanner)

Diff for: lib/core/virtual_host_scanner.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ class virtual_host_scanner(object):
1919
output: folder to write output file to
2020
"""
2121

22-
def __init__(self, target, base_host, port=80, real_port=80, ssl=False, unique_depth=1, ignore_http_codes='404', ignore_content_length=0,
23-
wordlist="./wordlists/virtual-host-scanning.txt"):
22+
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):
2423
self.target = target
2524
self.base_host = base_host
2625
self.port = int(port)
@@ -40,16 +39,15 @@ def __init__(self, target, base_host, port=80, real_port=80, ssl=False, unique_d
4039
# store associated data for discovered hosts in array for oN, oJ, etc'
4140
self.hosts = []
4241

43-
def scan(self):
44-
virtual_host_list = open(self.wordlist).read().splitlines()
4542

43+
def scan(self):
4644
if not self.base_host:
4745
self.base_host = self.target
4846

4947
if not self.real_port:
5048
self.real_port = self.port
5149

52-
for virtual_host in virtual_host_list:
50+
for virtual_host in self.wordlist:
5351
hostname = virtual_host.replace('%s', self.base_host)
5452

5553
headers = {

Diff for: lib/helpers/output_helper.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,18 @@ def write_normal(self, filename):
1515
file.write_file(self.generate_header() + self.output_normal_likely() + self.output_normal_detail())
1616

1717
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
18+
uniques = False
19+
depth = str(self.scanner.unique_depth)
20+
output = "\n[+] Most likely matches with a unique count of {} or less:".format(depth)
21+
22+
for p in self.scanner.likely_matches():
23+
output += "\n\t[>] {}".format(p)
24+
uniques = True
25+
26+
if(uniques):
27+
return output
28+
else:
29+
return "\n[!] No matches with a unique count of {} or less.".format(depth)
2230

2331
def output_normal_detail(self):
2432
output = "\n\n[+] Full scan results"
@@ -35,4 +43,4 @@ def generate_header(self):
3543
output += "\n\tReal Port {}\n\tIgnore HTTP Codes: {}".format(self.scanner.real_port,self.scanner.ignore_http_codes)
3644
output += "\n\tIgnore Content Length: {}\n\tWordlist: {}".format(self.scanner.ignore_content_length, self.scanner.wordlist)
3745
output += "\n\tUnique Depth: {}\n\tSSL: {}\n\t".format(self.scanner.unique_depth, self.scanner.ssl)
38-
return output
46+
return output

0 commit comments

Comments
 (0)