Description
Update:2020.1.13
一些封锁方式升级说明:
现在GFW对TCP的干扰更不容易被发现:利用静态路由对指定TCP/UDP端口进行黑洞路由(BGP劫持)
近期发现过两次类似做法
2019.3.8
对部分Telegram中间代理服务器的TCP 8888端口进行劫持
2019.12.29
对部分Github中raw.githubusercontent.com使用的fastly的CDN节点(151.101.228.133,151.101.0.133)的TCP 443端口进行劫持
其表现为,被劫持的TCP端口的RTT非常低,几乎接近访问同省/市网络的速度(猜测黑洞服务器位于同省/市位置) 跟踪任何没被劫持的端口/协议表现都是正常的
Update:2019.11.24
Google 正式完成并部署了新的TLS后量子密钥交换协议 CECPQ2(HRSS + X25519)
欢迎使用Chrome/Chromium Dev/Canary 最新版本的浏览器并且配合本项目hosts对 https://www.google.com/ncr 进行测试
Update:2018.10.16
非100%确认,只做提醒
经过几天的观测
GFW在部分地区开始对无SNI的1.1.1.1 https连接进行丢包 or 连接重置 (针对DoH)
概率:调查的10位中有8位出现
其中6位测试了使用浏览器打开https://1.1.1.1 没有被rst
使用cloudflared 作为DNS代理时出现丢包 or 连接重置的情况
104.16.111.25和104.16.112.25可能也会继续更进(1位报告有无法解析的情况)
Update time:2018.10.23
咩做了一系列测试来验证最近的封锁情况
如果不想看后面的实验内容我就简单的说明下:
GFW根据rfc6066 第3章节Server Name Indication中要求client hello中包含server_name(比如:你访问的是www.google.com,这里的server_name就是www.google.com 如果需要验证 请看下面的实验) 并且这部分为明文,gfw只需要根据此这部分信息进行过滤即可(如果需要验证 请看下面的实验)
解决方案:QUIC
解决方案:TLS1.3+ [ESNI?](个人并不认可现在cloudflare的DNS ESNI方案,过于依赖DNS) 完成
在更新时(2018.10.23)的ESNI草案就是类似与域前置的方案
我依然坚持基于PSK的跟进实现:https://github.com/QVQNetwork/ssl-patch
如果近期急用可以暂时使用:https://github.com/googlehosts/hosts/wiki/%E5%AE%9E%E9%AA%8C%E5%AE%A4#shadowsocks
测试环境:
OS: Ubuntu 18.04
Network:China Telecom
Software: Wireshark v2.9.0 hping3
SNI Server Software:
Chrome: 版本 70.0.3528.4(正式版本)dev (64 位)
结论:见最开始说明
1.首先我拿纽约时报的中文域名做了测试
hping3不断尝试向443端口发送SYN(-S[既TCP握手第一步建立连接])并没有被完全阻断,但是能明显看rtt=0.0 ms无法握手 网页被RST
并且间歇性的可以握手
2.接下来就该实际访问测试了
我们使用Chrome强制https://cn.nytimes.com/
Wireshark 中我们可以明显看到在Client Hello后ACK环节直接RST重置,GG 关于这一块去看看维基百科的无状态TCP协议连接重置介绍就知道了我也不多做说明
找到了原因让我们想一想gfw是如何判断这个IP是友军还是敌人呢昂
我先把最关键的提到前面
猜想四
我们在访问一个https网站时 第一步就是发送Client Hello,而RST也正好发生在Client Hello之后,那我们来看看Client Hello的时候到底发生了什么
我们可以直接在二进制内容中看到明文的cn.nytimes.com Server Name信息
GFW不需要任何解密 只需要根据明文的Server Name中的域名进行判断,向客户端发送一个TCP RST的包即可
我的wireshark抓包日志:nytimes.zip
后面的实验并非关键问题,我就懒得在重新做一次了结果一样
猜想一
会不会是因为gfw利用openssl分析IP的证书从而进行封锁的呢(例如:echo | openssl s_client -connect $ip:443 2>/dev/null | openssl x509 -text | grep -A1 "Subject Alternative Name:" | tail -n1 | tr -d ' ' | tr ',' '\n'
发现有关关键字的域名就封锁,于是为了验证我的猜想,我直接拿现在这个IP进行测试
echo | openssl s_client -connect 54.182.4.198:443 2>/dev/null | openssl x509 -text | grep -A1 "Subject Alternative Name:" | tail -n1 | tr -d ' ' | tr ',' '\n'
DNS:*.predix.io
DNS:predix.io
可以说和cn.nytimes.com没有一丁点关系
为了进一步验证
我要到了一台垃圾香港服务器,IP:45.124.64.82 使用Haproxy进行代理
我的配置如下:
我强制要求了TLS1.3并且也手动编译openssl master分支支持TLS1.3 draft-21 ,并且也手动编译了Haproxy 1.8-dev2-f57a29a 以支持openssl master 和tls1.3 draft-21 ()并且我还启用了AEAD CHACHA20-POLY1305级别的加密算法
Haproxy配置文件
global
#uid root
#gid root
daemon
maxconn 100
tune.ssl.default-dh-param 2048
ssl-default-server-options force-tlsv13
ssl-default-bind-options force-tlsv13
ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
ssl-default-server-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
defaults
timeout connect 1000
timeout client 5000
timeout server 5000
frontend www-https
bind :443
mode http
#option forwardfor
acl www-https hdr_reg(host) -i ^[a-zA-Z0-9_]+.google.com
acl www-https hdr_reg(host) -i ^[a-zA-Z0-9_]+.youtube.com
acl www-https hdr_reg(host) -i ^[a-zA-Z0-9_]+.googlevideo.com
acl www-https hdr_reg(host) -i ^[a-zA-Z0-9_]+.kik.com
acl www-https hdr_reg(host) -i ^[a-zA-Z0-9_]+.nytimes.com
reqadd X-Forwarded-Proto:\ https
default_backend google
#redirect scheme https if !{ ssl_fc }
backend google
mode http
balance roundrobin
redirect scheme https if !{ ssl_fc }
你们可以用任何方案对这个IP进行证书查询查到算我输
我自行测试的结果
openssl s_client -trace 45.124.64.82:443 2>/dev/null | openssl x509 -text
unable to load certificate
140586051438464:error:0909006C:PEM routines:get_name:no start line:crypto/pem/pem_lib.c:748:Expecting: TRUSTED CERTIFICATE
接着我再次修改了一下服务器的设置
加入了acl www-https hdr_reg(host) -i ^[a-zA-Z0-9_]+.nytimes.com 确保测试的一致性
然后感谢@Sharkkkk 提供的方案 忽略掉RST
客户端和服务器都加入了iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP 结果请见#64
接着我把我自己的IP带入hosts为了一致性我把ip给了cn.nytimes.com
继续hping3测试不停,并且访问网页
从截图中可以看到访问之前hping3 -S -p 443 是无干扰的
在尝试打开网页后 出现了干扰丢包的情况,并且依旧是在SYN后的第二步ACK确定是否做了正确的应答的时候,就很明显没有了,然后直接RST重置,GG
所以很明显不怪CA证书
猜想二
TCP协议的问题(并不是TCP的锅)
因为每次被重置都是在TCP握手的阶段,SYN-ACK-RST 所以我们无法排除但是也无法确认一定就是TCP的问题,这一块我也还在一点一点的测试等我有了确切的证据和验证方案我会继续更新,现在反而让我更加感觉是协议的问题了
猜想三
端口封锁(非核心原因)
感谢@Sharkkkk 提供了一个博客,里面提到了利用端口转发来处理针对端口的过滤,
首先我说说这个方案的原理,利用iptables进行端口转发
首先我们先确定我们要转发的IP和域名:
216.58.203.36 www.google.com
//在传东西稍后补图好了XD
好接下来我们在vps和手机上都进行开启ipv4转发的操作
vim /etc/sysctrl.conf加入net.ipv4.ip_forward=1
并且sysctl -p使其生效,我们可以看到
ipv4转发已经生效
android在有root的情况下直接shell:
echo 1 > /proc/sys/net/ipv4/ip_forward
开启ipv4转发
接着开始我们的骚操作233
iptables -t nat -A PREROUTING -d 45.124.64.82 -p tcp --dport 23333 -j DNAT --to-destination 216.58.203.36:443
iptables -t nat -A POSTROUTING -j MASQUERADE
首先让服务器的23333端口去中转216.58.203.36的443端口 并且修改数据包流向
然后就是Android上面的操作了
首先iptables -t nat -A OUTPUT -d 216.58.203.36 -p tcp --dport 443 -j DNAT --to-destination 45.124.64.82:23333
iptables -t nat -A INPUT -d 45.124.64.82 -p tcp --dport 23333 -j SNAT --to-source 216.58.203.36
将216.58.203.36 443端口的请求转发至45.124.64.82:23333
接着
端口之间的互相转发完成啦~
让我们实际测试下
curl -av https://216.58.203.36
输出
curl -av https://216.58.203.36
- Rebuilt URL to: https://216.58.203.36/
- Trying 216.58.203.36...
- Connected to 216.58.203.36 (216.58.203.36) port 443 (#0)
- libcurl is now using a weak random seed!
- ALPN, offering http/1.1
- Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@strength
- successfully set certificate verify locations:
- CAfile: none
CApath: /system/etc/security/cacerts - TLSv1.2 (OUT), TLS handshake, Client hello (1):
- TLSv1.2 (IN), TLS handshake, Server hello (2):
- TLSv1.2 (IN), TLS handshake, Certificate (11):
- TLSv1.2 (IN), TLS handshake, Server key exchange (12):
- TLSv1.2 (IN), TLS handshake, Server finished (14):
- TLSv1.2 (IN), TLS handshake, Server finished (14):
- TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
- TLSv1.2 (OUT), TLS change cipher, Client hello (1):
- TLSv1.2 (OUT), TLS handshake, Finished (20):
- TLSv1.2 (IN), TLS change cipher, Client hello (1):
- TLSv1.2 (IN), TLS handshake, Finished (20):
- SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
- ALPN, server accepted to use http/1.1
- Server certificate:
- subject: C=US; ST=California; L=Mountain View; O=Google Inc; CN=www.google.com
- start date: Sep 26 11:27:05 2017 GMT
- expire date: Dec 19 11:00:00 2017 GMT
- subjectAltName does not match 216.58.203.36
- SSL: no alternative certificate subject name matches target host name '216.58.203.36'
- Closing connection 0
- TLSv1.2 (OUT), TLS alert, Client hello (1):
curl: (51) SSL: no alternative certificate subject name matches target host name '216.58.203.36'
看来TLS部分一切顺利啊
然后我用Chrome实际进行了访问,确实能够正常访问,然而只是第一次可行
接着我们再次进行curl -av https://216.58.203.36 测试
就卡在了
51|capricorn:/ $ curl -av https://216.58.203.36 - Rebuilt URL to: https://216.58.203.36/
- Trying 216.58.203.36...
- connect to 216.58.203.36 port 443 failed: Connection timed out
- Failed to connect to 216.58.203.36 port 443: Connection timed out
- Closing connection 0
curl: (7) Failed to connect to 216.58.203.36 port 443: Connection timed out
于是又一次gg bomm
2333怀疑是公开测试的问题于是我私下换了端口 把23333 换到了 2333
然后还是之前的配置
第一次curl -av https://216.58.203.36尝试依旧成功
然后继续打开Chrome~成功访问web
接着再次curl -av https://216.58.203.36
capricorn:/ $ curl -av https://216.58.203.36
- Rebuilt URL to: https://216.58.203.36/
- Trying 216.58.203.36...
- connect to 216.58.203.36 port 443 failed: Connection timed out
- Failed to connect to 216.58.203.36 port 443: Connection timed out
- Closing connection 0
curl: (7) Failed to connect to 216.58.203.36 port 443: Connection timed out
然后我又去换了一个新的IP继续测试
还是之前的配置
第一次curl -av https://216.58.203.36尝试依旧成功
然后继续打开Chrome~成功访问网页
然后再次curl -av https://216.58.203.36
curl -av https://216.58.203.36
- Rebuilt URL to: https://216.58.203.36/
- Trying 216.58.203.36...
- Connected to 216.58.203.36 (216.58.203.36) port 443 (#0)
- libcurl is now using a weak random seed!
- ALPN, offering http/1.1
- Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@strength
- successfully set certificate verify locations:
- CAfile: none
CApath: /system/etc/security/cacerts - TLSv1.2 (OUT), TLS handshake, Client hello (1):
- TLSv1.2 (IN), TLS handshake, Server hello (2):
- TLSv1.2 (IN), TLS handshake, Certificate (11):
- TLSv1.2 (IN), TLS handshake, Server key exchange (12):
- TLSv1.2 (IN), TLS handshake, Server finished (14):
- TLSv1.2 (IN), TLS handshake, Server finished (14):
- TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
- TLSv1.2 (OUT), TLS change cipher, Client hello (1):
- TLSv1.2 (OUT), TLS handshake, Finished (20):
- Unknown SSL protocol error in connection to 216.58.203.36:443
- Closing connection 0
curl: (35) Unknown SSL protocol error in connection to 216.58.203.36:443
TLS握手完成(很玄学第一次完成了的),然后gg
Timeline cannot be loaded
The timeline is currently unavailable due to a system error. Try reloading the page. Contact support if the problem persists.