Total Pageviews

Tuesday, 19 March 2013

对TCP连接被重置解决方案的探究――跨过GFW通向自由网络的可行途径

中国的网络环境很复杂,同时中国也是对互联网高度控制的国家之一,当然仅限于大陆。而控制中国网民自由上网的网络海关正是大名鼎鼎的 GFW(Great Fire Wall,长城防火墙),GFW的工作原理就是重置TCP连接,那么在此就不得不提一下TCP协议三次握手的简单原理。

根据TCP协议的规定,用户和服务器建立连接需要三次握手:第一次握手用户向服务器发送SYN数据包发出请求(SYN, x:0),第二次握手服务器向用户发送SYN/ACK数据包发出回应(SYN/ACK, y:x+1),第三次握手用户向服务器发送ACK数据包发出确认(ACK, x+1:y+1),至此一个TCP连接建立成功。其中x为用户向服务器发送的序列号,y为服务器向用户发送的序列号。

现在我们来谈谈GFW的工作原理。GFW负责监控全国的TCP连接,当发现敏感词时就会介入,将服务器发回的SYN/ACK包改成SYN/ACK, Y:0,这代表TCP连接被重置,用户便主动放弃了连接,提示连接失败。可以看出,其实GFW就是在欺骗用户,让用户误认为服务器拒绝连接,而主动放弃继 续与服务器连接。

因为GFW是工作在TCP协议层上,这看似坚不可破,可我们不应忽略的是,检测全国TCP连接可不是一个简单的工作,即便是上千台的分布式计算机也 未必有能力逐个TCP连接监控,那么GFW是如何做到的呢?很简单,GFW只在连接发起时监测第一个TCP连接,这样就可大大提高GWF的工作性能。但就 是这样一个提供性能的方法却给GFW留下了一个致命的漏洞,我今天探讨的方案也正是建立在这个漏洞基础上。

首先我们正常发送SYN数据包到服务器进行第一次握手,之后服务器发回SYN/ACK到用户进行第二次握手,接下来,用户继续发送第一次握手时的 SYN数据包,此时GFW根据TCP协议规则认为本次TCP连接结束,停止了对本次TCP连接用户的监视,而服务器知道TCP连接尚未建立怎么会结束,所 以忽略了这个数据包而不受影响。但我们至此只完成了工作的一半,因为GFW是双向监视的,服务器依然被GFW监控,如果服务器能发回一个连接重置的数据 包,GFW也会停止对服务器的监视。虽说来容易,可服务器并不收用户控制,如何才能让服务器随用户的意愿发送连接重置数据包呢?根据TCP协议规则,如果 用户发回的数据包有错误,服务器就会发回连接重置数据包,前文提到,第三次握手时用户发送的ACK数据包应该是ACK: x+1:y+1,可如果用户发送的是ACK: x+1:y,那么服务器自然会发生连接重置数据包。当GFW收到这个包时,认为服务器也重置了此连接也就不再监视服务器了。但这个连接重置包被用户接收后 不能主动放弃连接,这样就需要用户忽略这个重置数据包,不过用户这边我们说了算,怎么搞都行。至此,GFW不再监视本次TCP的用户和服务器,双方可以自 由通信。

下面我再总结下这个TCP连接的过程:
用户-[SYN, x:0]->GFW-[SYN, x:0]->服务器{第一次握手}
用户<-[SYN/ACK, y:x+1]-GFW<-[SYN/ACK, y:x+1]-服务器{第二次握手}
用户-[RST, x:0]->GFW[认为用户TCP连接结束]-[RST, x:0]->服务器{忽略}
用户-[ACK, x+1:y]->GFW[停止监视用户]-[ACK, x+1:y]->服务器{判断是坏包}
用户{忽略}<-[RST, y:0]-GFW[认为服务器重置连接]<-[RST, y:0]-服务器{重置连接}
用户-[ACK, x+1:y+1]->GFW[停止监视服务器]-[ACK, x+1,y+1]->服务器{第三次握手}

这样,一个三次握手的过程被我们改成了六次握手,成功骗过了GFW,好戏正式开始了。

下面再来谈谈GFW的一些事吧。GFW并不是我们想象中的那么强大,相反它漏洞百出,非常脆弱。因为GFW是基于分布式的,所以修补漏洞十分困难, 如果GFW更改监视规则而监视所有TCP数据包则会使性能大大降低。另外GFW会记录下访问过敏感信息的ip一段时间,使该ip无法继续与相应服务器连 接,那么这个记录ip的缓存区就一定有上限,自然就有溢出的可能,如果大量ip访问敏感信息,GFW就会因为这个缓存区溢出而无法检视其他的TCP。没 错,这个其实就是DDoS,2010年1月3日前后的“解封”也听说是与北京GFW被DDoS有关。听说GFW有学生参与,而这些筑墙的哈工大和北邮的同 学们能力不一,导致GFW模块质量参差不齐,这也是GFW存在漏洞的重要因素之一。

