Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
how-to-build-a-tcp-proxy/tcp_proxy.py /
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
154 lines (126 sloc)
4.33 KB
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
| from twisted.internet import protocol, reactor | |
| from twisted.internet import ssl as twisted_ssl | |
| import dns.resolver | |
| import netifaces as ni | |
| # Adapted from http://stackoverflow.com/a/15645169/221061 | |
| class TCPProxyProtocol(protocol.Protocol): | |
| """ | |
| TCPProxyProtocol listens for TCP connections from a | |
| client (eg. a phone) and forwards them on to a | |
| specified destination (eg. an app's API server) over | |
| a second TCP connection, using a ProxyToServerProtocol. | |
| It assumes that neither leg of this trip is encrypted. | |
| """ | |
| def __init__(self): | |
| self.buffer = None | |
| self.proxy_to_server_protocol = None | |
| def connectionMade(self): | |
| """ | |
| Called by twisted when a client connects to the | |
| proxy. Makes an connection from the proxy to the | |
| server to complete the chain. | |
| """ | |
| print("Connection made from CLIENT => PROXY") | |
| proxy_to_server_factory = protocol.ClientFactory() | |
| proxy_to_server_factory.protocol = ProxyToServerProtocol | |
| proxy_to_server_factory.server = self | |
| reactor.connectTCP(DST_IP, DST_PORT, | |
| proxy_to_server_factory) | |
| def dataReceived(self, data): | |
| """ | |
| Called by twisted when the proxy receives data from | |
| the client. Sends the data on to the server. | |
| CLIENT ===> PROXY ===> DST | |
| """ | |
| print("") | |
| print("CLIENT => SERVER") | |
| print(FORMAT_FN(data)) | |
| print("") | |
| if self.proxy_to_server_protocol: | |
| self.proxy_to_server_protocol.write(data) | |
| else: | |
| self.buffer = data | |
| def write(self, data): | |
| self.transport.write(data) | |
| class ProxyToServerProtocol(protocol.Protocol): | |
| """ | |
| ProxyToServerProtocol connects to a server over TCP. | |
| It sends the server data given to it by an | |
| TCPProxyProtocol, and uses the TCPProxyProtocol to | |
| send data that it receives back from the server on | |
| to a client. | |
| """ | |
| def connectionMade(self): | |
| """ | |
| Called by twisted when the proxy connects to the | |
| server. Flushes any buffered data on the proxy to | |
| server. | |
| """ | |
| print("Connection made from PROXY => SERVER") | |
| self.factory.server.proxy_to_server_protocol = self | |
| self.write(self.factory.server.buffer) | |
| self.factory.server.buffer = '' | |
| def dataReceived(self, data): | |
| """ | |
| Called by twisted when the proxy receives data | |
| from the server. Sends the data on to to the client. | |
| DST ===> PROXY ===> CLIENT | |
| """ | |
| print("") | |
| print("SERVER => CLIENT") | |
| print(FORMAT_FN(data)) | |
| print("") | |
| self.factory.server.write(data) | |
| def write(self, data): | |
| if data: | |
| self.transport.write(data) | |
| def _noop(data): | |
| return data | |
| def get_local_ip(iface): | |
| ni.ifaddresses(iface) | |
| return ni.ifaddresses(iface)[ni.AF_INET][0]['addr'] | |
| FORMAT_FN = _noop | |
| LISTEN_PORT = 80 | |
| DST_PORT = 80 | |
| DST_HOST = "nonhttps.com" | |
| local_ip = get_local_ip('en0') | |
| # Look up the IP address of the target | |
| print("Querying DNS records for %s..." % DST_HOST) | |
| a_records = dns.resolver.query(DST_HOST, 'A') | |
| print("Found %d A records:" % len(a_records)) | |
| for r in a_records: | |
| print("* %s" % r.address) | |
| print("") | |
| assert(len(a_records) > 0) | |
| # THe target may have multiple IP addresses - we | |
| # simply choose the first one. | |
| DST_IP = a_records[0].address | |
| print("Choosing to proxy to %s" % DST_IP) | |
| print(""" | |
| #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# | |
| -#-#-#-#-#-RUNNING TCP PROXY-#-#-#-#-#- | |
| #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# | |
| Dst IP:\t%s | |
| Dst port:\t%d | |
| Dst hostname:\t%s | |
| Listen port:\t%d | |
| Local IP:\t%s | |
| """ % (DST_IP, DST_PORT, DST_HOST, LISTEN_PORT, local_ip)) | |
| print(""" | |
| Next steps: | |
| 1. Make sure you are spoofing DNS requests from the | |
| device you are trying to proxy request from so that they | |
| return your local IP (%s). | |
| 2. Make sure you have set the destination and listen ports | |
| correctly (they should generally be the same). | |
| 3. Use the device you are proxying requests from to make | |
| requests to %s and check that they are logged in this | |
| terminal. | |
| 4. Look at the requests, write more code to replay them, | |
| fiddle with them, etc. | |
| Listening for requests on %s:%d... | |
| """ % (local_ip, DST_HOST, local_ip, LISTEN_PORT)) | |
| factory = protocol.ServerFactory() | |
| factory.protocol = TCPProxyProtocol | |
| reactor.listenTCP(LISTEN_PORT, factory) | |
| reactor.run() |