I am investigating censorship in India, and know with a high degree of confidence that it is being blocked based on the SNI extension in the TLS ClientHello.
I am in control of both the Client (in India) and the Server (in Germany). I suspect someone in India is looking at the SNI to block websites (as opposed to the DNS lookup, or the certificate in the ServerHello in TLSv1.2).
I decided to confirm this by doing the following - make a TLS connection WITH the SNI set to a blocked website (pornhub as an example) to my own server.
This is what I observed on the client (in India):
- (TCP Success)
- Send ClientHello with SNI =
pornhub.com
- Receive TCP RST (apparently) from the Server (suspected MiTM)
- Subsequently receive some "legit" responses from REAL server - e.g. ServerHello (AFTER the initial TCP RST)
- Client thinks TCP conn. is already reset, so it responds with RST
This is what I observed on the server (in Germany)
- (TCP Success)
- Receive ClientHello with SNI =
pornhub.com
- Send the
ServerHello
(note: The cert is for a different domain, since I guess in nginx if the SNI doesn't match it just sends default cert). - Receive TCP RST from the Client
+-----------------+ +------------------------+ +-------------------+
| | | | | |
| Client | | INTERNET | | Server |
| (India) | | (Global) | | (Germany) |
| | | | | |
| 10.0.0.92 | | | |95.217.167.10 |
+-------+---------+ +-----------+------------+ +---------+---------+
| | |
| TLS Client Hello (SNI=pornhub) | TLS Client Hello (SNI=pornhub) |
+---------------------------------------> | -----------------------------------> |
| | |
| TCP RESET (from 95.217.167.10) | |
| <---------------------------------------+ |
| where does this come from? | |
| | |
| | |
| | |
| TLS Server Hello | TLS Server Hello |
| <--------------------------------------+ <------------------------------------+
| | |
| | |
| TCP RESET | TCP RESET |
| --------------------------------------> +-----------------------------------> |
| | |
Since I am in control of the Server, I know that it did not actually send the initial TCP RST to the Client. Is there a way to determine who in the middle is sending the TCP RST?
As a sanity check, I also tried using the SNI of an "ok" domain - google.com. That works just fine:
$ openssl s_client -connect 95.217.167.10:443 -servername google.com
CONNECTED(00000003)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = mijia.mywaifu.best
verify error:num=10:certificate has expired
notAfter=Jul 22 13:03:02 2023 GMT
verify return:1
depth=0 CN = mijia.mywaifu.best
notAfter=Jul 22 13:03:02 2023 GMT
verify return:1
---
Certificate chain
0 s:CN = mijia.mywaifu.best (Default cert on remote server)
and in the blocked case:
$ openssl s_client -connect 95.217.167.10:443 -servername pornhub.com
CONNECTED(00000003)
write:errno=104 /* Connection reset by peer */
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 313 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
I wanted to question my hosting provider, but I was wondering if I could first determine where the TCP RST is originating. I am 99% sure it is by the hosting provider, but theoretically they could say "it just originated from the internet and we only forwarded it to you". Though in a Great Firewall like scenario, it could actually originate from beyond the hosting provider.
traceroute
to figure out how far the packets travel.