【参加讨论】 tcp/ip序列号生成方法
tcp的initial sequence number(isn)的预测
(图1)
正常的tcp连接基于一个三次握手(3-way handshake),一个客户端(client)向服务器(server)发送一个初始化序列号isnc, 随后,服务器相应这个客户端ack(isnc),并且发送自己的初始化序列号isns,接着,客户端响应这个isns(如下图),三次握手完成。
c ---〉s: (isnc)
s ---〉c: ack(isnc)+ isns
c ---〉s: ack(isns)
c ---〉s: data
and / or
s ---〉c: data
下面,我以windows2000 advanced server为例,来说一下两台主机是如何进行三次握手。
(图2)
(图3)
我们可以看到:
1) smartboy首先发送一个seq:32468329的包给服务器202.116.128.6。
2) 然后, 202.116.128.6响应主机smartboy, 它送给smartboy自己的
seq:3333416325 而且响应smartboy的ack:3240689240。
3) smartboy再响应服务器202.116.128.6, seq:3240689240, ack:3333416326。
三次握手完毕,两台几建立起连接。
可以看出,在三次握手协议中,clinet一定要监听服务器发送过来的isns, tcp使用的sequence number是一个32位的计数器,从0-4294967295。tcp为每一个连接选择一个初始序号isn,为了防止因为延迟、重传等扰乱三次握手,isn不能随便选取,不同系统有不同算法。理解tcp如何分配isn以及isn随时间变化的规律,对于成功地进行ip欺骗攻击很重要。
在unix系统里,基于远程过程调用rpc的命令,比如rlogin、rcp、rsh等等,根据/etc/hosts.equiv以及$home/.rhosts文件进行安全校验,其实质是仅仅根据源ip地址进行用户身份确认,以便允许或拒绝用户rpc。这就给与了那些攻击者进行ip地址欺骗的机会。
让我们看x是如何冒充t来欺骗s,从而建立一个非法连接 :
x---->s: syn(isnx ) , src = t
s---->t: syn(isns ) , ack(isnt) (*)
x---->s: ack(isns+1 ) , src = t (**)
x---->s: ack(isns +1) , src = t, 攻击命令(可能是一些特权命令)
但是,t必须要在第(**)中给出isns, 问题是isns在第(*)步中发给了t(x当然很难截取到),幸运的是,tcp协议有一个约定: isn变量每秒增加250,000次,这个增加值在许多版本比较旧的操作系统中都是一个常量,在freebsd4.3中是125000次每秒,这就给x一个可乘之机。
看一下x是如何猜出isns :
a、首先, x发送一个syn包来获取服务器现在的isns
x ---〉s: (isnx)
s ---〉x: ack(isnx)+ isns# (1)
b、紧接着,x冒充t向服务器发送syn包
x ---〉s: syn(isnx ) , src = t (2)
c、于是,服务器发出一个响应包给t(这个包x是收不到的)
s ---〉t: syn(isns$) , ack(isnt ) (3)
d、x计算isns$:
isns$ = isns# + rtt×increment of isn (4)
其中,rtt(round trip time),是一个包往返x和s所用的时间,可以通过ping 来得到。
(图4)
上图显示了round trip times (rtt) 大概是0。
increment of isn是协议栈的初始序列号每秒钟增加的值,以unix为例,当没有外部连接发生时,服务器的isn每秒增加128,000,有连接的时候,服务器的isn每秒增加64,000。
e、于是,
x ---> s : ack(isns$) (冒充可信主机成功了)
x ---> s : 恶意的命令或窃取机密消息的命令
在评价以下的解决方案时有几点要注意:
1.该解决方案是否很好地满足tcp的稳定性和可操作性的要求?
2.该解决方案是否容易实现?
3.该解决方案对性能的影响如何?
4.该解决方案是否经得起时间的考验?
以下的几种方案各有各的优点和缺点,它们都是基于增强isn生成器的目标提出的。
配置和使用密码安全协议
tcp的初始序列号并没有提供防范连接攻击的相应措施。tcp的头部缺少加密选项用于强加密认证,于是,一种叫做ipsec的密码安全协议的技术提出了。ipsec提供了一种加密技术(end to end cryptographic),使系统能验证一个包是否属于一个特定的流。这种加密技术是在网络层实现的。其它的在传输层实现的解决方案(如ssl/tls和ssh1/ssh2), 只能防止一个无关的包插入一个会话中,但对连接重置(拒绝服务)却无能为力,原因是因为连接处理是发生在更低的层。ipsec能够同时应付着两种攻击(包攻击和连接攻击)。它直接集成在网络层的安全模型里面。
上面的解决方案并不需要对tcp协议做任何得修改,rfc2385(“基于tcp md5签名选项的bgp会话保护)和其他的技术提供了增加tcp头部的密码保护,但是,却带来了收到拒绝服务攻击和互操作性和性能方面的潜在威胁。使用加密安全协议有几个优于其它方案的地方。tcp头部加密防止了hijacking和包扰乱等攻击行为,而tcp层仍然能够提供返回一个简单增加isn的机制,使方案提供了最大程度的可靠性。但实现ipsec非常复杂,而且它需要客户机支持,考虑到可用性,许多系统都选择使用rfc 1948。
使用rfc1948
在rfc1948中,bellovin提出了通过使用4-tuples的hash单向加密函数,能够使远程攻击者无从下手(但不能阻止同一网段的攻击者通过监听网络上的数据来判断isn)。
newsham 在他的论文 [ref_newsham]中提到:
rfc 1948 [ref1]提出了一种不容易攻击(通过猜测)的tcp isn的生成方法。此方法通过连接标识符来区分序列号空间。每一个连接标识符由本地地址,本地端口,远程地址,远程端口来组成,并由一个函数计算标识符分的序列号地址空间偏移值(唯一)。此函数不能被攻击者获得,否则,攻击者可以通过计算获得isn。于是,isn就在这个偏移值上增加。isn的值以这种方式产生能够抵受上面提到的对isn的猜测攻击。
一旦全局isn空间由上述方法来生成,所有的对tcp isn的远程攻击都变得不合实际。但是,需要指出的,即使我们依照rfc 1948来实现isn的生成器,攻击者仍然可以通过特定的条件来获得isn(这一点在后面叙述).
另外,用加密的强哈希算法(md5)来实现isn的生成器会导致tcp的建立时间延长。所以,有些生成器(如linux kernel )选择用减少了轮数的md4函数来提供足够好的安全性同时又把性能下降变得最低。削弱哈希函数的一个地方是每几分钟就需要对生成器做一次re-key 的处理,经过了一次re-key的处理后,安全性提高了,但是,rfc793提到的可靠性却变成另一个问题。
我们已经知道,严格符合rfc1948的isn生成方法有一个潜在的危机:
一个攻击者如果以前合法拥有过一个ip地址,他通过对isn进行大量的采样,可以估计到随后的isn的变化规律。在以后,尽管这个ip地址已经不属于此攻击者,但他仍然可以通过猜测isn来进行ip欺骗。
以下,我们可以看到rfc 1948的弱点:
isn = m + f(sip, sport, dip, dport,
)
其中
isn 32位的初始序列号
m 单调增加的计数器
f 单向散列哈希函数 (例如 md4 or md5)
sip 源ip地址
sport 源端口
dip 目的ip地址
dport 目的端口
哈希函数可选部分,使远程攻击者更难猜到isn.
isn自身的值是按照一个常数值稳定增加的,所以f()需要保持相对的稳定性。而根据bellovin 所提出的,是一个系统特定的值(例如机器的启动时间,密码,初始随机数等),这些值并不 会经常变。
但是,如果hash函数在实现上存在漏洞(我们无法保证一个绝对安全的hash函数,况且,它的实现又与操作系统密切相关),攻击者就可以通过大量的采样,来分析,其中,源ip地址,源端口,目的ip地址,目的端口都是不变的,这减少了攻击者分析的难度。
linux tcp的isn生成器避免了这一点。它每5分钟计算一次值,把泄漏的风险降到了最低。
有一个办法可以做的更好:
取m = m + r(t)
isn = m + f(sip, sport, dip, dport, )
其中
r(t) 是一个关于时间的随机函数
很有必要这样做,因为它使攻击者猜测isn的难度更大了(弱点在理论上还是存在的)。