1

So I am trying to connect to different sites on the Internet through the proxy that I am currently trying to build.

If I try to connect to a website on port 80 EVERYTHING works perfectly. But when I try to connect to any https link it does not work.

I tried using a self signed certificate but that did not work.

I am now using the certifi lib and it looks like I made so progress.

The function I use for TLS handshake:

def do_handshake():
    ca_certificates = certifi.where()
    print("ca-certificate mozilla", ca_certificates)
    
    CIPHERS = 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:AES256-SHA'

    conn_s = ssl.wrap_socket(conn, ssl_version=ssl.PROTOCOL_TLSv1_2, cert_reqs=ssl.CERT_OPTIONAL, ca_certs=ca_certificates, do_handshake_on_connect=False)
    
    print("Initiating Handshake!")
    conn_s.do_handshake()
    print("Handshake Completed!")

I accept connection and start a new thread to process the request.

while True:

    conn, addr = s.accept()
    request = conn.recv(4704)
    print(request)

    thread = threading.Thread(target=url_string, args=(conn, addr))
    thread.setDaemon(True)
    thread.start()

The socket server runs on port 9000.

Now If I send a curl request I get some weird text showing in the terminal and it gets stuck on conn_s.do_handshake() forever and eventually gets timed out.

The headers I send through my socket server back to client:

HTTP/1.1 200 Connection established
< Content-Type: text/plain
< Connection: close
< Set-Cookie: cookie=verified, Expires=Saturday, 8 May 2021 03:10:29

The curl request: curl https://google.com --proxy 127.0.0.1:9000 -v

If I don't to the TLS handshake I get this error (and many more AAA):

curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number

I use the requests library to first send the get or post request to the url and then return it to conn.

How do I do the handshake successfully and send the request back to conn?

UPDATE:

So the handshake problem was fixed by sending the HTTP/1.1 200 Connection Established header. I checked and the headers were not being sent before the TLS handshake was done.

But still I get the same error:

curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number

the code:

import socket, ssl
import os, sys, threading
import requests
import datetime
import calendar


HOST = "0.0.0.0"
PORT = os.environ.get("PORT") or 9000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(9)

print("Server listening on port {}".format(PORT))  

def url_string(conn, addr):
    string = request.decode("UTF-8")

    data = string.encode("UTF-8")

    print("Request String:", data)

    first_line = string.split("\n")[0]
    print(first_line)
    url = first_line.split(" ")[1]
    
    is_post_request = False

    if(first_line.split(" ") [0] == "POST"):
        is_post_request = True

    http_pos = url.find("://")
    if(http_pos == -1):
        temp = url
    else:
        temp = url[(http_pos + 3):]

    port_pos = temp.find(":")

    webserver = ""
    port = -1

    path_pos = temp.find("/")
    path = ""
    if(path_pos == -1):
        path_pos = len(temp)
    else:
        path = temp[path_pos : len(temp)]


    if(port_pos == -1 or path_pos < port_pos):
        port = 80
        webserver = temp[:path_pos]
    else:
        port = int(temp[port_pos + 1 : len(temp)])
        webserver = temp[:port_pos]

    print(webserver + path, port)

    proxy_request(webserver=webserver + path, port=port)

def proxy_request(webserver, port):
    
    headers = get_HTTP_headers()

    data_recv = ""
    try:
        print("Proxying request to ", addr[0])
        if(port == 443):
            r = requests.get("https://" + webserver)
            data_recv = r.text
            send_headers_test()
            do_handshake()
        else:
            r = requests.get("http://" + webserver)
            data_recv = r.text
    
    except Exception as e:
        print(str(e))
        data_recv = str(e).encode()

    finally:
        print("finally block entered")
        merge_HTTP_headers_and_content(headers, data_recv)

import certifi

def do_handshake():
    ca_certificates = certifi.where()
    print("ca-certificate mozilla", ca_certificates)

    CIPHERS = 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:AES256-SHA'

    conn_s = ssl.wrap_socket(conn, ssl_version=ssl.PROTOCOL_TLS, cert_reqs=ssl.CERT_OPTIONAL, ca_certs=ca_certificates, do_handshake_on_connect=False)
    #conn_s = context.wrap_socket(conn, do_handshake_on_connect=False)

    print("Initiating Handshake!")
    conn_s.do_handshake()
    print("Handshake Completed!")


while True:

    conn, addr = s.accept()
    request = conn.recv(4704)
    print(request)

    thread = threading.Thread(target=url_string, args=(conn, addr))
    thread.setDaemon(True)
    thread.start()
