摘要:BIND8安全漏洞分析
对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*/