Skip to content

Commit

Permalink
added basic tools
Browse files Browse the repository at this point in the history
  • Loading branch information
safakun committed Sep 14, 2023
0 parents commit 356dc4f
Show file tree
Hide file tree
Showing 9 changed files with 671 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Simple TCP proxy written in Python

### To find a local host to listen
```bash
ifconfig
```
post should be 192.168.*.*

sudo ./proxy.py 127.0.0.1 21 ftp.target.ca 21 True
176 changes: 176 additions & 0 deletions proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import sys
import socket
import threading


# this is a pretty hex dumping function directly taken from
# http://code.activestate.com/recipes/142812-hex-dumper/

def hexdump(src, length=16):
result = []
digits = 4 if isinstance(src, str) else 2

for i in range(0, len(src), length):
s = src[i:i + length]
hexa = b' '.join([b"%0*X" % (digits, ord(x)) for x in s])
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
result.append(
b"%04X %-*s %s" % (i, length * (digits + 1), hexa, text))

print(b'\n'.join(result))


def receive_from(connection):
buffer = b''

# We set a 2 second time-out. Depending on your target this may need
# to be adjusted
connection.settimeout(2)

try:

# keep reading into the buffer until there's no more data or we
# time-out
while True:
data = connection.recv(4096)
if not data:
break
buffer += data

except TimeoutError:
pass

return buffer


# modify any requests destined for the remote host
def request_handler(buffer):
# perform packet modifications
return buffer


# modify any responses destined for the local host
def response_handler(buffer):
# perform packet modifications
return buffer


def proxy_handler(client_socket, remote_host, remote_port, receive_first):
# connect to the remote host
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_socket.connect((remote_host, remote_port))

# receive data from the remote end if necessary
if receive_first:
remote_buffer = receive_from(remote_socket)
hexdump(remote_buffer)

# send it to our response handler
remote_buffer = response_handler(remote_buffer)

# if we have data to send to our local client send it
if len(remote_buffer):
print("[<==] Sending %d bytes to localhost." % len(remote_buffer))
client_socket.send(remote_buffer)

# now let's loop and read from local, send to remote, send to local
# rinse wash repeat
while True:
# read from local host
local_buffer = receive_from(client_socket)

if len(local_buffer):
print("[==>] Received %d bytes from localhost." % len(local_buffer))
hexdump(local_buffer)

# send it to our request handler
local_buffer = request_handler(local_buffer)

# send off the data to the remote host
remote_socket.send(local_buffer)
print("[==>] Sent to remote.")

# receive back the response
remote_buffer = receive_from(remote_socket)

if len(remote_buffer):
print("[<==] Received %d bytes from remote." % len(remote_buffer))
hexdump(remote_buffer)

# send to our response handler
remote_buffer = response_handler(remote_buffer)

# send the response to the local socket
client_socket.send(remote_buffer)

print("[<==] Sent to localhost.")

# if no more data on either side close the connections
if not len(local_buffer) or not len(remote_buffer):
client_socket.close()
remote_socket.close()
print("[*] No more data. Closing connections.")
break


def server_loop(local_host, local_port, remote_host, remote_port,
receive_first):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
server.bind((local_host, local_port))
except socket.error as exc:
print("[!!] Failed to listen on %s:%d" % (local_host,
local_port))
print("[!!] Check for other listening sockets or correct "
"permissions.")
print(f"[!!] Caught exception error: {exc}")
sys.exit(0)

print("[*] Listening on %s:%d" % (local_host, local_port))

server.listen(5)

while True:
client_socket, addr = server.accept()

# print out the local connection information
print("[==>] Received incoming connection from %s:%d" % (
addr[0], addr[1]))

# start a thread to talk to the remote host
proxy_thread = threading.Thread(target=proxy_handler, args=(
client_socket, remote_host, remote_port, receive_first))
proxy_thread.start()


def main():
# no fancy command line parsing here
if len(sys.argv[1:]) != 5:
print("Usage: ./proxy.py [localhost] [localport] [remotehost] "
"[remoteport] [receive_first]")
print("Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True")
sys.exit(0)

# setup local listening parameters
local_host = sys.argv[1]
local_port = int(sys.argv[2])

