├── .gitignore ├── requirements.txt ├── LICENSE ├── README.md ├── fake_dns_server.py ├── tcp_proxy.py └── tls_tcp_proxy.py /.gitignore: -------------------------------------------------------------------------------- 1 | ca-cert.pem 2 | *.swp 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyopenssl 2 | netifaces 3 | twisted 4 | dnspython 5 | service_identity 6 | scapy 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to make a TCP proxy 2 | 3 | This is the code from my series on how to build a TCP proxy: 4 | 5 | * [How to build a TCP proxy #1: Intro](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-1/). 6 | * [How to build a TCP proxy #2: Fake DNS Server](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-2/). 7 | * [How to build a TCP proxy #3: Proxy Server](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-3/). 8 | * [How to build a TCP proxy #3: Fake Certificate Authority](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-4/). 9 | 10 | ## Installation 11 | 12 | 1. Clone this repo 13 | 2. In the repo directory, make a new virtualenv using `virtualenv vendor` 14 | 3. Activate it using `source vendor/bin/activate` 15 | 4. Install requirements to the virtualenv using `pip install -r requirements.txt` 16 | 5. Whenever you want to run the code, activate the virtualenv by running `source vendor/bin/activate` again 17 | 18 | ## Usage 19 | 20 | ### Fake DNS Server 21 | 22 | Set your phone's DNS server to be the local IP of your laptop. Then run: 23 | 24 | ``` 25 | sudo python fake_dns_server.py 26 | ``` 27 | 28 | For lots more detail, see [How to build a TCP proxy #2: Fake DNS Server](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-2/). 29 | 30 | ### Non-TLS TCP proxy 31 | 32 | Set the DNS Spoofer running, then: 33 | 34 | ``` 35 | sudo python tcp_proxy.py 36 | ``` 37 | 38 | For lots more detail, see [How to build a TCP proxy #3: Proxy Server](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-3/). 39 | 40 | ### TLS TCP proxy 41 | 42 | Set the DNS Spoofer running, then: 43 | 44 | ``` 45 | sudo python tls_tcp_proxy.py 46 | ``` 47 | 48 | For lots more detail, see [How to build a TCP proxy #4: Proxy Server](https://robertheaton.com/2018/08/31/how-to-build-a-tcp-proxy-4/). 49 | -------------------------------------------------------------------------------- /fake_dns_server.py: -------------------------------------------------------------------------------- 1 | import dns.resolver 2 | import scapy.all as scapy 3 | import netifaces as ni 4 | 5 | def handle_packet_fn(iface, spoof_ip, spoof_domains): 6 | def handle_packet(packet): 7 | ip = packet.getlayer(scapy.IP) 8 | udp = packet.getlayer(scapy.UDP) 9 | 10 | # Ignore packets containing data we aren't interested 11 | # in. 12 | if hasattr(packet, 'qd') and packet.qd is not None: 13 | queried_host = packet.qd.qname[:-1].decode("utf-8") 14 | if queried_host is None: 15 | print("queried_host is None, dropping request") 16 | return 17 | 18 | # If the queried_host is one of the domains we want 19 | # to spoof, return the spoof_ip. 20 | if queried_host in spoof_domains: 21 | print("!!!! Spoofing DNS request for %s by %s !!!!" 22 | % (queried_host, ip.src)) 23 | resolved_ip = spoof_ip 24 | # Else use dns.resolver to make a real DNS "A record" 25 | # request, and return the result of that. 26 | else: 27 | print("Forwarding DNS request for %s by %s" % 28 | (queried_host, ip.src)) 29 | a_records = dns.resolver.query(queried_host, 'A') 30 | resolved_ip = a_records[0].address 31 | 32 | # Build the DNS answer 33 | dns_answer = scapy.DNSRR( 34 | rrname=queried_host + ".", 35 | ttl=330, 36 | type="A", 37 | rclass="IN", 38 | rdata=resolved_ip) 39 | # Build the DNS response by constructing the IP 40 | # packet, the UDP "datagram" that goes inside the 41 | # packet, and finally the DNS response that goes 42 | # inside the datagram. 43 | dns_response = \ 44 | scapy.IP(src=ip.dst, dst=ip.src) / \ 45 | scapy.UDP( 46 | sport=udp.dport, 47 | dport=udp.sport 48 | ) / \ 49 | scapy.DNS( 50 | id = packet[scapy.DNS].id, 51 | qr = 1, 52 | aa = 0, 53 | rcode = 0, 54 | qd = packet.qd, 55 | an = dns_answer 56 | ) 57 | 58 | print("Resolved DNS request for %s to %s for %s" % 59 | (queried_host, resolved_ip, ip.src)) 60 | 61 | # Use scapy to send our response back to your phone. 62 | scapy.send(dns_response, iface=iface) 63 | else: 64 | print("Ignoring unrecognized packet from %s" % ip.src) 65 | 66 | return handle_packet 67 | 68 | 69 | def _get_local_ip(iface): 70 | ni.ifaddresses(iface) 71 | return ni.ifaddresses(iface)[ni.AF_INET][0]['addr'] 72 | 73 | 74 | def run(iface, local_ip, sniff_filter, spoof_domains): 75 | print("#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#") 76 | print("-#-#-#-#-#-RUNNING DNS SPOOFER-#-#-#-#-#-") 77 | print("#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#") 78 | print("Interface:\t\t\t%s" % iface) 79 | print("Resolving to IP:\t\t%s" % local_ip) 80 | print("Spoof domains:\t\t%s" % ', '.join(spoof_domains)) 81 | print("BPF sniff filter:\t\t%s" % sniff_filter) 82 | print("") 83 | print("Waiting for DNS requests...") 84 | print("(Make sure the device you are targeting is set to use"\ 85 | "your local IP (%s) as its DNS server)" % local_ip) 86 | 87 | scapy.sniff(iface=iface, 88 | filter=sniff_filter, 89 | prn=handle_packet_fn(iface, local_ip, spoof_domains)) 90 | 91 | 92 | IFACE= 'en0' 93 | local_ip = _get_local_ip(IFACE) 94 | # The local IP of your phone 95 | client_ip = '192.168.42.74' 96 | 97 | # SPOOF_DOMAINS = ['nonhttps.com', 'www.nonhttps.com'] 98 | SPOOF_DOMAINS = ['google.com', 'www.google.com'] 99 | SNIFF_FILTER = ("udp port 53 && dst %s && src %s" % 100 | (local_ip, client_ip)) 101 | 102 | run(IFACE, local_ip, SNIFF_FILTER, SPOOF_DOMAINS) 103 | -------------------------------------------------------------------------------- /tcp_proxy.py: -------------------------------------------------------------------------------- 1 | from twisted.internet import protocol, reactor 2 | from twisted.internet import ssl as twisted_ssl 3 | import dns.resolver 4 | import netifaces as ni 5 | 6 | # Adapted from http://stackoverflow.com/a/15645169/221061 7 | 8 | class TCPProxyProtocol(protocol.Protocol): 9 | """ 10 | TCPProxyProtocol listens for TCP connections from a 11 | client (eg. a phone) and forwards them on to a 12 | specified destination (eg. an app's API server) over 13 | a second TCP connection, using a ProxyToServerProtocol. 14 | 15 | It assumes that neither leg of this trip is encrypted. 16 | """ 17 | def __init__(self): 18 | self.buffer = None 19 | self.proxy_to_server_protocol = None 20 | 21 | def connectionMade(self): 22 | """ 23 | Called by twisted when a client connects to the 24 | proxy. Makes an connection from the proxy to the 25 | server to complete the chain. 26 | """ 27 | print("Connection made from CLIENT => PROXY") 28 | proxy_to_server_factory = protocol.ClientFactory() 29 | proxy_to_server_factory.protocol = ProxyToServerProtocol 30 | proxy_to_server_factory.server = self 31 | 32 | reactor.connectTCP(DST_IP, DST_PORT, 33 | proxy_to_server_factory) 34 | 35 | def dataReceived(self, data): 36 | """ 37 | Called by twisted when the proxy receives data from 38 | the client. Sends the data on to the server. 39 | 40 | CLIENT ===> PROXY ===> DST 41 | """ 42 | print("") 43 | print("CLIENT => SERVER") 44 | print(FORMAT_FN(data)) 45 | print("") 46 | if self.proxy_to_server_protocol: 47 | self.proxy_to_server_protocol.write(data) 48 | else: 49 | self.buffer = data 50 | 51 | def write(self, data): 52 | self.transport.write(data) 53 | 54 | 55 | class ProxyToServerProtocol(protocol.Protocol): 56 | """ 57 | ProxyToServerProtocol connects to a server over TCP. 58 | It sends the server data given to it by an 59 | TCPProxyProtocol, and uses the TCPProxyProtocol to 60 | send data that it receives back from the server on 61 | to a client. 62 | """ 63 | 64 | def connectionMade(self): 65 | """ 66 | Called by twisted when the proxy connects to the 67 | server. Flushes any buffered data on the proxy to 68 | server. 69 | """ 70 | print("Connection made from PROXY => SERVER") 71 | self.factory.server.proxy_to_server_protocol = self 72 | self.write(self.factory.server.buffer) 73 | self.factory.server.buffer = '' 74 | 75 | def dataReceived(self, data): 76 | """ 77 | Called by twisted when the proxy receives data 78 | from the server. Sends the data on to to the client. 79 | 80 | DST ===> PROXY ===> CLIENT 81 | """ 82 | print("") 83 | print("SERVER => CLIENT") 84 | print(FORMAT_FN(data)) 85 | print("") 86 | self.factory.server.write(data) 87 | 88 | def write(self, data): 89 | if data: 90 | self.transport.write(data) 91 | 92 | 93 | def _noop(data): 94 | return data 95 | 96 | def get_local_ip(iface): 97 | ni.ifaddresses(iface) 98 | return ni.ifaddresses(iface)[ni.AF_INET][0]['addr'] 99 | 100 | FORMAT_FN = _noop 101 | 102 | LISTEN_PORT = 80 103 | DST_PORT = 80 104 | DST_HOST = "nonhttps.com" 105 | local_ip = get_local_ip('en0') 106 | 107 | # Look up the IP address of the target 108 | print("Querying DNS records for %s..." % DST_HOST) 109 | a_records = dns.resolver.query(DST_HOST, 'A') 110 | print("Found %d A records:" % len(a_records)) 111 | for r in a_records: 112 | print("* %s" % r.address) 113 | print("") 114 | assert(len(a_records) > 0) 115 | 116 | # THe target may have multiple IP addresses - we 117 | # simply choose the first one. 118 | DST_IP = a_records[0].address 119 | print("Choosing to proxy to %s" % DST_IP) 120 | 121 | print(""" 122 | #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# 123 | -#-#-#-#-#-RUNNING TCP PROXY-#-#-#-#-#- 124 | #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# 125 | 126 | Dst IP:\t%s 127 | Dst port:\t%d 128 | Dst hostname:\t%s 129 | 130 | Listen port:\t%d 131 | Local IP:\t%s 132 | """ % (DST_IP, DST_PORT, DST_HOST, LISTEN_PORT, local_ip)) 133 | 134 | print(""" 135 | Next steps: 136 | 137 | 1. Make sure you are spoofing DNS requests from the 138 | device you are trying to proxy request from so that they 139 | return your local IP (%s). 140 | 2. Make sure you have set the destination and listen ports 141 | correctly (they should generally be the same). 142 | 3. Use the device you are proxying requests from to make 143 | requests to %s and check that they are logged in this 144 | terminal. 145 | 4. Look at the requests, write more code to replay them, 146 | fiddle with them, etc. 147 | 148 | Listening for requests on %s:%d... 149 | """ % (local_ip, DST_HOST, local_ip, LISTEN_PORT)) 150 | 151 | factory = protocol.ServerFactory() 152 | factory.protocol = TCPProxyProtocol 153 | reactor.listenTCP(LISTEN_PORT, factory) 154 | reactor.run() 155 | -------------------------------------------------------------------------------- /tls_tcp_proxy.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from twisted.internet import protocol, reactor 4 | from twisted.internet import ssl as twisted_ssl 5 | import dns.resolver 6 | 7 | from OpenSSL.crypto import (X509Extension, X509, 8 | dump_privatekey, dump_certificate, 9 | load_certificate, load_privatekey, 10 | PKey, TYPE_RSA, X509Req) 11 | from OpenSSL.SSL import FILETYPE_PEM 12 | import tempfile 13 | import os 14 | import netifaces as ni 15 | 16 | # Adapted from http://stackoverflow.com/a/15645169/221061 17 | 18 | class TLSTCPProxyProtocol(protocol.Protocol): 19 | """ 20 | TLSTCPProxyProtocol listens for TCP connections from a 21 | client (eg. a phone) and forwards them on to a specified 22 | destination (eg. an app's API server) over a second TCP 23 | connection, using a ProxyToServerProtocol. 24 | 25 | It assumes that both legs of this trip are encrypted 26 | using TLS. 27 | """ 28 | def __init__(self): 29 | self.buffer = None 30 | self.proxy_to_server_protocol = None 31 | 32 | def connectionMade(self): 33 | """ 34 | Called by twisted when a client connects to the 35 | proxy. Makes an TLS connection from the proxy to 36 | the server to complete the chain. 37 | """ 38 | print("Connection made from CLIENT => PROXY") 39 | proxy_to_server_factory = protocol.ClientFactory() 40 | proxy_to_server_factory.protocol = ProxyToServerProtocol 41 | proxy_to_server_factory.server = self 42 | 43 | reactor.connectSSL(DST_IP, DST_PORT, 44 | proxy_to_server_factory, 45 | twisted_ssl.CertificateOptions()) 46 | 47 | def dataReceived(self, data): 48 | """ 49 | Called by twisted when the proxy receives data from 50 | the client. Sends the data on to the server. 51 | 52 | CLIENT ===> PROXY ===> DST 53 | """ 54 | print("") 55 | print("CLIENT => SERVER") 56 | print(FORMAT_FN(data)) 57 | WRITE_TO_FILE(data) 58 | print("") 59 | if self.proxy_to_server_protocol: 60 | self.proxy_to_server_protocol.write(data) 61 | else: 62 | self.buffer = data 63 | 64 | def write(self, data): 65 | self.transport.write(data) 66 | 67 | 68 | class ProxyToServerProtocol(protocol.Protocol): 69 | """ 70 | ProxyToServerProtocol connects to a server over TCP. 71 | It sends the server data given to it by an 72 | TLSTCPProxyProtocol, and uses the TLSTCPProxyProtocol 73 | to send data that it receives back from the server on 74 | to a client. 75 | """ 76 | 77 | def connectionMade(self): 78 | """ 79 | Called by twisted when the proxy connects to the 80 | server. Flushes any buffered data on the proxy 81 | to server. 82 | """ 83 | print("Connection made from PROXY => SERVER") 84 | self.factory.server.proxy_to_server_protocol = self 85 | self.write(self.factory.server.buffer) 86 | self.factory.server.buffer = '' 87 | 88 | def dataReceived(self, data): 89 | """ 90 | Called by twisted when the proxy receives data 91 | from the server. Sends the data on to to the 92 | client. 93 | 94 | DST ===> PROXY ===> CLIENT 95 | """ 96 | print("") 97 | print("SERVER => CLIENT") 98 | print(FORMAT_FN(data)) 99 | print("") 100 | self.factory.server.write(data) 101 | 102 | def write(self, data): 103 | if data: 104 | self.transport.write(data) 105 | 106 | 107 | # A class that represents a CA. It wraps a root CA TLS 108 | # certificate, and can generate and sign certificates using 109 | # this root cert. 110 | # 111 | # Inpsiration from 112 | # https://github.com/allfro/pymiproxy/blob/master/src/miproxy/proxy.py 113 | class CertificateAuthority(object): 114 | 115 | CERT_PREFIX = 'fake-cert' 116 | 117 | def __init__(self, ca_file, cache_dir=tempfile.mkdtemp()): 118 | print("Initializing CertificateAuthority ca_file=%s cache_dir=%s" % 119 | (ca_file, cache_dir)) 120 | 121 | self.ca_file = ca_file 122 | self.cache_dir = cache_dir 123 | if not os.path.exists(ca_file): 124 | raise Exception("No cert exists at %s" % ca_file) 125 | else: 126 | self._read_ca(ca_file) 127 | 128 | def get_cert_path(self, cn): 129 | cnp = os.path.sep.join([self.cache_dir, '%s-%s.pem' % 130 | (self.CERT_PREFIX, cn)]) 131 | if os.path.exists(cnp): 132 | print("Cert already exists common_name=%s" % cn) 133 | else: 134 | print("Creating and signing cert common_name=%s" % cn) 135 | key = PKey() 136 | key.generate_key(TYPE_RSA, 2048) 137 | 138 | # Generate CSR 139 | req = X509Req() 140 | req.get_subject().CN = cn 141 | req.set_pubkey(key) 142 | req.sign(key, 'sha1') 143 | 144 | # Sign CSR 145 | cert = X509() 146 | cert.set_subject(req.get_subject()) 147 | cert.set_serial_number(123) 148 | cert.gmtime_adj_notBefore(0) 149 | cert.gmtime_adj_notAfter(31536000) 150 | cert.set_issuer(self.cert.get_subject()) 151 | cert.set_pubkey(req.get_pubkey()) 152 | cert.sign(self.key, 'sha1') 153 | 154 | with open(cnp, 'wb+') as f: 155 | f.write(dump_privatekey(FILETYPE_PEM, key)) 156 | f.write(dump_certificate(FILETYPE_PEM, cert)) 157 | 158 | print("Created cert common_name=%s location=%s" % (cn, cnp)) 159 | 160 | return cnp 161 | 162 | def _read_ca(self, file): 163 | self.cert = load_certificate(FILETYPE_PEM, open(file).read()) 164 | self.key = load_privatekey(FILETYPE_PEM, open(file).read()) 165 | 166 | @staticmethod 167 | def generate_ca_cert(path, common_name): 168 | if os.path.exists(path): 169 | print("Cert already exists at %s, not regenerating" % path) 170 | return 171 | # Generate key 172 | key = PKey() 173 | key.generate_key(TYPE_RSA, 2048) 174 | 175 | # Generate certificate 176 | cert = X509() 177 | cert.set_version(3) 178 | cert.set_serial_number(1) 179 | cert.get_subject().CN = common_name 180 | cert.gmtime_adj_notBefore(0) 181 | cert.gmtime_adj_notAfter(315360000) 182 | cert.set_issuer(cert.get_subject()) 183 | cert.set_pubkey(key) 184 | cert.sign(key, "sha256") 185 | 186 | with open(path, 'wb+') as f: 187 | f.write(dump_privatekey(FILETYPE_PEM, key)) 188 | f.write(dump_certificate(FILETYPE_PEM, cert)) 189 | 190 | 191 | def get_local_ip(iface): 192 | ni.ifaddresses(iface) 193 | return ni.ifaddresses(iface)[ni.AF_INET][0]['addr'] 194 | 195 | 196 | # Alternative functions for formating output data 197 | def _side_by_side_hex(data): 198 | BLOCK_SIZE = 16 199 | 200 | output_lines = [] 201 | for i in range(0, len(data), BLOCK_SIZE): 202 | block = data[i:i+BLOCK_SIZE] 203 | _hex = ["%.2x" % el for el in block] 204 | _str = [chr(el) if chr(el).isprintable() else "." for el in block] 205 | line = " ".join(_hex).ljust((3*BLOCK_SIZE)+4) + "".join(_str).replace("\n", ".") 206 | output_lines.append(line) 207 | return "\n".join(output_lines) 208 | 209 | def _stacked_hex(data): 210 | BLOCK_SIZE = 32 211 | 212 | hex_lines = [] 213 | plaintext_lines = [] 214 | for i in range(0, len(data), BLOCK_SIZE): 215 | block = data[i:i+BLOCK_SIZE] 216 | _hex = ["%.2x" % el for el in block] 217 | _str = [chr(el) if chr(el).isprintable() else "." for el in block] 218 | 219 | hex_line = " ".join(_hex) 220 | hex_lines.append(hex_line) 221 | 222 | plaintext_line = " ".join(_str).replace("\n", ".") 223 | plaintext_lines.append(plaintext_line) 224 | 225 | lines = hex_lines + ["\n"] + plaintext_lines 226 | return "\n".join(lines) 227 | 228 | def _replayable(data): 229 | d = data[0:4000] 230 | _hex = "".join(["%.2x" % el for el in d]) 231 | _str = "".join([chr(el) if chr(el).isprintable() else "." for el in d]) 232 | 233 | return _hex + "\n" + _str 234 | 235 | def _noop(data): 236 | return data 237 | 238 | # Change this line to use an alternative formating function 239 | FORMAT_FN = _noop 240 | 241 | # Record data sent to the server to files 242 | DIR_NAME = "replays/messages-%d/" % time.time() 243 | os.mkdir(DIR_NAME) 244 | f_n = 0 245 | def _write_to_file(data): 246 | # Global variables are bad but they do the job 247 | global f_n 248 | with open(DIR_NAME + str(f_n), 'wb') as f: 249 | f.write(data) 250 | f_n += 1 251 | WRITE_TO_FILE = _write_to_file 252 | 253 | CA_CERT_PATH = "./ca-cert.pem" 254 | 255 | LISTEN_PORT = 443 256 | DST_PORT = 443 257 | DST_HOST = "www.bbc.com" 258 | local_ip = get_local_ip('en0') 259 | 260 | print("Querying DNS records for %s..." % DST_HOST) 261 | a_records = dns.resolver.query(DST_HOST, 'A') 262 | print("Found %d A records:" % len(a_records)) 263 | for r in a_records: 264 | print("* %s" % r.address) 265 | print("") 266 | assert(len(a_records) > 0) 267 | 268 | DST_IP = a_records[0].address 269 | print("Choosing to proxy to %s" % DST_IP) 270 | 271 | print(""" 272 | #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# 273 | -#-#-#-#-#-RUNNING TLS TCP PROXY-#-#-#-#-#- 274 | #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# 275 | 276 | Root CA path:\t%s 277 | 278 | Dst IP:\t%s 279 | Dst port:\t%d 280 | Dst hostname:\t%s 281 | 282 | Listen port:\t%d 283 | Local IP:\t%s 284 | """ % (CA_CERT_PATH, DST_IP, DST_PORT, DST_HOST, LISTEN_PORT, local_ip)) 285 | 286 | CertificateAuthority.generate_ca_cert(CA_CERT_PATH, "Robert's Trusty Certificate Corp") 287 | ca = CertificateAuthority(CA_CERT_PATH) 288 | certfile = ca.get_cert_path(DST_HOST) 289 | with open(certfile) as f: 290 | cert = twisted_ssl.PrivateCertificate.loadPEM(f.read()) 291 | 292 | print(""" 293 | Next steps: 294 | 295 | 1. Make sure you are spoofing DNS requests from the 296 | device you are trying to proxy request from so that they 297 | return your local IP (%s). 298 | 2. Make sure you have set the destination and listen 299 | ports correctly (they should generally be the same). 300 | 3. Use the device you are proxying requests from to make 301 | requests to %s and check that they are logged in this 302 | terminal. 303 | 4. Look at the requests, write more code to replay them, 304 | fiddle with them, etc. 305 | 306 | Listening for requests on %s:%d... 307 | """ % (local_ip, DST_HOST, local_ip, LISTEN_PORT)) 308 | 309 | factory = protocol.ServerFactory() 310 | factory.protocol = TLSTCPProxyProtocol 311 | reactor.listenSSL(LISTEN_PORT, factory, cert.options(), 312 | interface=local_ip) 313 | reactor.run() 314 | --------------------------------------------------------------------------------