TCP劫持
此文自http://www.techrepublic.com/article/TCP-hijacking/5033594翻译而来,文章在翻译过程中根据译者个人理解有轻微改动。文章著作权归原作者所有,译者只保留中文著作权。本文只作为技术讨论之用,如因使用文中提到的技术而导致的任何良性和不良后果,本文概不负责。
译者序:TCP劫持是很老的技术,之所以翻译这篇文章是认为它揭示了系统可能存在的一些问题和TCP的协议的细节。此外,这篇文章详细阐述了在发起TCP劫持攻击时的细节思路。
1.  前言
2.  TCP协议简介
3.  如何发起TCP劫持
4.  跨网段攻击
a.  方法1:猜测ISN数值
b.  方法2:利用信任关系进行rsh服务器攻击
5.  总结
1.前言
不久前,TCP劫持是入侵者获得对因特网服务器未授权访问的最流行手段之一。即便在如今,由于许多系统管理员仅仅是知道这样的潜在威胁但却并未了解其实现所使用的技术细节,导致TCP劫持仍然很常见。他们并不能阻止那些清楚了解他们工作的狡猾的黑客。为了建立有效的防范机制,你有必要了解TCP劫持的细节。这也是为什么我建议你先回顾TCP协议的原因。
2.TCP协议简介
Transmission Control Protocol(TCP) 是TCP/IP分层协议模型中传输层的基本协议之一。TCP协议使得数据包传输的纠错成为可能。它建立了从一台主机到另一台主机的逻辑连接-一种我们可以认为是不会发生错误的虚拟连接。TCP协议的低层算法控制数据包队列和损坏数据包的重发。TCP按数据包的Sequence Number域来处理它接收到所有数据包,并且将它们排序。所有这些对于虚拟连接都是透明的,并且将会随连接会话的结束而消失。TCP协议是TCP/IP协议族中唯一提供额外数据包和连接认证识别机制的协议。而这恰好解释了为什么应用层的诸多协议,如SMTP,FTP和TELNET都使用TCP协议来访问远程主机。
就TCP数据包的识别而言,TCP包头由两个32位数组成,它们都被用作计数器,分别是Sequence Number和Acknowledgment Number。另外还有个6位长的控制字段(也叫命令字段),可以是以下值:
  URG: Urgent pointer field significant 
  ACK: Acknowledgment field significant 
  PSH: Push function 
  RST: Reset the connection 
  SYN: Synchronize Sequence Numbers 
  FIN: No more data from sender
TCP连接建立举例
假设主机A想要建立到主机B的TCP连接。则主机A发送给主机B一个这样的数据包:
A->B:SYN,ISSa
在A发往B的信息中,命令字段的SYN置位,并且Sequence Number的初始值设置为32位数ISSa。在主机B接收到这个数据包后,它作出响应:
B->A:SYN,ACK,ISSb,ACK(ISSa+1)
该响应消息设置命令字段为SYN和ACK。主机B设置Sequence Number的初始值为ISSb,并且设置Acknowledgment Number为ISSa(从主机A发来的数据包)加1。为完成三次握手,主机A发送数据包:
A->B:ACK,ISSa+1,ACK(ISSb+1)
该数据包设置命令字段为ACK。而Sequence Number设置为ISSa加1,Acknowledgment Number设置为ISSb加1.在该数据包发送之后,主机A就完成了3次握手。一个在主机A和B之间的TCP连接就建立了。从此,主机A就可以通过这条虚拟连接线路发送实际的数据了:
A->B:ACK,ISSa+1,ACK(ISSb+1);DATA
你可以在下图中看到TCP连接建立的整个过程:


3.如何发起TCP劫持
在上面描述的TCP连接建立例子中,TCP连接双方的唯一识别信息就是Sequence Number 和Acknowledgment Number 两个32位数。为了生成一个伪TCP数据包,入侵者唯一需要做的就是获得特定TCP连接的当前识别信息:Sequence Number 和Acknowledgment Number。而另一方面,通过分析基于TCP实现的FTP和Telnet协议可以发现,识别FTP和TELnet数据包的工作都是由TCP协议处理,再无其他;因此,入侵者唯一需要做的就是获得特定TCP连接(如TELNET连接)的当前识别值,即当前的Sequence Number和Acknowledgment Number。然后,入侵者从任意一台联网的主机,构造目标地址为特定TCP连接其中一方,源地址为另一方的TCP数据包即可。
FTP和Telnet协议都不会校验数据包来源的IP地址。当这些协议得到入侵者构造的伪数据包,协议会假设数据包是来源于TCP连接中另一方的合法数据包,并且发送响应包到(入侵者构造的数据包中设置的IP地址)。随后,原来的TCP连接会由于计数器不匹配而断开连接。
对于上述类型的攻击,入侵者需要知道的仅仅是两个用于TCP协议识别信息的32位数的当前值。那如何获得该信息呢?如果攻击者和被攻击主机(以下简称靶机)在同一个网段或者靶机所有数据包都要通过攻击者所在的网段,那么攻击者所需要做的仅仅是捕获并分析网络通信数据。
4.跨网段攻击
  更复杂的是跨网段攻击-攻击者和靶机在不同的网段内。此时,获得Sequence Number和Acknowledgment Number就不那么容易了。入侵者此时有两种方法解决:第一种是使用数学方法猜测TCP连接的初始数值;第二种方法是利用TCP连接中的识别部分,通过rsh 服务攻击。
