📄 447.htm
字号:
3.3 ip_glue() <br>
ip_glue()函数将负责将一个所有分片已经到齐的的IP包组合好。当这一步进行时,所 <br>
有 <br>
分片已经 <br>
按顺序排好,并解决了所有的重叠问题。因此其流程相应很简单。 <br>
首先生成一个足够大的(足以容纳所有的分片包长度的总和)新的skbuff: <br>
skb = dev_alloc_skb(len); <br>
if (!skb) <br>
goto out_nomem; <br>
调整一些必要的指针后,就在一个while循环中依次将原有分片的内容用memcoy拷贝到新 <br>
的s <br>
kbuff中。 <br>
kbuff中。 <br>
再进行一些指针调整后,过程结束,将新的skbuff返回。 <br>
3.4 ip_expire() <br>
前面已经提到,每个ipq保留了一个定时器,当一定时间以后组装还没有完成,将清空此 <br>
队 <br>
列。 <br>
定时器的值保留在sysctl_ipfrag_time中: <br>
int sysctl_ipfrag_time = IP_FRAG_TIME; <br>
(在/include/net/ip.h中有#define IP_FRAG_TIME (30 * HZ) ) <br>
此值也可以用sysctl设置。 <br>
定时器的具体实现机制的没有分析。 <br>
4. 2.4系列的变化 <br>
其实如果仔细看一下,2.4的分片组装代码的流程与2.2系列基本相同,不同的是将函数 <br>
的分 <br>
工变化了。 <br>
由于原ipfrag结构保留的结构均可在skbuff中得到,在2.4中将此结构取消了,并对ipq <br>
结构 <br>
做了一些 <br>
修改。其他主要变化有: <br>
1)ip_defrag分成了ip_defrag和ip_frag_queue两部分。 <br>
2)ip_glue换名成ip_frag_reasm,流程基本未动。 <br>
3)现在ipq中用meat保留现有的分片长度的累加值(已经解决重叠),如果此值到达总 <br>
长度 <br>
长度 <br>
,则意味 <br>
着所有的分片到达,因此取消了ip_done函数,不必每次遍历一次链表,因此在效率上有 <br>
了 <br>
较大 提高 <br>
,抗小碎片攻击的能力得到加强。 <br>
5.常见碎片攻击 <br>
IP碎片经常被用来作DOS攻击,典型的例子便是teardrop和jolt2,其原理都是利用发送 <br>
异常 <br>
的分片, <br>
如果操作系统的内核在处理分片重组时没有考虑到所有的异常情况,将可能引向异常的 <br>
流程 <br>
,造成 <br>
拒绝服务(DOS)。 <br>
我们首先要仔细考虑一下linux在处理分片重叠时的办法。 <br>
代码主要在ip_defrag中,首先要遍历链表,定位此分片的位置,具体就是给prev和nex <br>
t两 <br>
指针赋上 <br>
正确数值。然后处理与前面的重合,代码如下: <br>
/* We found where to put this one. Check for overlap with <br>
* preceding fragment, and, if needed, align things so that <br>
* any overlaps are eliminated. <br>
*/ <br>
*/ <br>
if ((prev != NULL) && (offset < prev->end)) { <br>
i = prev->end - offset; <br>
offset += i; /* ptr into datagram */ <br>
ptr += i; /* ptr into fragment data */ <br>
} <br>
注意此处offset已经乘了8,即以byte为单位了。 <br>
举个形象一点的例子,如果有这样两个分片: <br>
offset1=0 end1=256 <br>
------------------------- <br>
| Frag1(先到) |<---------prev <br>
------------------------- <br>
offset2=64 end2=640 <br>
------------------------------------------ <br>
| Frag2(后到) | <br>
------------------------------------------ <br>
处理后变为: <br>
offset1=0 end1=256 <br>
------------------------- <br>
| Frag1(先到) |<---------prev <br>
------------------------- <br>
offset2=256 end2=640 <br>
----------------------- <br>
| Frag2(后到) | <br>
----------------------- <br>
紧接着做与后面分片重叠的处理,代码如下: <br>
/* Look for overlap with succeeding segments. <br>
* If we can merge fragments, do it. <br>
*/ <br>
for (tmp = next; tmp != NULL; tmp = tfp) { <br>
tfp = tmp->next; <br>
if (tmp->offset >= end) <br>
break; /* no overlaps at all */ <br>
i = end - next->offset; /* overlap is 'i' bytes */ <br>
tmp->len -= i; /* so reduce size of */ <br>
tmp->offset += i; /* next fragment */ <br>
tmp->ptr += i; <br>
/* If we get a frag size of <= 0, remove it and the packet <br>
* that it goes with. <br>
*/ <br>
if (tmp->len <= 0) { <br>
if (tmp->prev != NULL) <br>
tmp->prev->next = tmp->next; <br>
else <br>
qp->fragments = tmp->next; <br>
if (tmp->next != NULL) <br>
tmp->next->prev = tmp->prev; <br>
/* We have killed the original next frame. */ <br>
next = tfp; <br>
frag_kfree_skb(tmp->skb); <br>
frag_kfree_s(tmp, sizeof(struct ipfrag)); <br>
} <br>
} <br>
其中if (tmp->len <= 0)判断后面的是为了处理teardrop攻击的,将在后面描述。 <br>
我们继续用图表示,如果有这样两个分片: <br>
offset1=128 end1=960 <br>
----------------------------------- <br>
next------->| Frag1(先到) | <br>
(tmp) ----------------------------------- <br>
offset2=64 end2=320 <br>
------------------------- <br>
| Frag2(后到) | <br>
------------------------- <br>
处理后将变为: <br>
offset1=320 end1=960 <br>
---------------------- <br>
next------->| Frag1(先到) | <br>
(tmp) ---------------------- <br>
offset2=64 end2=320 <br>
------------------------- <br>
| Frag2(后到) | <br>
------------------------- <br>
更复杂的情况不再一一列举,下面我们便可看一下具体利用分片的攻击办法: <br>
(1)Teardrop(CERT CA-97.29,bugtraq id 124) <br>
许多老系统在处理分片组装时存在漏洞,发送异常的分片包会使系统运行异常,teardr <br>
op <br>
便是一个经典的利用这个漏洞的攻击程序。其原理如下(以linux为例): <br>
发送两个分片IP包,其中第二个IP包完全与第一在位置上重合。如下图: <br>
<- len1 -> <br>
------------------------- <br>
| Frag1 | <br>
------------------------- <br>
offset1 end1 <br>
<- len2 -> <br>
------------- <br>
| Frag2 | <br>
------------- <br>
offset2 end2 <br>
在linux(2.0内核)中有以下处理: <br>
当发现有位置重合时(offset2<end1),将offset向后调到end1(offset2=end1), 然后 <br>
更 <br>
改len2的值: <br>
len2=end2-offset2; <br>
注意此时len2变成了一个小于零的值,在以后处理时若不加注意便会出现系统崩溃的问 <br>
题。 <br>
但具体到什么地方出现问题没有追踪过,毕竟这已经是陈年旧事了。 <br>
新的版本检查了这个值的大小,如果出现小于零的情况,则把这个分片丢掉。 <br>
(2)Jolt2(MS00-029) <br>
jolt2是2000年五月份出现的新的利用分片进行的攻击程序,几乎可以造成当前所有的w <br>
indo <br>
ws <br>
平台(95,98,NT,2000)死机。原理是发送许多相同的分片包,且这些包的offset值 <br>
(8190*8=65520 bytes)与总长度(48 bytes)之和超出了单个IP包的长度限制(65536 by <br>
tes <br>
)。 <br>
如下图: <br>
0 65535 <br>
------------......------------- <br>
| Max normal Fragment | <br>
------------......------------- <br>
65520 65568(>65535) <br>
---------------- <br>
|Jolt2 Fragment| <br>
---------------- <br>
在linux中这种几乎马上就会被丢掉,在ip_defrag中有: <br>
/* Attempt to construct an oversize packet. */ <br>
if((ntohs(iph->tot_len) + ((int) offset)) > 65535) <br>
goto out_oversize; <br>
尽管在后面(out_oversize出)对报警信息已经做了net_ratelimit()处理,但在遭受攻 <br>
击 <br>
时 <br>
每5秒便打印一条信息还是很繁人,可以更改net_ratelimit()的间隔时间和干脆 <br>
关掉此条警 <br>
告。 <br>
对windows系统便不知道它是怎么处理的了,一打的话CPU便会达到100%。2000的SP1号称 <br>
已 <br>
解决 <br>
了此问题,但没有试过。 <br>
(3)bugtraq id 376 Linux IP Fragment Overlap Vulnerability <br>
此种攻击对2.0.33内核有效,其实此攻击事实上并不是分片组装算法的问题,而是在在 <br>
实现 <br>
上 <br>
出现了小的纰漏,在ip_glue中有: <br>
if(len>65535) <br>
{ <br>
printk("Oversized IP packet from %s.\n", in_ntoa(qp->iph->sa <br>
ddr) <br>
); <br>
ip_statistics.IpReasmFails++; <br>
ip_free(qp); <br>
return NULL; <br>
} <br>
问题出现在printk上,如果对方一直用超大碎片(len>65535),内核将会无节制的调用 <br>
pri <br>
ntk <br>
报警。而printk这种操作是相当耗费资源的,因此造成DOS。 <br>
在2.0.34版中改成了: <br>
NETDEBUG(printk("Oversized IP packet from %s.\n", in_ntoa(qp->iph->saddr))); <br>
<br>
而/include/net/sock.h中: <br>
#if 1 <br>
#define NETDEBUG(x) do { } while (0) <br>
#else <br>
#define NETDEBUG(x) do { x; } while (0) <br>
#endif <br>
#endif <br>
即只有在调试是才打开此功能,正常时不作任何事。 <br>
而后来的版本中加入了net_ratelimit()函数,限制成最多5秒钟发出一次内核警告: <br>
<br>
if (net_ratelimit()) <br>
printk(KERN_INFO <br>
"Oversized IP packet from %d.%d.%d.%d.\n", <br>
NIPQUAD(qp->iph->saddr)); <br>
这个问题不光在分片组装时用到,所有的网络部分的代码在打印调试信息时都要考虑大 <br>
量日 <br>
志造成拒绝服务的问题。目前的比较好且通用解决办法便是通过net_ratelimit()函数 <br>
。 <br>
(4)bugtraq id 543 Linux IPChains Fragment Overlap Vulnerability <br>
ipchains在处理分片包,只处理第一个(offset==0&&MF=1),因为只有这个包有TCP,UD <br>
P的 <br>
头信息,其他后续的分片不作防火墙规则匹配,直接通过。假如防火墙之后的系统的IP <br>
分 <br>
片组装类似如下做法:若有重叠,后来的包覆盖前面来的包。这样的话攻击者可以首先 <br>
造 <br>
一个可以通过防火墙规则的合法分片(如一个可访问的端口 ),再造一个与之重叠的分片 <br>
, <br>
改掉前一个片中的信息(如一个不可访问的端口),这样最终的结果便是突破了防火墙 <br>
的 <br>
的 <br>
检测。 <br>
但此种方法只是理论上的,要依靠防火墙后面的主机的分片组装算法的具体实现。反正 <br>
如 <br>
果后面也是一台linux的话便无效,因为在处理重叠的时候linux不允许改变位置在自己 <br>
之 <br>
前的片的内容(详见上面的讨论)。 <br>
此方法在2.2.11以后的内核版本中更难实施,在ipchains处理分片时会检查分片的长度 <br>
, <br>
如果过小则返回FW_BLOCK,即丢弃。 <br>
(5)其他 <br>
碎片攻击不光会攻击操作系统,由于许多网络工具,如防火墙,入侵检测系统(IDS)也 <br>
在 <br>
内部作了分片组装,如果处理不当,也同样会遭受攻击,如著名的checkpoint的防火墙 <br>
<br>
FW-1某些版本(最新的已经改正了)便同样会受到碎片DOS攻击(可详见nsfocus第12期 <br>
月 <br>
刊《了解Check Point FW-1状态表》). <br>
碎片也可以用来逃避IDS检测,许多网络入侵检测系统的机理是单IP包检测,没有处理分 <br>
片 <br>
,即使是象ISS这样的公司也是在最新的5.0版本中才实现了组装功能,更不用说snort了 <br>
, <br>
其IP组装插件经常造成core dump,因此大多数人都将此功能关闭了。 <br>
参考资料: <br>
[1.] linux2.0.33,2.0.34,2.2.16,2.4.0-test3的源代码。 <br>
[2.] securityfocus的漏洞资料。 <br>
[3.] bugtraq上的一些邮件,数目众多,恕不一一列举。 <br>
-- <br>
</small><hr>
<p align="center">[<a href="index.htm">回到开始</a>][<a href="319.htm">上一层</a>][<a href="448.htm">下一篇</a>]
<p align="center"><a href="http://cterm.163.net">欢迎访问Cterm主页</a></p>
</table>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -