mirror of
https://github.com/ChronosX88/PyNesca.git
synced 2024-11-24 14:02:17 +00:00
TCP scanner prototype added
This commit is contained in:
parent
346b52df9c
commit
825b89e62a
98
RawTCP.py
Normal file
98
RawTCP.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
from struct import pack, unpack
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
class TCPHeader():
|
||||||
|
# TCP header class. Thanks to Silver Moon for the flags calculation and packing order
|
||||||
|
# This was designed to be re-used. You might want to randomize the seq number
|
||||||
|
# get_struct performs packing based on if you have a valid checksum or not
|
||||||
|
def __init__(self, src_port=47123, dst_port=80, seqnum=1000, acknum=0, data_offset=80, fin=0, syn=1, rst=0, psh=0, ack=0, urg=0, window=5840, check=0, urg_ptr=0):
|
||||||
|
# !=network(big-endian), H=short(2), L=long(4),B=char(1)
|
||||||
|
self.order = "!HHLLBBHHH"
|
||||||
|
self.src_port = src_port
|
||||||
|
self.dst_port = dst_port
|
||||||
|
self.seqnum = seqnum
|
||||||
|
self.acknum = acknum
|
||||||
|
# size of tcp header; size is specified by 4-byte words; This is 80
|
||||||
|
# decimal, which is 0x50, which is 20bytes (5words*4bytes).
|
||||||
|
self.data_offset = data_offset
|
||||||
|
self.fin = fin
|
||||||
|
self.syn = syn
|
||||||
|
self.rst = rst
|
||||||
|
self.psh = psh
|
||||||
|
self.ack = ack
|
||||||
|
self.urg = urg
|
||||||
|
self.window = socket.htons(window)
|
||||||
|
self.check = check
|
||||||
|
self.urg_ptr = urg_ptr
|
||||||
|
|
||||||
|
def flags(self):
|
||||||
|
return self.fin + (self.syn << 1) + (self.rst << 2) + (self.psh << 3) + (self.ack << 4) + (self.urg << 5)
|
||||||
|
|
||||||
|
def get_struct(self, check=False, checksummed=False):
|
||||||
|
if check is not False:
|
||||||
|
self.check = check
|
||||||
|
if checksummed:
|
||||||
|
return pack('!HHLLBBH', self.src_port, self.dst_port, self.seqnum, self.acknum, self.data_offset, self.flags(), self.window) + pack('H', self.check) + pack('!H', self.urg_ptr)
|
||||||
|
else:
|
||||||
|
return pack(self.order, self.src_port, self.dst_port, self.seqnum, self.acknum, self.data_offset, self.flags(), self.window, self.check, self.urg_ptr)
|
||||||
|
|
||||||
|
|
||||||
|
def checksum(msg):
|
||||||
|
# Shoutout to Silver Moon @ binarytides for this checksum algo.
|
||||||
|
sum = 0
|
||||||
|
for i in range(0, len(msg), 2):
|
||||||
|
w = msg[i] + (msg[i + 1] << 8)
|
||||||
|
sum = sum + w
|
||||||
|
|
||||||
|
sum = (sum >> 16) + (sum & 0xffff)
|
||||||
|
sum = sum + (sum >> 16)
|
||||||
|
sum = ~sum & 0xffff
|
||||||
|
return sum
|
||||||
|
|
||||||
|
|
||||||
|
def tcp_checksum(source_ip, dest_ip, tcp_header, user_data=b''):
|
||||||
|
# Calculates the correct checksum for the tcp header
|
||||||
|
tcp_length = len(tcp_header) + len(user_data)
|
||||||
|
# This is an IP header w/ TCP as protocol.
|
||||||
|
ip_header = pack('!4s4sBBH', socket.inet_aton(source_ip), socket.inet_aton(
|
||||||
|
dest_ip), 0, socket.IPPROTO_TCP, tcp_length)
|
||||||
|
# Assemble the packet (IP Header + TCP Header + data, and then send it to
|
||||||
|
# checksum function)
|
||||||
|
packet = ip_header + tcp_header + user_data
|
||||||
|
return checksum(packet)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_packet(raw_packet):
|
||||||
|
# Now we need to unpack the packet. It will be an IP/TCP packet
|
||||||
|
# We are looking for SYN-ACKs from our SYN scan
|
||||||
|
# Fields to check: IP - src addr; TCP - src port, flags
|
||||||
|
# We want to pull out and compare only these three
|
||||||
|
# Heres the math for unpacking: B=1, H=2, L=4, 4s=4 (those are bytes)
|
||||||
|
packet = raw_packet[0]
|
||||||
|
# This is the IP header, not including any self.options OR THE DST ADDR.
|
||||||
|
# Normal length is 20!! Im parsing as little as possible
|
||||||
|
ip_header = unpack('!BBHHHBBH4s', packet[0:16])
|
||||||
|
# If there are any self.options, the length of the IP header will be >20. We
|
||||||
|
# dont care about self.options
|
||||||
|
ip_header_length = (ip_header[0] & 0xf) * 4
|
||||||
|
# This is the source address (position 8, or the first "4s" in our
|
||||||
|
# unpack)
|
||||||
|
src_addr = socket.inet_ntoa(ip_header[8])
|
||||||
|
|
||||||
|
# We had to get the proper IP Header length to find the TCP header
|
||||||
|
# offset.
|
||||||
|
tcp_header_raw = packet[ip_header_length:ip_header_length + 14]
|
||||||
|
# TCP header structure is pretty straight-forward. We want PORTS and
|
||||||
|
# FLAGS, so we partial unpack it
|
||||||
|
tcp_header = unpack('!HHLLBB', tcp_header_raw)
|
||||||
|
|
||||||
|
src_port = tcp_header[0] # self-explanatory
|
||||||
|
dst_port = tcp_header[1] # self-explanatory FIXME: notused
|
||||||
|
# We only care about syn-ack, which will be 18 (0x12)
|
||||||
|
flag = tcp_header[5]
|
||||||
|
|
||||||
|
if flag == 18:
|
||||||
|
return (src_addr, src_port)
|
||||||
|
else:
|
||||||
|
return None
|
89
TCPScan.py
Normal file
89
TCPScan.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
from RawTCP import TCPHeader, handle_packet, tcp_checksum
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class TCPScanner:
|
||||||
|
def __init__(self):
|
||||||
|
self.source_ips = {}
|
||||||
|
# TODO: options, threading, input/output queues
|
||||||
|
self.options = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def in_scope(self):
|
||||||
|
"""Check that IP is in scanning scope"""
|
||||||
|
# TODO
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tcp_listener(self):
|
||||||
|
# Raw socket listener for when send_raw_syn() is used. This will catch
|
||||||
|
# return SYN-ACKs
|
||||||
|
listen = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
|
||||||
|
while True:
|
||||||
|
# packet = ('E \x00(\x1f\xaa@\x00w\x06\x99w2\xe0\xc8\xa2\xa2\xf3\xac\x18\xdf\xb3\x00\x16\xb6\x80\xc1\xa0/\xa6=$P\x10\xce\xab\xd1\xe4\x00\x00', ('50.XXX.200.162', 0))
|
||||||
|
raw_packet = listen.recvfrom(65565)
|
||||||
|
ret = handle_packet(raw_packet)
|
||||||
|
if ret is None:
|
||||||
|
continue
|
||||||
|
src_addr, src_port = ret
|
||||||
|
if self.in_scope(src_addr) and src_port in self.ports:
|
||||||
|
self.output_queue.put((src_addr, src_port))
|
||||||
|
|
||||||
|
def send_raw_syn(self, dest_ip, dst_port):
|
||||||
|
# Use raw sockets to send a SYN packet.
|
||||||
|
# If you want, you could use the IP header assembled in the tcp_checksum
|
||||||
|
# function to have a fully custom TCP/IP stack
|
||||||
|
try:
|
||||||
|
# Using IPPROTO_TCP so the kernel will deal with the IP packet for us.
|
||||||
|
# Change to IPPROTO_IP if you want control of IP header as well
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
|
||||||
|
except Exception:
|
||||||
|
sys.stderr.write("Error creating socket in send_raw_syn\n")
|
||||||
|
return
|
||||||
|
if self.options.source == "auto":
|
||||||
|
# This gets the correct source IP. Just in case of multiple interfaces,
|
||||||
|
# it will pick the right one
|
||||||
|
src_addr = self.get_source_ip(dest_ip)
|
||||||
|
else:
|
||||||
|
src_addr = self.options.source
|
||||||
|
src_port = 54321
|
||||||
|
make_tcpheader = TCPHeader(src_port, dst_port)
|
||||||
|
tcp_header = make_tcpheader.get_struct()
|
||||||
|
packet = make_tcpheader.get_struct(check=tcp_checksum(
|
||||||
|
src_addr, dest_ip, tcp_header), checksummed=True)
|
||||||
|
try:
|
||||||
|
s.sendto(packet, (dest_ip, 0))
|
||||||
|
except Exception as e:
|
||||||
|
sys.stderr.write("Error utilizing raw socket in send_raw_syn: {}\n".format(e))
|
||||||
|
|
||||||
|
def get_source_ip(self, dst_addr):
|
||||||
|
# Credit: 131264/alexander from stackoverflow. This gets the correct IP for sending. Useful if you have multiple interfaces
|
||||||
|
# NOTE: This will send an additional packet for every single IP to confirm
|
||||||
|
# the route. (but just one packet)
|
||||||
|
try:
|
||||||
|
if dst_addr in self.source_ips:
|
||||||
|
return self.source_ips[dst_addr]
|
||||||
|
else:
|
||||||
|
self.source_ips[dst_addr] = [(s.connect((dst_addr, 53)), s.getsockname()[0], s.close(
|
||||||
|
)) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
|
||||||
|
return self.source_ips[dst_addr]
|
||||||
|
except Exception:
|
||||||
|
sys.stderr.write(
|
||||||
|
"Something went wrong in get_source_ip, results might be wrong\n")
|
||||||
|
|
||||||
|
|
||||||
|
def send_full_connect_syn(ip, port, timeout):
|
||||||
|
# Normal scan using socket to connect. Does 3-way handshack, then graceful
|
||||||
|
# teardown using FIN
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.settimeout(timeout)
|
||||||
|
except Exception as e:
|
||||||
|
sys.stderr.write("Error creating socket in send_full_connect_syn: {}\n".format(e))
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
s.connect((ip, port))
|
||||||
|
return True
|
||||||
|
s.close()
|
||||||
|
except Exception:
|
||||||
|
return False
|
Loading…
Reference in New Issue
Block a user