Skip to content

Commit

Permalink
New features: service detection and comma-separated ports (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwesthaus authored Apr 14, 2020
1 parent d6095da commit bab228e
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 12 deletions.
4 changes: 2 additions & 2 deletions scanners/host_up.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def run(targets, is_admin):
# TCP ACK to port 80, and an ICMP timestamp request
icmp_echo_targets_up = icmp_echo.run(targets, print_results=False)
targets_up.update(icmp_echo_targets_up)
tcp_syn_targets_up = tcp_privileged.syn.run(targets, [443], options = [1, 2, 3, 4, 5], fragment_size=None, print_results=False)
tcp_syn_targets_up, tcp_syn_ports_open = tcp_privileged.syn.run(targets, [443], options = [1, 2, 3, 4, 5], fragment_size=None, print_results=False)
targets_up.update(tcp_syn_targets_up)
tcp_ack_targets_up = tcp_privileged.ack.run(targets, [80], options = [2, 3, 4, 5], fragment_size=None, print_results=False)
targets_up.update(tcp_ack_targets_up)
Expand All @@ -20,7 +20,7 @@ def run(targets, is_admin):
# Unprivileged
# Use TCP connect() scan on two common ports so as to not look suspicious
check_tcp_ports = [80, 443]
tcp_connect_targets_up = tcp_connect.run(targets, check_tcp_ports, print_results=False)
tcp_connect_targets_up, tcp_connect_ports_open = tcp_connect.run(targets, check_tcp_ports, print_results=False)
targets_up.update(tcp_connect_targets_up)
ping_os_targets_up = ping_os.run(targets, print_results=False, verbose=False)
targets_up.update(ping_os_targets_up)
Expand Down
98 changes: 98 additions & 0 deletions scanners/service_detection/service_detection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Kyle Westhaus
from color import pcolor
import socket

HTTP_MSG = b'GET / HTTP/1.0\r\n\r\n'

SMB_MSG = b"\x00\x00\x00\xa4\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x08\x01\x40" \
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x06" \
b"\x00\x00\x01\x00\x00\x81\x00\x02\x50\x43\x20\x4e\x45\x54\x57\x4f" \
b"\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00\x02" \
b"\x4d\x49\x43\x52\x4f\x53\x4f\x46\x54\x20\x4e\x45\x54\x57\x4f\x52" \
b"\x4b\x53\x20\x31\x2e\x30\x33\x00\x02\x4d\x49\x43\x52\x4f\x53\x4f" \
b"\x46\x54\x20\x4e\x45\x54\x57\x4f\x52\x4b\x53\x20\x33\x2e\x30\x00" \
b"\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00\x02\x4c\x4d\x31\x2e" \
b"\x32\x58\x30\x30\x32\x00\x02\x53\x61\x6d\x62\x61\x00\x02\x4e\x54" \
b"\x20\x4c\x41\x4e\x4d\x41\x4e\x20\x31\x2e\x30\x00\x02\x4e\x54\x20" \
b"\x4c\x4d\x20\x30\x2e\x31\x32\x00"

def run(ports_open):
for target, ports in ports_open.items():
print(f'Service Results on Host {target}')
print(f'Port\tService')
for port in ports:
time.sleep(1)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
s.settimeout(2)
s.connect((str(target), port))
except:
# Port wasn't actually open or socket error, no service detected
print(f'{port}\tNo service detected')
s.close()
continue

try:
ssh_resp = s.recv(1024)
ssh_resp = str(ssh_resp.rstrip(b'\r\n'), 'ascii')
except:
# No response, not SSH, run other detections
pass
else:
# SSH, test response
if 'SSH' in ssh_resp:
print(f'{port}\t{ssh_resp}')
else:
print(f'{port}\tUnknown')
s.close()
continue

# Other detections
try:
s.send(HTTP_MSG)
except:
# Port wasn't actually open or socket error, no service detected
print(f'{port}\tNo service detected')
s.close()
continue

try:
http_resp = s.recv(1024)
except:
# No response, not HTTP, run other detections
pass
else:
if b'HTTP' in http_resp:
print(f'{port}\tHTTP')
else:
print(f'{port}\tUnknown')
s.close()
continue

# Other detections
try:
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
s.connect((str(target), port))
s.send(SMB_MSG)
except:
# Port wasn't actually open or socket error, no service detected
print(f'{port}\tNo service detected')
continue

try:
smb_resp = s.recv(1024)
except:
# No response, all detections failed
pass
else:
if b'SMB' in smb_resp:
print(f'{port}\tSamba')
continue

# Fallthrough, all detections failed
print(f'{port}\tUnknown')
s.close()

print('Service detection complete.')

2 changes: 1 addition & 1 deletion scanners/tcp_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def run(targets, port_range, print_results=True):
output_ports(open_targets, closed_targets, filtered_targets)
else:
log(open_targets, closed_targets, filtered_targets)
return up_hosts
return up_hosts, open_targets


def get_port_status(s, host, port):
Expand Down
2 changes: 1 addition & 1 deletion scanners/tcp_privileged/syn.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def run(targets, port_range, options, fragment_size, src_ip=None, print_results=
print_result(open_targets, closed_targets, filtered_targets, unexpected_targets)
else:
log(open_targets, closed_targets, filtered_targets, unexpected_targets)
return up_hosts
return up_hosts, open_targets


def print_result(open_targets, closed, filtered, unexpected):
Expand Down
2 changes: 1 addition & 1 deletion scanners/tcp_privileged/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def run(targets, ports, options, fragment_size, src_ip=None, print_results=True)
print_result(open_targets, closed_targets, filtered_targets, unexpected_targets)
else:
log(open_targets, closed_targets, filtered_targets, unexpected_targets)
return
return open_targets


def print_result(open_targets, closed, filtered, unexpected):
Expand Down
36 changes: 29 additions & 7 deletions urban_rain.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,26 @@
from scanners.util.host_parser import parse_hosts
from attacks import syn_attack
from scanners.os_detection import os_detection
from scanners.service_detection import service_detection
import subprocess
import socket
import re
import textwrap
import time

# Parser for port ranges
def parseNumRange(string):
m = re.match(r'(\d+)(?:-(\d+))?$', string)
if not m:
raise argparse.ArgumentTypeError(pcolor.color.ERROR + "'" + string + "' is not a range of numbers (e.g. '80-100')." + pcolor.color.CLEAR)
start = m.group(1)
end = m.group(2) or start
return list(range(int(start,10), int(end,10)+1))
m = re.match(r'(\d+)(?:,(\d+))*$', string)
if not m:
raise argparse.ArgumentTypeError(pcolor.color.ERROR + "'" + string + "' is not a range of numbers (e.g. '80-100')." + pcolor.color.CLEAR)
else:
return [int(group) for group in string.split(',')]
else:
start = m.group(1)
end = m.group(2) or start
return list(range(int(start,10), int(end,10)+1))

# Check if fragment size is multiple of 8
def fragmentSize(string):
Expand Down Expand Up @@ -105,6 +112,7 @@ def main():
parser.add_argument('-sF', action='store_true', help='run a privileged TCP FIN scan')
parser.add_argument('-sM', action='store_true', help='run a privileged TCP Maimon scan')
parser.add_argument('-sW', action='store_true', help='run a privileged TCP Window scan')
parser.add_argument('-sV', action='store_true', help='run service detection on open ports')

parser.add_argument('-f', '--fragmenter', type=fragmentSize, help='fragment privileged TCP scan')

Expand Down Expand Up @@ -144,6 +152,8 @@ def main():
else:
unpacked_targets.add(str(target))

ports_open = {}

# Run selected scan types (can be multiple)
if args.discovery == 'host' or args.discovery == 'both':
unpacked_targets = host_up.run(unpacked_targets, is_admin)
Expand All @@ -153,14 +163,16 @@ def main():
scantype_provided = 0
if args.sT:
scantype_provided = 1
tcp_connect.run(unpacked_targets, args.port_range,args.log)
tcp_connect_targets_up, tcp_connect_ports_open = tcp_connect.run(unpacked_targets, args.port_range,args.log)
ports_open.update(tcp_connect_ports_open)
if args.sU:
scantype_provided = 1
udp_connect.run(unpacked_targets, args.port_range, args.log)
if args.sS:
scantype_provided = 1
if is_admin:
syn.run(unpacked_targets, args.port_range, optionList, args.fragmenter, args.src_addr, args.log)
tcp_syn_targets_up, tcp_syn_ports_open = syn.run(unpacked_targets, args.port_range, optionList, args.fragmenter, args.src_addr, args.log)
ports_open.update(tcp_syn_ports_open)
else:
print(pcolor.color.WARNING + 'TCP SYN scan requires privileges, skipping' + pcolor.color.CLEAR)
if args.sA:
Expand Down Expand Up @@ -208,9 +220,19 @@ def main():
if args.sW:
scantype_provided = 1
if is_admin:
window.run(unpacked_targets, args.port_range, optionList, args.fragmenter, args.src_addr, args.log)
tcp_window_ports_open = window.run(unpacked_targets, args.port_range, optionList, args.fragmenter, args.src_addr, args.log)
ports_open.update(tcp_window_ports_open)
else:
print(pcolor.color.WARNING + 'TCP Window Scan requires privileges, skipping' + pcolor.color.CLEAR)
if args.sV:
scantype_provided = 1
if not (args.sT or args.sS or args.sW):
print(pcolor.color.WARNING + 'Service detection must be paired with either a TCP Connect, TCP SYN, or TCP Window scan, skipping' + pcolor.color.CLEAR)
elif len(ports_open) == 0:
print(pcolor.color.WARNING + 'No open ports passed to service detection, skipping' + pcolor.color.CLEAR)
else:
time.sleep(1)
service_detection.run(ports_open)

if scantype_provided == 0:
print(pcolor.color.WARNING + 'Port scan requested but no scan type provided, skipping' + pcolor.color.CLEAR)
Expand Down

0 comments on commit bab228e

Please sign in to comment.