论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > C语言程序设计教程
Tag:新手,函数,指针,数据类型,对象,Turbo,入门,运算符,数组,结构,二级,,tc,游戏,试题,问答,编译,视频教程

对BIND几个缺陷的分析

文章类别:C语言程序设计 | 发表日期:2008-9-24 14:36:58

综述
现在随着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 uf. */
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]= |
---------------<---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栈的信息,为下一步的单字节缓冲区攻击作好预备。

来源:Linuxbyte
视频教程列表
文章教程搜索
 
C语言程序设计推荐教程
C语言程序设计热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058