但最终技术无罪,只是技术被政治所利用才是最大的悲哀。其实不止是中国,美国用来做网络深度检测的CNCI,预算300亿美元,是GWF的多少倍。区别只在于中国的执事者做事太笨拙而又没有底线,导致普通人也能看出破绽。

最后,对这个方案的探索体现了人们对技术的热衷,而对翻墙的研究则体现了人们对事实与自由的追求。而不论是从对技术热衷的角度还是从对事实与自由追求的角度,我都很愿意成为他们中的一员。

参考文献:
[1]深入理解GFW,http://gfwrev.blogspot.com/2010/03/gfw.html (墙外)
[2] “西厢计划”原理小解,http://blog.youxu.info/2010/03/14/west-chamber/
--------------------------------------------------------------------------------

简述TCP三次握手过程,并说明为什么要3次握手

TCP 三次握手TCP 连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。以下步骤概述了通常情况下客户端计算机联系服务器计算机的过程:1. 客户端向服务器发送一个SYN置位的TCP报文,其中包含连接的初始序列号x和一个窗口大小(表示客户端上用来存储从服务器发送来的传入段的缓冲区的大 小)。2. 服务器收到客户端发送过来的SYN报文后,向客户端发送一个SYN和ACK都置位的TCP报文,其中包含它选择的初始序列号y、对客户端的序列号的确认 x+1和一个窗口大小(表示服务器上用来存储从客户端发送来的传入段的缓冲区的大小)。3. .客户端接收到服务器端返回的SYN+ACK报文后,向服务器端返回一个确认号y+1和序号x+1的ACK报文,一个标准的TCP连接完成。TCP 使用类似的握手过程来结束连接。这可确保两个主机均能完成传输并确保所有的数据均得以接收 TCP Client Flags TCP Server 1 Send SYN (seq=x) ----SYN---> SYN Received 2 SYN/ACK Received <---SYN/ACK---- Send SYN (seq=y), ACK (x+1) 3 Send ACK (y+1) ----ACK---> ACK Received, Connection Established w: ISN (Initial Sequence Number) of the Client x: ISN of the Server.
----------------------------------------------------------------

用 iptables 解决某些时候抢先应答的 RST 造成的异常中断: 忽略RESET 保证数据畅通



一个真实的例子 由于可能触发了什么东西 A到B的数据交互会被一个抢先的RST回应干扰
简单解决是忽略这种类型的全部 RST 包
iptables -I INPUT -p tcp --tcp-flags SYN,FIN,RST,URG,PSH RST -j DROP

 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:0x2F/0x04

顺便复习备忘下

1 SYN
 14+20+20+20(options)
eth 6+6+2 dstmac+srcmac+0800
ip 45+ds+2ip.len(60)+2ip.id+flags(DF 0x04).2frag_offset+ttl+proto tcp 06+2checksum+src+dst
tcp 2srcport+dstport+4seq+4+1hdr_len+1flags(0x02syn 04rst 10ack)+2window+2checksum+2+options
options 4mss+2sack(04 02)+10timestamp+1nop+7window_scale
seq=0 mss=1460 04c751df

2 SYN+ACK 
tcp 
seq=0 ack=1 len=0 0540780e 04c751e0
1flags(0x12 syn+ack)

3 ACK
tcp 20+12(1nop+1nop+10timestamp)
1flags(0x10 ack)
seq=1 ack=1 04c751e0  0540780f

4 PSH+ACK
ip ip.len=169
tcp len=117
seq=1(next118 169-20-32=117) 04c751e0 0540780f
1flags(0x18 psh+ack) tcp segment data=117

5 RST
ip 2ip.len(52) flags(DF 0x00)
tcp
seq=1 0540780f ack(04c75255)
flags(0x04 RST ack not set)

6 ACK
tcp
seq=1 ack=118
1flags(0x10 ack)

7 RST
ip 2ip.len(40) flags(DF 0x04)
tcp seq=118 04c75255 ack(00000000)
flags(0x04 RST)


1 2 3 syn syn+ack ack
4 PSH+ACK (GET ...)
6 ACK
由于5 抢先 RESET 只好 7 RESET


解决:
忽略异常RESET包  (不包括端口未开放的RST+ACK)
iptables -I INPUT -p tcp --tcp-flags SYN,FIN,RST,URG,PSH RST -j DROP
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:0x2F/0x04