-
-
Save WangYihang/e7d36b744557e4673d2157499f6c6b5e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
# Tcp Port Forwarding (Reverse Proxy) | |
# Author : WangYihang <wangyihanger@gmail.com> | |
''' | |
+-----------------------------+ +---------------------------------------------+ +--------------------------------+ | |
| My Laptop (Alice) | | Intermediary Server (Bob) | | Internal Server (Carol) | | |
+-----------------------------+ +----------------------+----------------------+ +--------------------------------+ | |
| $ ssh -p 1022 carol@1.2.3.4 |<------->| IF 1: 1.2.3.4 | IF 2: 192.168.1.1 |<------->| IF 1: 192.168.1.2 | | |
| carol@1.2.3.4's password: | +----------------------+----------------------+ +--------------------------------+ | |
| carol@hostname:~$ whoami | | $ python pf.py --listen-host 1.2.3.4 \ | | 192.168.1.2:22(OpenSSH Server) | | |
| carol | | --listen-port 1022 \ | +--------------------------------+ | |
+-----------------------------+ | --connect-host 192.168.1.2 \ | | |
| --connect-port 22 | | |
+---------------------------------------------+ | |
''' | |
import socket | |
import threading | |
import argparse | |
import logging | |
format = '%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s: %(message)s' | |
logging.basicConfig(level=logging.INFO, format=format) | |
def handle(buffer, direction, src_address, src_port, dst_address, dst_port): | |
''' | |
intercept the data flows between local port and the target port | |
''' | |
if direction: | |
logging.debug(f"{src_address, src_port} -> {dst_address, dst_port} {len(buffer)} bytes") | |
else: | |
logging.debug(f"{src_address, src_port} <- {dst_address, dst_port} {len(buffer)} bytes") | |
return buffer | |
def transfer(src, dst, direction): | |
src_address, src_port = src.getsockname() | |
dst_address, dst_port = dst.getsockname() | |
while True: | |
try: | |
buffer = src.recv(4096) | |
if len(buffer) == 0: | |
break | |
dst.send(handle(buffer, direction, src_address, src_port, dst_address, dst_port)) | |
except Exception as e: | |
logging.error(repr(e)) | |
break | |
logging.warning(f"Closing connect {src_address, src_port}! ") | |
src.close() | |
logging.warning(f"Closing connect {dst_address, dst_port}! ") | |
dst.close() | |
def server(local_host, local_port, remote_host, remote_port): | |
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
server_socket.bind((local_host, local_port)) | |
server_socket.listen(0x40) | |
logging.info(f"Server started {local_host, local_port}") | |
logging.info(f"Connect to {local_host, local_port} to get the content of {remote_host, remote_port}") | |
while True: | |
src_socket, src_address = server_socket.accept() | |
logging.info(f"[Establishing] {src_address} -> {local_host, local_port} -> ? -> {remote_host, remote_port}") | |
try: | |
dst_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
dst_socket.connect((remote_host, remote_port)) | |
logging.info(f"[OK] {src_address} -> {local_host, local_port} -> {dst_socket.getsockname()} -> {remote_host, remote_port}") | |
s = threading.Thread(target=transfer, args=(dst_socket, src_socket, False)) | |
r = threading.Thread(target=transfer, args=(src_socket, dst_socket, True)) | |
s.start() | |
r.start() | |
except Exception as e: | |
logging.error(repr(e)) | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument("--listen-host", help="the host to listen", required=True) | |
parser.add_argument("--listen-port", type=int, help="the port to bind", required=True) | |
parser.add_argument("--connect-host", help="the target host to connect", required=True) | |
parser.add_argument("--connect-port", type=int, help="the target port to connect", required=True) | |
args = parser.parse_args() | |
server(args.listen_host, args.listen_port, | |
args.connect_host, args.connect_port) | |
if __name__ == "__main__": | |
main() |
Thanks
If you're looking for ruby implementation for port forwarding (ruby-port-forward)
👍
if am using this as a tunnel connection between two, how can i print the input queries to this local address and port in the logs?
@prateek-mgh, thank you for asking, would please elaborate 'input queries', did you mean income connection?
yes i was trying to log the income connections with the queries.
Am able to do now but looking forward for your input how would you do that
Also i noticed that though the script shows closing connection, it actually doesn't drop the connection, i guess this can depend upon target client to which it is forwarding to
can i use this as a traffic bot?
Thanks for the code @WangYihang .
I feel F-strings are more readable, so I changed your string formatting (% operator) to F-strings.
But it makes the code incompatible with Python releases before 3.6
For anyone interested:
https://gist.github.com/amnkh/879caa4a82c513503d368f1d56643f11
THANKS FOR YOUR HELP!!
I got a flag from my ctf using your code! Amazing work!
can i use this for remote debugging where my application will be running on a non-static ip and the debugger will connect from a static ip?
Hello, I am getting an error like this:
[+] Detect connection from [127.0.0.1:43122] [+] Trying to connect the REMOTE server [127.0.0.1:45825] [+] Tunnel connected! Tranfering data... [+] 127.0.0.1:8000 >>> 127.0.0.1:38794 [912] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [17] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [336] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [1024] [+] 127.0.0.1:8000 <<< 127.0.0.1:38794 [918] [-] No data received! Breaking... [+] Closing connecions! [127.0.0.1:38794] [+] Closing connecions! [127.0.0.1:8000] [-] No data received! Breaking... [+] Closing connecions! [127.0.0.1:8000] Exception in thread Thread-34: Traceback (most recent call last): File "/usr/lib64/python2.7/threading.py", line 801, in __bootstrap_inner self.run() File "/usr/lib64/python2.7/threading.py", line 754, in run self.__target(*self.__args, **self.__kwargs) File "/home/..../port_forward.py", line 30, in transfer src.shutdown(socket.SHUT_RDWR) File "/usr/lib64/python2.7/socket.py", line 228, in meth return getattr(self._sock,name)(*args) File "/usr/lib64/python2.7/socket.py", line 174, in _dummy raise error(EBADF, 'Bad file descriptor') error: [Errno 9] Bad file descriptor
Do you know how to fix it?
my guy leaked his own ip
This has been most used script by SYS-Admins so far.
Change print command to print function to get it working with python3.
print "[-] No data received! Breaking..." will become print("[-] No data received! Breaking...")
Sorry I have little experience with sockets, what would I put for L_HOST L_PORT R_HOST R_PORT?
@sudocpMATHdotPY
After running python port-forwarding.py 0.0.0.0 1022 192.168.1.1 22
, the script will listen on 0.0.0.0:1022
.
Suppose the script is running on some machine that has a public IP address (1.2.3.4), then if you try to connect 1.2.3.4:1022
as if you communicating with 192.168.1.1:22
.
@WangYihang Please change print into function so that it can also work with python3 version.
@shubham2110 thanks for your advice, done.
@WangYihang
which ip address is public and which is private?
This diagram should be able to make it easy to understand. @sudocpMATHdotPY
hello.
Tried to use mssql to port forwarding the connection. The connection is in progress. The program communicates with the base, there is no call to the "transfer" function. Why?
I wanted to see queries to the database, and see the response from the database. Tell me how can this be done?
I wanted to see queries to the database, and see the response from the database. Tell me how can this be done?
Modify function handle
to meet your requirements.
def handle(buffer, direction, src_address, src_port, dst_address, dst_port):
'''
intercept the data flows between local port and the target port
'''
if direction:
logging.info(f"{src_address, src_port} -> {dst_address, dst_port} {len(buffer)} bytes")
logging.info(buffer)
else:
logging.info(f"{src_address, src_port} <- {dst_address, dst_port} {len(buffer)} bytes")
logging.info(buffer)
return buffer
As the following image shows.
Or you can use the Python package hexdump to view the hexadecimal data, just like the following image shows.
pip install hexdump
21a22
> import hexdump
33c34,35
< logging.debug(f"{src_address, src_port} -> {dst_address, dst_port} {len(buffer)} bytes")
---
> logging.info(f"{src_address, src_port} -> {dst_address, dst_port} {len(buffer)} bytes")
> hexdump.hexdump(buffer)
35c37,38
< logging.debug(f"{src_address, src_port} <- {dst_address, dst_port} {len(buffer)} bytes")
---
> logging.info(f"{src_address, src_port} <- {dst_address, dst_port} {len(buffer)} bytes")
> hexdump.hexdump(buffer)
91d93
<
The program communicates with the base, there is no call to the "transfer" function. Why?
See line 70-71, the target function of threading.Thread
is function transfer
.
In addition, please refer to the document of the Python threading package.
target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called.
Hello everyone,
I really like port forwarding and what you can do with it.
Here is my Python port forwarding version that I've made recently, inspired by @wangyihan script :D
- Using socket & Asyncio:
https://github.com/medram/port-forwarding/blob/with-asyncio/port_forwarding.py - Using sockets & Selectors:
https://github.com/medram/port-forwarding/blob/with-selectors/port_forwarding.py
I hope you like it.
By the way, it support HTTP Custom & HTTP injection (for fake Websocket upgrade reply).
what about udp ?
usage: 213.py [-h] --listen-host LISTEN_HOST --listen-port LISTEN_PORT
--connect-host CONNECT_HOST --connect-port CONNECT_PORT
213.py: error: the following arguments are required: --listen-host, --listen-port, --connect-host, --connect-port
Help, how to fix that?
line 45, if len(buffer) == 0, there would be endless loop
@ruanhao thanks for pointing out this bug, fixed.
Why do we need --listen-host parameter ? 127.0.0.1 won't always do ?
Why do we need --listen-host parameter ? 127.0.0.1 won't always do ?
there is need for 0.0.0.0
since this program repeatedly close to socket in transfer function, so the easy way to fix it is just delete or comment line 37 to line 39. have fun with it