diff --git a/README.md b/README.md index cf066bf..7778f66 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,25 @@ Then: python sslproxy.py -Then you can open `https://localhost:8443` in the browser. +Then you can add the REST source (note the `httpS`): -To add the REST source: + winget source add -n wpr -a https://localhost:8443/api/ -t "Microsoft.Rest" - winget source add -n "winget-pkgs-restsource" -a https://localhost:8443/api/ -t "Microsoft.Rest" \ No newline at end of file +### Sniff Microsoft's implementation + +Look at the `msstore` source shown by `winget source list`. Typically, it is: + + https://storeedgefd.dsx.mp.microsoft.com/v9.0 + +Start `sslproxy` as a man-in-the-middle proxy for the above domain: + + python sslproxy.py 8444 storeedgefd.dsx.mp.microsoft.com 443 + +Then you can add the proxied source via: + + winget source add -n ms -a https://localhost:8444/v9.0 -t "Microsoft.Rest" + +(Note that we used the path `/v9.0` from above.) + +Now you can inspect the traffic in the output of `sslproxy` when you perform +`winget` commands. \ No newline at end of file diff --git a/sslproxy.py b/sslproxy.py index 41ed2a6..0c2cb23 100644 --- a/sslproxy.py +++ b/sslproxy.py @@ -1,33 +1,84 @@ -import socket, ssl +""" +Usage: + python sslproxy [local_port] [dest_host] [dest_port] + +The defaults are local_port=8443, dest_host=localhost, dest_port=8000. +""" + +import socket, ssl, sys + +from functools import partial from threading import Thread -def forward(source, destination): - string = b' ' - while string: - string = source.recv(1024) - if string: - destination.sendall(string) +if len(sys.argv) > 1: + local_port = int(sys.argv[1]) +else: + local_port = 8443 + +if len(sys.argv) > 2: + dest_host = sys.argv[2] +else: + dest_host = 'localhost' + +if len(sys.argv) > 3: + dest_port = int(sys.argv[3]) +else: + dest_port = 8000 + +def forward(source, destination, prefix=''): + data = b' ' + while data: + try: + data = source.recv(1024) + except ConnectionResetError: + break + if data: + decoded = data.decode('utf-8') + for line in decoded.split('\n'): + print(prefix + line) + decoded = decoded.replace( + f'\nHost: localhost:{local_port}', + f'\nHost:{dest_host}' + ) + destination.sendall(decoded.encode('utf-8')) else: source.shutdown(socket.SHUT_RD) destination.shutdown(socket.SHUT_WR) -context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) -context.load_cert_chain(certfile="conf/localhost.cert", keyfile="conf/localhost.key") +local_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) +local_context.load_cert_chain( + certfile="conf/localhost.cert", keyfile="conf/localhost.key" +) + +if dest_port == 443: + dest_context = ssl.create_default_context() +else: + dest_context = None bindsocket = socket.socket() -bindsocket.bind(('', 8443)) +bindsocket.bind(('', local_port)) bindsocket.listen(5) +bindsocket.settimeout(.5) -print("Forwarding https://localhost:8443 -> http://localhost:8000") +print(f'Forwarding localhost:{local_port} -> {dest_host}:{dest_port}') while True: try: - newsocket, fromaddr = bindsocket.accept() - client_socket = context.wrap_socket(newsocket, server_side=True) - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.connect(('localhost', 8000)) - Thread(target=forward, args=(client_socket, server_socket)).start() - Thread(target=forward, args=(server_socket, client_socket)).start() + try: + newsocket, fromaddr = bindsocket.accept() + client = local_context.wrap_socket(newsocket, server_side=True) + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.connect((dest_host, dest_port)) + if dest_context: + server = dest_context.wrap_socket(server, server_hostname=dest_host) + Thread( + target=partial(forward, prefix='-> '), args=(client, server) + ).start() + Thread( + target=partial(forward, prefix='<- '), args=(server, client) + ).start() + except socket.timeout: + pass except KeyboardInterrupt: break \ No newline at end of file