yfydz在他的《
Linux内核中FTP跟踪中的序列号处理漏洞》一文中提到Linux内核在处理ftp跟踪时的一些问题,并且针对他所提的问题给出了解决方法。起先,这篇文章并没有引起我多大的兴趣,直到他说出这还是一个安全漏洞[],所以就饶有兴趣地自己详细分析了起来。
经过详细分析后发现,yfydz提到的序列号乱序攻击确实是较难实现的,不过如果换成在一个tcp包中“隐藏”多条ftp命令的方式,轻而易举地就能使ftp的数据连接逃脱netfilter的监控,从而使攻击更加容易达成。
$sock->send("user ftp\r\npass x\r\nport"
. " 127,0,0,1,227," . int(rand(254)) . "\r\n");
|
关于序列号的更新规则,我也和yfydz持不同意见。首先,需要明确seq_aft_nl数组保留的是期待的新行的数据包序列号,这是为了弥补基于包的协议识别在处理基于流的协议时的缺陷而采用的一种暂行方法;再次,保存多于一个的序列号是考虑到数据包的延迟和重发等,防止关键包不被mangle。对于不在期待之列的数据包,当前内核采用的策略是放行,并采用fifo的规则更新se_aft_nl数组,
ends_in_nl = (fb_ptr[datalen - 1] == '\n');
seq = ntohl(th->seq) + datalen;
/* Look up to see if we're just after a \n. */
if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {
/* Now if this ends in \n, update ftp info. */
DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n",
ct_ftp_info->seq_aft_nl[0][dir]
old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
ret = NF_ACCEPT;
goto out_update_nl;
}
|
out_update_nl:
/* Now if this ends in \n, update ftp info. Seq may have been
* adjusted by NAT code. */
if (ends_in_nl)
update_nl_seq(seq, ct_ftp_info,dir, *pskb);
|
但是对于不能顺利解析的关键命令/回复则采取丢弃策略。
for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {
found = find_pattern(fb_ptr, (*pskb)->len - dataoff,
search[dir][i].pattern,
search[dir][i].plen,
search[dir][i].skip,
search[dir][i].term,
&matchoff, &matchlen,
array,
search[dir][i].getnum);
if (found) break;
}
if (found == -1) {
/* We don't usually drop packets. After all, this is
connection tracking, not packet filtering.
However, it is necessary for accurate tracking in
this case. */
if (net_ratelimit())
printk("conntrack_ftp: partial %s %u+%u\n",
search[dir][i].pattern,
ntohl(th->seq), datalen);
ret = NF_DROP;
goto out;
|
代码的注释中比较明确的表达了不应该丢包的原则,因为毕竟不是过滤器(filter),但是对于为什么这时就需要精确的跟踪数据包了,以至于置非过滤器不丢包的原则于不顾的解释却不尽详细?个人猜测可能是鉴于phrack那篇文章的影响力。那么我们不禁要问,这时丢包是否就真的合适呢?如果真的是客户端/服务器端的命令/响应被截断了,那会不会造成网络的异常呢?这就要看tcp协议的具体实现了,对于超时的数据包,发送端如何处理:
- 重新原封不动地发送原数据包。如果sack选项被启用,并且后续的数据包已被确认,那么此时就会采用如此策略。
- 从未ack的数据开始,重新构造数据包并发出。这时就有可能将后续的数据封装在一个数据包内。在这里,情况就是新的数据包可能顺利经过校验。
从我实际的测试结果来看,Linux-2.6.17的tcp协议行为符合2。所以丢包的危害不大,或者说是并无太大危害。如果结合它对非期待数据包的处理来分析,在sack选项开启的情况下,可能会发生一些意外。
好象扯得远了点儿,发现自己的思维习惯是深度优先。
我建议还可以把关把得再严些,
对于非期待的包采取直接丢弃的策略,不光是因为sack,还是针对如下的数据发送情况:
$sock->send("user ftp\r\npass x\r\npor");
$sock->send("t 127,0,0,1,227," . int(rand(254)) . "\r\n");
|
是不是照样漏网?
既然丢包不成问题,我们就可以走得再远点儿,
直接限制每个数据包必须包含整数个命令/回应,否则丢弃之。其实,
很多类telnet协议都可以做如此假设,并采用期待队列加丢包的处理方式。
另外,还有两个小的问题:
- 不能识别只以‘\n’结尾的非标准命令/回应。有的时候标准只是那么一回事,事实又是另外一回事,除了应该支持这个,还可以考虑支持更少众的‘\r’模式。
- 不支持命令和参数之间的多空格(' '),这个和1不一样,它可是在的5.3节有明确规定的。
针对以上问题,我向netfilter-devel提交了两个补丁[
1][
2],不过都石沉大海了。哎,浪费我一番苦心。如果真的有人利用上面提到的问题成功地做出了什么非法的事情,于我无干哈!
突然想到,初始的期待序列号如果通过三次握手的包来获得岂不更好?有心且有时间的朋友可以接着搞下去!因为邮件中的补丁有些畸形了,所以把补丁附在本文的最后吧。
|
文件: | ip_conntrack_ftp.tar.gz |
大小: | 2KB |
下载: | 下载 |
|
阅读(2693) | 评论(2) | 转发(0) |