方法1:猜测ISN数值
关于网络的算法是如何生成初始的序列数值(Initial Sequence Number,ISN)的呢?显然,从安全的角度,最好是通过软件或硬件的具有极大周期的随机数发生器来随机生成ISN。从而,每个新的ISN不依赖于前一个ISN的值;所以,攻击者即使想要从理论上找到一个ISN发生器的函数关系都是不可行的。
然而,TCP协议说明的作者和操作系统内核开发者并没有完全实现这样的ISN随机发生器。例如,TCP协议说明建议该32位计数器每4微妙增1。那么,在实际的实现中又是怎样的呢?在早期的伯克利兼容的UNIx内核中,该计数器的值每秒增128并且每个新连接增64。分析Linux内核源代码可以知道,Linux并非随机生成ISN,而是依赖当前时间的变量(mcs代表以微妙计的当前时间,s是从epoch得到的以秒计的当前时间):
ISN=mcs+s*1000000
在其他系统中也同样存在这个问题,Windows NT 4.0系统,ISN的值每毫秒乘10倍(ms表示毫秒计的时间):
ISN=ms*10
如果操作系统的网络子系统使用时间相关的ISN发生器,入侵者就能找到某个函数,该函数能够覆盖所有可能的ISN值。由于该函数与具体的操作系统实现相关,入侵者可以构造一个通用的ISN发生器函数(mcs是以微妙通常是最小的计时单位,这依赖于具体硬件计的时间,ms是以毫秒计的时间,s是以秒计的时间):
ISN=F(mcs,ms,s);
这是通用公式,我们可以假设ISN发生器函数依赖于微妙。最后,最可能有用的函数往往是一个简单的线性函数。
当我们指定了ISN猜测的相关基本单位和函数依赖后,就距离获得操作系统的ISN发生器更近了。首先,可以分析操作系统内核的源代码。通常操作系统的源代码是不能得到的,除了Linux和三种BSD变体,它们都是开源的。
因此,入侵者需要通过其他的方式获得ISN发生器和mcs参数之间的函数依赖关系。通常,入侵者把操作系统当作黑盒,并实现请求-应答测试。大量的正常TCP连接建立请求被发往受害系统,随后入侵者在每个时间间隔内,接收到同等数量(不考虑丢包等因数)的当前ISN值。入侵者以微妙为单位,记录应答的时间周期。最终,入侵者将得到一张包含离散ISN值和对应时间值的表(ISNn是从测试开始起在时间tn时ISN的值,测试开始时间定义为0):
ISN0  ISN1  ISN2 ~ISNn-1  ISNn
t0  t1  t2~tn-1  tn
现在,入侵者可以很容易根据上述对应表,通过数学方法生成ISN的函数依赖关系:ISN=f(t),此处自变量t是t0~tn之间的值。
当得到ISN发生器函数之后,入侵者就能很容易从上个ISN的值得到其他ISN值,从而能够基于时间预测下个可能的ISN。
由于网路数据包传输的往返时间关系,往返时间越短越有可能得到可靠的函数关系。往返时间越长,入侵者就越要考虑往返时间这个环境因素。(译者注:可以在得到函数关系前,测试往返时间。如大家熟知的ping -t命令。通过求得平均往返时间来考虑这个因素的影响。最理想的就是在稳定的网络环境下得到ISN函数关系。)当然,其实入侵者其实不必直接在受害机上测试函数关系,完全可以测试一台和受害机具有同样操作系统的主机,然后在其上做ISN函数测试。
现在来看下在实际中Linux和Windows NT 4.0系统的实际结果。下面是Linux系统在有限时间周期内的示例结果:
ISN  65135  134234  202324  270948  338028
t  2759  5685  8560  11462  14303
下图则是上表内容的图形表示:
在得到服务器和客户机操作系统的ISN发生函数后,攻击者则开始侦听服务器系统,并且等待从客户机发来的连接请求。当连接建立后,攻击者计算在连接建立时使用的可能的ISN值。由于攻击者计算得到的ISN值是在有限范围内的可能值,攻击者将不得不猜测结果。当然,如果攻击者不得到函数关系而直接猜测,将必须猜测2^64次,这是不现实的。而作了上述分析之后,攻击者大约需要发送10000个数据包来完全囊括所有可能的正确值。
方法2:利用信任关系进行rsh服务器攻击
所有Unix类系统都有信任主机的概念。所有(译者注:在一个网络环境中,任何一台主机A度可以试图登录和访问到lan/internet 的一台主机B,这台unix主机B为安全起见可以拒绝任何用户登录自己的系统,也可以有选择的让某些用户登录自己的系统。如果该主机B对某些用户特别信任,就像人们对自己的好朋友和家人特别信任一样,就不需要他们提供用户密码直接登录自己的系统,省去了身份的验证。
主机A始终信任远程网络上的某一台unix主机B,那么就把主机B称为信任主机,而把信任主机上的用户称为信任用户。详见:http://dbblog.itpub.net/post/23779/212812)信任主机都能不经身份验证 和识别信息直接访问服务器。为通过信任主机不经身份验证访问服务器,必须使用远程服务(r-service)协议。大多数Unix类系统都有rhost文件,其中列举了信任主机的主机名和ip地址。每个远程服务都使用这个文件确定一台主机是否是信任主机。信任主机的唯一识别信息就是ip地址。几乎所有远程服务都使用TCP协议作为基础协议。
远程服务中有个程序是远程shell,即rsh。rsh能让信任主机发送命令到受害机的shell上并执行。在这种攻击中,仅仅发送命令,但却不能收到执行结果。对于攻击者来说,最大的困难就是需要发送源地址为信任主机IP地址的数据包,所以所有的结果数据都被发送到信任主机,而不会发送到攻击机。
R.T. Morris第一个提出这种提出这种攻击模式,下图描述了这种攻击的细节:

设主机A存在于主机B的信任列表,C是攻击机。C向B的TCP端口(如邮件,echo等)打开一个TCP连接。主机C就得到了B当前的ISNb值。C伪装成A,并向B发起TCP连接请求:
C(A)->B:SYN,ISSx
当B收到请求,它分析数据包的源IP地址,并认为是从信任主机A发来的数据。主机B以新的ISNb’发送响应包到A:
B->A:SYN,ACK,ISNb’,ACK(ISSx+1)
因此,主机C不会收到这个响应包。使用前一个ISNb和攻击者获得的生成ISNb’的方法,主机C能够发送新的数据包到B:
C(A)->B:ACK,ISSx+1,ACK(ISNb’+1)
为发送这个数据包,主机C必须尝试各种可能的ISNb’+1的可能值。
当数据包发送成功,并被主机B认为是合法后,攻击者必须解决下面的问题。当主机B收到响应后,它认为这是从A发来的数据包,它会发出连接确认到主机A。但由于A并没有发送过任何数据,A会发出RST置位的包到主机B,使得该连接被重置(即关闭)。因此攻击机A必须使用拒绝服务攻击来使得A无法响应主机B。
结果,主机B上的远程服务r-service会认为他已经建立与信任主机A之间的TCP连接,而实际上这是来自攻击机C的连接。攻击者C将能够通过r-service在主机B上执行命令,但却不能收到任何的回显数据。
5.总结:
TCP劫持技术是一种很老的技术。在凯文*米特尼克,世界头号黑客,在1995年使用TCP劫持侵入Tsutomu Shimomura的工作站并且TCP劫持技术细节被公布后,它很快成为黑客手中的重要手段。为实施这样的攻击,攻击者必须具备一些专业的知识。当然,操作系统的开发者总能够找到一些策略来防御这种攻击,或者至少设置障碍。
这种技术现在已经很少使用,但是我必须说因特网上大约35%的主机仍然存在这种潜在威胁。这并非操作系统的问题,而是信任主机和信任服务的问题,如rsh,rlogin和rcmd。