今日openVPN連不上了,抓包發現剛一握手結束便收到了一個RST包,導致一直連不上。
可以看到,三次握手剛完畢,客戶端發送第一個控制消息到服務端,便收到了服務端發送的RST數據包,一直如此。
應該是有中間設備搞的鬼,于是我又到服務器端抓取了些數據:
果然的,服務器也收到了RST數據包,于是兩者的連接便斷開了。
再仔細分析下客戶端的RST數據包:
IP包的序號是12345,TTL是120。再看正常的數據包:
IP包的序號是0,TTL是46。很明顯RST數據包的TTL比正常的要大,而且每次RST的IP序號都是12345,應該是GFW沒錯了。
正常情況下初始的TTL是64,正常收到的TTL是46,跳數是15,說明我的電腦到服務器之間經過了15個路由設備。
為了證明這點,查看服務端收到的正常數據包:
服務器收到的TTL是50,因為我的電腦還要經過內部的一個路由器,所以TTL差了1。
同時查看服務端RST數據包的TTL值:
TTL值為117,因此得到的信息如下:
客戶端->服務器:15、GWF->服務器:117、GFW->客戶端:120。
假設GFW每次發送的TTL值都固定不變且為x,則有:x-117+x-120=15;得x=126。
所以GFW和我的電腦的跳數應該是6:
圖示的應該就是GFW的位置。
接下來問題來了,她是怎么識別出openVPN流量的呢?
我猜測是根據數據包的特征來識別的,那么我單獨發送單個數據包,應該也會返回RST數據,根據這一理論,我用scapy發送了單個的數據包,內容和三次握手之后客戶端發送的第一個數據包一樣,但結果是失望的,并沒有收到RST數據包。
于是進一步猜測,TCP連接之后再發送相應的數據包,應該能收到RST,于是又根據這一理論,寫下了如下代碼:
from?scapy.all?import?* vpn_payload?=?"\x00\x0e\x38\x24\x5d\x21\xaa\x3a\x11\x2f\xb3\x00\x00\x00\x00\x00" conf.verb?=?0 vpn_s?=?IP(dst="yovey.me",id=12345)/TCP(sport=58620,dport=1194,flags="S",seq=0) print?"sending?syn" vpn_s.show() ans0,unans0?=?sr(vpn_s) print?"recv?packet,seq?=?",ans0[0][1].seq ans0[0][1].show() vpn_sa?=?IP(dst="yovey.me",id=12346)/TCP(sport=58620,dport=1194,flags="A",seq=1,ack=ans0[0][1].seq+1) print?"sending?ack" vpn_sa.show() ans1,unasn1?=?sr(vpn_sa,timeout=1) vpn?=?IP(dst="yovey.me",id=12347)/TCP(sport=58620,dport=1194,flags="PA",seq=1,ack=ans0[0][1].seq+1)/vpn_payload print?"sending?vpn?payload" ans2,unasn2?=?sr(vpn) ans2[0][1].show()
運行程序,還是沒有收到RST數據包。
于是我打開tcpdump,抓取了發包過程的數據包,發現了問題:
在服務器返回syn+ack之后,客戶端居然發送了RST到服務器,導致連接斷開。經過短暫的思考,才明白客戶端網卡在收到來自服務器的syn+ack之后,發現并沒有進程在監聽該數據包的端口,于是發送了RST數據包給服務器。
必須讓客戶端不發送RST數據包才行,想到可以通過iptable來過濾數據包,于是在iptable中添加如下規則:
iptables?-t?filter?-A?OUTPUT?-p?tcp?--tcp-flags?RST?RST?-j?DROP
再運行程序,一切都在計劃之中:
還是熟悉的IP序號,還是熟悉的TTL,看來GFW已經可以根據連接來識別流量了,真是下了血本啊。
想到建立連接,我立馬聯想到不用建立連接的UDP,是不是UDP數據只需要根據單個數據包就能識別了?于是將服務器配置成UDP模式,再次打開openVPN,特么的居然連上了!于是問題解決了,將配置改成UDP就能正常連接了。
上一篇:如何保護網絡個人隱私