You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A) CONNECT requests to an http target URL are falsely attempted to be intercepted by interception mode. proxypy ssl exception is triggered and request fails.
B) do_intercept() does not intercept request target that are supposed to be intercepted when CONNECT request use IP as authority.
To Reproduce
Steps to reproduce the behavior:
the issue can be reproduced with running poetry install and then poetry shell and then python proxy-test/custom_proxy.py in the root dir of https://github.com/jj-author/https-interception-proxypy/ or alternatively setting up your own version of proxypy on port 8897 that TLS intercepts requests for example.org but not all other request targets (e.g. not for www.example.org , google.com and all other)
run test for problem A and problem B below
A)
run http request (plaintext) command via CONNECT (curl -p option)
curl -p -I -x http://tools.dbpedia.org:8897 http://example.org
HTTP/1.1 200 Connection established
curl: (52) Empty reply from server
024-10-14 14:50:10,909 - pid:1143139 [D] handler.handle_readables:222 - Client is read ready, receiving...
2024-10-14 14:50:10,909 - pid:1143139 [D] connection.recv:62 - received 112 bytes from client
2024-10-14 14:50:10,909 - pid:1143139 [D] server.connect_upstream:578 - Connecting to upstream example.org:80
2024-10-14 14:50:11,014 - pid:1143139 [D] server.connect_upstream:613 - Connected to upstream example.org:80
Do intercept triggered: {'state': 6, 'type': 1, 'protocol': None, 'host': b'example.org', 'port': 80, 'path': None, 'method': b'CONNECT', 'code': None, 'reason': None, 'version': b'HTTP/1.1', 'total_size': 112, 'buffer': None, 'headers': {b'host': (b'Host', b'example.org:80'), b'user-agent': (b'User-Agent', b'curl/7.81.0'), b'proxy-connection': (b'Proxy-Connection', b'Keep-Alive')}, 'body': None, 'chunk': None, '_url': <proxy.http.url.Url object at 0x7f3f39053a60>, '_is_chunked_encoded': False, '_content_expected': False, '_is_https_tunnel': True}
##### Self.client vars: {'tag': 'client', 'buffer': [<memory at 0x7f3f393004c0>], 'closed': False, '_reusable': False, '_num_buffer': 1, '_conn': <socket.socket fd=107, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('127.0.0.1', 8897), raddr=('127.0.0.1', 56922)>, 'addr': ('127.0.0.1', 56922)}
2024-10-14 14:50:11,127 - pid:1143139 [E] server.wrap_server:802 - SSLError when wrapping client for upstream: example.org
Traceback (most recent call last):
File "/root/.cache/pypoetry/virtualenvs/proxy-test-f6SwClI5-py3.10/lib/python3.10/site-packages/proxy/http/proxy/server.py", line 776, in wrap_server
self.upstream.wrap(
File "/root/.cache/pypoetry/virtualenvs/proxy-test-f6SwClI5-py3.10/lib/python3.10/site-packages/proxy/core/connection/server.py", line 63, in wrap
self._conn = ctx.wrap_socket(
File "/root/.pyenv/versions/3.10.14/lib/python3.10/ssl.py", line 513, in wrap_socket
return self.sslsocket_class._create(
File "/root/.pyenv/versions/3.10.14/lib/python3.10/ssl.py", line 1104, in _create
self.do_handshake()
File "/root/.pyenv/versions/3.10.14/lib/python3.10/ssl.py", line 1375, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1007)
2024-10-14 14:50:11,128 - pid:1143139 [D] tcp_server.handle_readables:212 - Implementation signaled shutdown for client 127.0.0.1:56922
2024-10-14 14:50:11,129 - pid:1143139 [D] tcp_server.handle_readables:218 - Client 127.0.0.1:56922 has pending buffer, will be flushed before shutting down
2024-10-14 14:50:11,129 - pid:1143139 [D] threadless._update_work_events:166 - fd#107 modified for mask#2 by work#107
2024-10-14 14:50:11,129 - pid:1143139 [D] handler.handle_writables:193 - Client is write ready, flushing...
2024-10-14 14:50:11,129 - pid:1143139 [D] tcp_server.handle_writables:173 - Flushing buffer to client 127.0.0.1:56922
2024-10-14 14:50:11,129 - pid:1143139 [D] connection.flush:101 - flushed 39 bytes to client
2024-10-14 14:50:11,129 - pid:1143139 [D] threadless._cleanup:311 - fd#107 unregistered by work#107
2024-10-14 14:50:11,130 - pid:1143139 [I] server.access_log:393 - 127.0.0.1:56922 - CONNECT example.org:80 - 0 bytes - 220.20ms
2024-10-14 14:50:11,130 - pid:1143139 [D] server.on_client_connection_close:384 - Closed server connection, has buffer False
2024-10-14 14:50:11,130 - pid:1143139 [D] handler.shutdown:89 - Closing client connection 127.0.0.1:56922 has buffer False
2024-10-14 14:50:11,130 - pid:1143139 [D] handler.shutdown:99 - Client connection shutdown successful
2024-10-14 14:50:11,130 - pid:1143139 [D] handler.shutdown:113 - Client connection closed
B)
issue CONNECT request that is using IP as authority but uses SNI/servername that is supposed to be intercepted => bug is that original certificate (see issuer) is returned
A) I expect the curl request to succeed or more specifically I expect that plaintext http tunnels are not attempted to be TLS-intercepted. In other words proxypy should only try to intercept a connect request if a TLS hello is sent by the client. I think the correct behavior would be for non-tls tunnels either to not trigger do_intercept or at least ignore the return value of it. As a sidenote: I would expect 'request._is_https_tunnel' to be false in this case because that is obviously not true.
B) I expect proxypy to correctly intercept example.org and to see my local certificate issuer for that request proofing interception. On implementation level do_intercept method needs fixing in a way that the decision to intercept is being made based on the actual TLS connection hostname (SNI) and not on the CONNECT target/authority/hostname - which doesn't and won't work.
So in other words expected is that the decision is being (or at least can be) made based on the SNI header if present. For reasons of plugin backward compatibility probably the easiest way to fix it would be to add a connect_tunnel object information to the client variable/object. that tunnel information would carry for now the field tsl_client_sni (that contains the SNI name null if none detected) tls_client_hello_version (version set to null if no tls detected). This would allow that by default there is the "faulty" behavior but new plugins could implement it correctly. Additionally adding the destination port tunnel_dest_port to that tunnel object would make a lot of sense would help to mitigate the port bug of proxypy explained in #1437
I filed both bugs in the same issue because fixing both is achieved by parsing and taking actions based on the TLS client hello.
Note that fixing this would also fix broken websocket requests #1432 (comment) and also another aspect of issue #1437 where there is no easy way to identify whether it is an https or http request
Version information
OS: Ubuntu 20.04 with dual stack (IPv4 and IPv6)
clients: curl
Device: same server
proxy.py Version 2.4.8
Additional context
compare with output of B) - when connect target-authority and tunneled request-authority/SNI match selective interception works
Please note that there seems another issue about the encoding of the original values lets just hope for now that does not break anything...
JJ-Author
changed the title
TLS interception breaks tunneled http requests and is not activated reliably based on the https request target
TLS interception breaks tunneled http requests and is not activated reliably based on the https request target authority
Oct 15, 2024
Describe the bug
A) CONNECT requests to an http target URL are falsely attempted to be intercepted by interception mode. proxypy ssl exception is triggered and request fails.
B) do_intercept() does not intercept request target that are supposed to be intercepted when CONNECT request use IP as authority.
To Reproduce
Steps to reproduce the behavior:
A)
run http request (plaintext) command via CONNECT (curl -p option)
http messages
proxypy exception
B)
issue CONNECT request that is using IP as authority but uses SNI/servername that is supposed to be intercepted => bug is that original certificate (see issuer) is returned
http messages
Expected behavior
A) I expect the curl request to succeed or more specifically I expect that plaintext http tunnels are not attempted to be TLS-intercepted. In other words proxypy should only try to intercept a connect request if a TLS hello is sent by the client. I think the correct behavior would be for non-tls tunnels either to not trigger do_intercept or at least ignore the return value of it. As a sidenote: I would expect 'request._is_https_tunnel' to be false in this case because that is obviously not true.
B) I expect proxypy to correctly intercept example.org and to see my local certificate issuer for that request proofing interception. On implementation level
do_intercept
method needs fixing in a way that the decision to intercept is being made based on the actual TLS connection hostname (SNI) and not on the CONNECT target/authority/hostname - which doesn't and won't work.So in other words expected is that the decision is being (or at least can be) made based on the SNI header if present. For reasons of plugin backward compatibility probably the easiest way to fix it would be to add a
connect_tunnel
object information to theclient
variable/object. that tunnel information would carry for now the fieldtsl_client_sni
(that contains the SNI name null if none detected)tls_client_hello_version
(version set to null if no tls detected). This would allow that by default there is the "faulty" behavior but new plugins could implement it correctly. Additionally adding the destination porttunnel_dest_port
to that tunnel object would make a lot of sense would help to mitigate the port bug of proxypy explained in #1437I filed both bugs in the same issue because fixing both is achieved by parsing and taking actions based on the TLS client hello.
Note that fixing this would also fix broken websocket requests #1432 (comment) and also another aspect of issue #1437 where there is no easy way to identify whether it is an https or http request
Version information
Additional context
compare with output of B) - when connect target-authority and tunneled request-authority/SNI match selective interception works
Please note that there seems another issue about the encoding of the original values lets just hope for now that does not break anything...
The text was updated successfully, but these errors were encountered: