分享
 
 
 

BIND8安全漏洞分析

王朝厨房·作者佚名  2007-01-05
窄屏简体版  字體: |||超大  

对BIND几个缺陷的分析

综述

现在随着Internet的日益普及,而Internet非常依赖于域名服务(DNS)。在RFC845中对域名服务作了如下定义:一个迭代的分布式数据库系统,它为Internet操作提供了基本的信息,例如:域名<-->IP地址的相互转换,邮件处理信息。BIND(Berkeley Inetnet Name Domain,伯克利Internet域名是一种使用最广的域名系统。它有安全缺陷对Internet无疑于是一场灾难。

2001年月29日,Network Associates of California发表了一个报告,指出了BIND最近出现的四个安全缺陷。其中有两个是关于缓冲区溢出,可以使攻击者关闭DNS或者获得root权限,一个叫做"TSIG bug",影响BIND8,另一个是叫“complain bug"的缓冲区溢出缺陷,影响BIND4。其余两个一个叫做"infoleak",影响BIND4和BIND8,另一个叫做"complain bug"格式化字符串缺陷,只影响BIND4。本文将着重讲述infoleak和TSIG bug。其中,infoleak bug不能直接用来进行攻击,但是它可以泄露栈的信息,甚至使攻击者得到BIND运行时的内存布局,为使用TSIG进行攻击创造了便利。这恐怕也是最近两个蠕虫:lion和adore都使用BIND的漏洞进行传播的主要原因之一。

细节

1.infoleak

infoleak bug是由Claudio Musmarra发现的,最早在CERT安全建议CA-2001-02对这个BUG进行了报道。它使攻击者能够直接得到named程序栈祯的信息,从而直接计算出进行单字节缓冲区溢出所需要的信息,大大增加了攻击的成功率。

程序执行时,在栈中保存了程序运行的内部变量和函数的局部变量,以及函数调用的返回地址等信息。infoleak bug可以使攻击者直接读出在栈中的这些信息,甚至程序运行时的内存布局。通过向运行有这个缺陷的BIND版本的DNS服务器发送一个特制的查询包,就可以达成上述目的。

所谓特制的查询包就是向一个合法的很大的IQUERY(反向查询)查询包。向一个运行BIND的DNS服务器发出一个合法的IQUERY请求,DNS服务器把应答记录放在这个查询包之后返回。应答包括一个域名、类型(type)、类别(class)和ttl(包的生存时间)。在构造这个反向查询包时,只要使域名对named程序的dn_skipname()函数是合法的就可以了。把这个反向查询包的数据长度设置为一个和很大的数值,就会是应答记录超出缓冲区的边界。named程序的req_iquery()函数会发现这个反向查询包非法,并且返回一个指示错误的字符串。不幸的是,它在检查是否有错误时,不管反向查询包的数据区有多长,首先把指向包尾的指针cp向后推,这样很可能使cp指针超出了缓冲区的边界。从req_iquery()函数返回后,ns_req()函数就会发出大小是cp-msg(指向缓冲区的头)个字节含有错误信息的应答包。如果这个应答包已经超出了缓冲区的大小,就会包含named程序当前栈祯的信息如ebp等等,然后攻击者就可以使用TSIG安全缺陷进行单字节缓冲区溢出攻击了。

因为相对于TSIG安全缺陷关于infoleak的分析资料较少,所以我将以bind-8.2为例对infoleak进行分析。BIND在查询包小于512个字节时,使用UDP/53端口接受数据(更详细的信息请参考TSIG部分),具体接受数据的函数就是datagram_read(),以下是datagram_read()函数的相关源代码

static void

datagram_read(evContext lev, void *uap, int fd, int evmask) {

interface *ifp = uap;

struct sockaddr_in from;

int from_len = sizeof from;

int n, nudp;

union {

HEADER h; /* Force alignment of 'buf'. */

u_char buf[PACKETSZ+1];

} u;<--这就是named函数存放小于512个字节的查询包的缓冲区,后面对于查询的处理操作都是针对于这个缓冲区的,也就是说,datagram_read是使用传址方式把查询包传递给以后的处理函数*/

..................

dispatch_message(u.buf, n, PACKETSZ, NULL, from, fd, ifp);

if (++nudp < nudptrans)

goto more;

}

这时,栈的布局如下:

------------------

|参数 |

| |

| |

------------------

| |

| 返回地址 |

------------------

|ebp |

------------------

|各个局部变量 |

-----------------

|u.buff[513] |

-----------------

|u.buff[512] |

-----------------

| ..... |

-----------------

|u.buff[0] |<----缓冲区

-----------------

接着,dispatch_message函数调用ns_req()函数:

void

ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp,

struct sockaddr_in from, int dfd)

{

HEADER *hp = (HEADER *) msg;

u_char *cp, *eom;/*<---cp指向请求包的数据区*/

/*cp = msg + HFIXEDSZ*/

/*eom指向请求包的尾*/

/*eom = msg + msglen*/

................

if (error == NOERROR) {

switch (hp->opcode) {

case ns_o_query:

action = req_query(hp, &cp, eom, qsp,

&buflen, &msglen,

msg, dfd, from, in_tsig);

break;

case ns_o_iquery:

action = req_iquery(hp, &cp, eom, &buflen, msg, from);

break;

/*反向请求包由req_iquery函数处理*/

此时,栈如图所示:

------------------

|参数 |

| |

| |

------------------

| |

| 返回地址 |

------------------

|ebp |

------------------

|各个局部变量 |

-----------------<----eom

|u.buff[513] |

-----------------

|u.buff[512] |

-----------------

| ..... |

-----------------

|u.buff[12] |

-----------------<----cp

| ..... |

-----------------

|u.buff[0] |

-----------------<---msg

下面是req_iquery()函数:

static enum req_action

req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp,

u_char *msg, struct sockaddr_in from)

{

int dlen, alen, n, type, class, count;

char dnbuf[MAXDNAME], anbuf[PACKETSZ], *data, *fname;

nameserIncr(from.sin_addr, nssRcvdIQ);

if (ntohs(hp->ancount) != 1

' 'ntohs(hp->qdcount) != 0

' 'ntohs(hp->nscount) != 0

' 'ntohs(hp->arcount) != 0) {

ns_debug(ns_log_default, 1,

"FORMERR IQuery header counts wrong");

hp->qdcount = htons(0);

hp->ancount = htons(0);

hp->nscount = htons(0);

hp->arcount = htons(0);

hp->rcode = FORMERR;

return (Finish);

}/*构造包时,使其能够通过这些检查*/

/*

* Skip domain name, get class, and type.

*/

if ((n = dn_skipname(*cpp, eom)) < 0) {

ns_debug(ns_log_default, 1,

"FORMERR IQuery packet name problem");

hp->rcode = FORMERR;

return (Finish);

}

/*dn_skipname函数接着调用ns_name_skip函数*/

*cpp += n;

/*使攻击程序构造的包数据区很大*/

假设这时,栈如图所示:

------------------

|参数 |

| |

| |

------------------

| |

| 返回地址 |

------------------

|ebp |

------------------

|各个局部变量 |

-----------------<----eom

|u.buff[512] |

-----------------

|u.buff[511] |

-----------------

| ..... |

-----------------

|u.buff[446] |

-----------------<----cp

| ... |

-----------------

|u.buff[12] |

-----------------

| ..... |

-----------------

|u.buff[0] |

-----------------<---msg

/*但是符合*cpp+3*INT16SZ+INT32SZ<=eom*/

if (*cpp + 3 * INT16SZ + INT32SZ > eom) {

ns_debug(ns_log_default, 1,

"FORMERR IQuery message too short");

hp->rcode = FORMERR;

return (Finish);

}

/*named处理type,class*/

GETSHORT(type, *cpp);

GETSHORT(class, *cpp);

*cpp += INT32SZ; /* ttl */

GETSHORT(dlen, *cpp);

/*cpp已经接近缓冲区的边界了*/

此时,栈如图所示:

------------------

|参数 |

| |

| |

------------------

| |

| 返回地址 |

------------------

|ebp |

------------------

|各个局部变量 |

-----------------<----eom

|u.buff[512] |

-----------------

|u.buff[511] |

-----------------

| .... |

-----------------

|u.buff[458]=255|

-----------------<---cp

|u.buff[457]=0 |

-----------------

|u.buff[456]=255|<--假设dlen为255

-----------------

| ..... |

-----------------

|u.buff[446] |

-----------------

| ... |

-----------------

|u.buff[12] |

-----------------

| ..... |

-----------------

|u.buff[0] |

-----------------<---msg

*cpp += dlen;

/*攻击程序发出的反向查询包的dlen为一个很大的值*/

/*此时,再向后推dlen个字节*/

/*哈,越界了*/

if (*cpp != eom) {

ns_debug(ns_log_default, 1,

"FORMERR IQuery message length off");

hp->rcode = FORMERR;

return (Finish);

}

接下来,就是由ns_req()将cp-msg个字节发送给攻击程序。攻击者就可以得到named栈的信息,为下一步的单字节缓冲区攻击作好准备。

2.TSIG bug

DNS域名服务器使用TISG(tranaction signature)来进行验证通讯。TSIG BUG因此而得名。在最近出现的四个BIND BUG中,TSIG BUG危害是最为严重的。一个TSIG是一个高层的DNS资源记录,在请求或者应答中是分别计算的,用完后丢弃,不能重复使用,也不应该保存在高速缓存中。TSIG是一个复杂的安全机制。它必须在消息的最后。如果在资源记录(RR)中有几个TSIG,或者位置不正确,BIND就会丢弃这个包并且送回一个错误信息。TSIG有几个验证机制,阻止了攻击者从网络上截取含有TSIG的包使用。

TSIG BUG影响的BIND版本有:8.2(any service pack),8.2.1,8.2.2(packs1-7),和所有的8.2.3beta版本。

当BIND接到一个请求,它会根据接受请求使用的传输机制,把请求放在栈或者堆中。如果DNS请求小于512个字节,它就使用UDP/53接受请求的数据,并将其放在栈区中;如果DNS请求大于512个字节,它就使用TCP/53接受请求的数据,并把请求数据放在堆中。

当请求小于或者等于512个字节时,由datagram_read()函数把请求放到栈中的一个513个字节缓冲区中,即u.buff;当收到一个TCP请求时,就由stream_getlen()函数把请求数据读到一个64K的缓冲区中,这个缓冲区叫做sp->s_buff,是在堆中为每个套接字分配的。其中,有一个很有意思的特征,无论是使用TCP传输协议还是UDP传输协议,BIND都是只在缓冲区中对数据进行操作,然后做出相应的响应。

BIND使用两个变量来跟踪缓冲区的使用情况:msglen保存缓冲区中现有数据的字节数;bufflen保存缓冲区没有使用的字节数。

当接到一个DNS消息,msglen被初始化为从网络上接到的数据的长度。对于UDP来说,就是由recvfrom()系统调用返回的数;而TCP消息的msglen是由客户端给出的。buflen被设置为读取消息的缓冲区的大小,UDP是512,TCP是64K。

通常情况下,在处理一个DNS查询时,BIND回在查询的后面加上应答、验证以及其它的记录信息。接着,BIND就会修改这个DNS查询的头来显示上面所做的修改,将其送出。在处理过程中,msglen将会反映构造应答信息的缓冲区使用情况,而buflen将跟踪缓冲区空闲区域的情况。在整个处理过程中,BIND都假设buflen+msglen等于缓冲区的长度。

根据消息头的设置,BIND会区分请求还是应答,分别对其进行处理。如果接到一个请求,它就区分这个请求是查询(query)、反向查询(iquery)、update还是notification。从BIND8.2开始,在处理请求数据之前,BIND首先要检验有没有TSIG,这个功能是由ns_find_tsig()来完成的,同时这个函数还会对TSIG的合法性进行基本的验证。如果有一个正确的TSIG而没有准确的security key,BIND就发出一个错误信号,并跳过正常的处理操作。此时,msglen和buflen也保留为其初始值,而不是被设置为其工作值。

因为有一个正确的TSIG但没有准确的security key,BIND就进行错误处理。在产生错误信息时,BIND会重新起用缓冲区并且在这个有问题请求之后加上一个TSIG。此时,BIND假定msglen+buflen等于缓冲区的大小,当然在通常情况下,这时对的,但是只是在通常情况下-:(,在一些特殊的情况下msglen+buflen几乎可以达到缓冲区大小的两倍。接下来,BIND就调用ns_sign()函数在查询之后加上一个TSIG,可能已经超出了缓冲区的范围。

下面是BIND处理请求的大体过程的示意图:(本来是应该使用HTML格式的,但----是我一直对制作页面缺乏兴趣,所以只要如此了,不过我会抓紧学一学的-:) )

----------------

|收到DNS请求 |

----------------

| |

| |

/ /

-------------- ---------------

|UDP请求由 | |TCP请求由 |

|datagram_read| |stream_getlen |

|放到 | |放到 |

-------------- ---------------

| |

| |

/ /

------------ -----------------

|栈 | |堆heap |

|u.buff[513]| | sp->s_buff |

| | | |

------------ ------------------

跟踪变量:msglen---数据长度

buflen---未使用缓冲区的长度

图2.1

|----------------------------------------------------------------|

| 接到一个DNS请求 |

| msglen设置为从网络上接受的数据的长度 |buflen被初始化为缓冲区的长度|

| UDP:recvfrom() |UDP:513 bytes |

| TCP:由客户端给出 |TCP:64K bytes |

|-----------------------------------------------------------------|

判断请求是:

query

iquery

update

notification

检查是否有TSIG

并检验其合法性

----------------------------------------------------------------------

通常情况下, 异常情况下,

TSIG和security key合法 正确的TSIG,而security key非法

处理请求 发出错误信号:

BIND跳过正常对请求的处理

进行错误处理

在查询信息之后 msglen和buflen保留为其原来的值

加上验证及其它

记录

修改查询包的头 重用缓冲区产生错误信息

msglen等于数据的长度 假设nsglen+buflen等于原来缓冲区的长度

buflen空闲缓冲区的长度

假设原来缓冲区的长度等于

msglen+buflen

发出应答 加上TSIG,溢出

------------------------------------------------------------------

例如,A公司的DNS使用的是BIND8.2.2-Patch5。一名攻击者通过端口扫描知道了相关的信息,接着攻击者向这台域名服务器发出了一个请求,而这个请求包有一个TSIG而security key非法。A公司的DNS收到这个请求进行处理,它发现一个TSIG但是没有合法的security key就发出了错误信号,而此时msglen和buflen都被锁定,不能在处理错误时准确反映缓冲区的情况。datagram_read()或者stream_getlen()函数返回之前,BIND在请求包之后加上了新的TSIG,就超出了缓冲区。

总结

针对这个BUG可以使用单字节缓冲区溢出和堆溢出exploit。由于这两个BUG和系统设置无关,所以应该赶快升级BIND系统。有一些途径可以用来对named进行保护:

1).不以root运行named;

2).使用chroot保护文件系统。

此外还可以使用其它一些方式保护自己的系统。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有