# setup remote target
remote_host = sys.argv[3]
remote_port = int(sys.argv[4])

# this tells our proxy to connect and receive data
# before sending to the remote host
receive_first = sys.argv[5]

if "True" in receive_first:
receive_first = True
else:
receive_first = False

# now spin up our listening socket
server_loop(local_host, local_port, remote_host, remote_port, receive_first)


main()
145 changes: 145 additions & 0 deletions scanner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import socket
import os
import struct
import threading
from ipaddress import ip_address, ip_network
from ctypes import *

# host to listen on
host = "192.168.43.66"

# subnet to target
tgt_subnet = "192.168.43.0/24"

# magic we'll check ICMP responses for
tgt_message = "PYTHONRULES!"


def udp_sender(sub_net, magic_message):
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

for ip in ip_network(sub_net).hosts():
sender.sendto(magic_message.encode('utf-8'), (str(ip), 65212))


class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4),
("version", c_ubyte, 4),
("tos", c_ubyte),
("len", c_ushort),
("id", c_ushort),
("offset", c_ushort),
("ttl", c_ubyte),
("protocol_num", c_ubyte),
("sum", c_ushort),
("src", c_uint32),
("dst", c_uint32)
]

def __new__(cls, socket_buffer=None):
return cls.from_buffer_copy(socket_buffer)

def __init__(self, socket_buffer=None):
self.socket_buffer = socket_buffer

# map protocol constants to their names
self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}

# human readable IP addresses
self.src_address = socket.inet_ntoa(struct.pack("@I", self.src))
self.dst_address = socket.inet_ntoa(struct.pack("@I", self.dst))

# human readable protocol
try:
self.protocol = self.protocol_map[self.protocol_num]
except IndexError:
self.protocol = str(self.protocol_num)


class ICMP(Structure):
_fields_ = [
("type", c_ubyte),
("code", c_ubyte),
("checksum", c_ushort),
("unused", c_ushort),
("next_hop_mtu", c_ushort)
]

def __new__(cls, socket_buffer):
return cls.from_buffer_copy(socket_buffer)

def __init__(self, socket_buffer):
self.socket_buffer = socket_buffer


# create a raw socket and bind it to the public interface
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# we want the IP headers included in the capture
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# if we're on Windows we need to send some ioctl
# to setup promiscuous mode
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# start sending packets
t = threading.Thread(target=udp_sender, args=(tgt_subnet, tgt_message))
t.start()

try:
while True:

# read in a single packet
raw_buffer = sniffer.recvfrom(65535)[0]

# create an IP header from the first 20 bytes of the buffer
ip_header = IP(raw_buffer[:20])

print("Protocol: %s %s -> %s" % (
ip_header.protocol,
ip_header.src_address,
ip_header.dst_address)
)

# if it's ICMP we want it
if ip_header.protocol == "ICMP":

# calculate where our ICMP packet starts
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + sizeof(ICMP)]

# create our ICMP structure
icmp_header = ICMP(buf)

print("ICMP -> Type: %d Code: %d" % (
icmp_header.type,
icmp_header.code)
)

# now check for the TYPE 3 and CODE 3 which indicates
# a host is up but no port available to talk to
if icmp_header.code == 3 and icmp_header.type == 3:

# check to make sure we are receiving the response
# that lands in our subnet
if ip_address(ip_header.src_address) in ip_network(tgt_subnet):

# test for our magic message
if raw_buffer[len(raw_buffer)
- len(tgt_message):] == tgt_message:
print("Host Up: %s" % ip_header.src_address)

# handle CTRL-C
except KeyboardInterrupt:
# if we're on Windows turn off promiscuous mode
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
30 changes: 30 additions & 0 deletions sniffer_basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import socket
import os

# host to listen on
host = "192.168.43.66"

# create a raw socket and bind it to the public interface
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# we want the IP headers included in the capture
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# if we're on Windows we need to send an IOCTL
# to setup promiscuous mode
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# read in a single packet
print(sniffer.recvfrom(65535))

# if we're on Windows turn off promiscuous mode
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
Loading

0 comments on commit 356dc4f

Please sign in to comment.