Rag
  • 101
  • 1
  • 11
  • 2
    Unfortunately you only provides snippets of your code. The problem lies likely outside of these snippets. Please provide a __minimal__ but __complete__ code which is sufficient to __reproduce__ your problem. – Steffen Ullrich May 08 '21 at 10:39
  • I added some more code as you said @SteffenUllrich – Rag May 08 '21 at 11:03
  • The code you show does not reflect what you describe. Specifically the shown code does not handle a CONNECT request in a special way and there is nothing in your code which sends the `HTTP/1.1 200 Connection Established` although you claim that you send it. – Steffen Ullrich May 08 '21 at 11:24
  • I do send the header as I said. I created a gist see here pls @SteffenUllrich https://gist.github.com/192493374d1b9c769cf355fb850d0f62 – Rag May 08 '21 at 11:52
  • 1
    The gist is different from what you show in your question (more complete). And it looks like that you don't understand how a CONNECT request works. The proxy is not supposed to do a TLS handshake with the server and then send the non-TLS traffic to the client. Instead the proxy is supposed to blindly pass everything through after the initial CONNECT request, so that client and server do the end-to-end TLS handshake by themselves. If TLS interception is required than the proxy would need to do a TLS handshake both with the server and with the client. The latter requires a certificate. – Steffen Ullrich May 08 '21 at 13:05
  • Thanks for the reply. I did try blindly passing everything but the initial CONNECT request that I pass through this proxy gives me 405 method not allowed. Could you give me an example of how to do the initial CONNECT request please? @SteffenUllrich – Rag May 08 '21 at 14:07
  • 1
    I cannot see what you did in code you did not show. And I will not try to hack a solution in your code. The behavior is quite clear: CONNECT request should be read, response to the client should be sent and from then on everything should be passed through in both directions. If you got an error than likely you did not implement this behavior properly. If you still get an error after implementing this then you need to provide the actual code. – Steffen Ullrich May 08 '21 at 14:28
  • https://gist.github.com/CommanderRag/1994ae05b9717301573b0fb5b4f2e4a6 Can you check what is wrong in that script? That one gives me 405 method not allowed. Thanks.@SteffenUllrich – Rag May 08 '21 at 14:55
  • Based on this code you forward the original CONNECT request to the server. That's exactly what you should not do (as explained) and what you claimed not to do. – Steffen Ullrich May 08 '21 at 15:04
  • I don't get it. What should I do then? – Rag May 08 '21 at 15:06
  • 1. Read the CONNECT request in the proxy. 2. Create a TCP (not TLS!) connection to the server and port given in the CONNECT request. 3. DO NOT FORWARD THE REQUEST TO THE SERVER. 4. Send a response indicating the success to the client after the TCP connection to the server succeeded. 5. From then on pass everything through between client and server in both directions. – Steffen Ullrich May 08 '21 at 15:10
  • I did what you said but I still see something like PROXY CONNECT aborted when I use curl but I still see what I sent,but when I use requests the request just gets denied ```ConnectionAbortedError``` . the updated gist: https://gist.github.com/CommanderRag/1994ae05b9717301573b0fb5b4f2e4a6. Help pls – Rag May 08 '21 at 15:35
  • 1
    *"I did what you said"* - not based on your code. You did step#1. Then did step#4, even though step#2 wasn't done yet. Then you did neither do step#2 nor step#5 but instead used `requests` to do a HTTPS connection to the server even though I explicitly said to do a __TCP__ connection in step#2 (i.e. a simple `socket.connect`). – Steffen Ullrich May 08 '21 at 15:46
  • I did try ```socket.connect()``` to port 443 but if I try to print what I received its just an empty byte. Thats why I used requests. And how do I know if step#2 is done? @SteffenUllrich – Rag May 08 '21 at 15:56
  • 1
    If the `socket.connect` succeeded than step#2 is done. And you really need to forward the data between client and server in both directions at the same time, i.e. you cannot expect one sending first and then the other. – Steffen Ullrich May 08 '21 at 17:13
  • I tried sending a get request to google.com port 443 and If i send back what I received in ```socket.connect()``` the request gets timed out. @SteffenUllrich – Rag May 09 '21 at 04:47
  • No code no help. It is not the first time that you claimed to do something and then the code showed that you did something else. – Steffen Ullrich May 09 '21 at 04:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/232137/discussion-between-rag-and-steffen-ullrich). – Rag May 09 '21 at 05:47
  • see chat pls @SteffenUllrich – Rag May 09 '21 at 08:12
  • come back pls I am finally able to connect to HTTPS sites. updated gist:https://gist.github.com/CommanderRag/1994ae05b9717301573b0fb5b4f2e4a6, but this does not work if I use a browser @SteffenUllrich – Rag May 17 '21 at 08:57

0 Answers0