-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 356dc4f
Showing
9 changed files
with
671 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.