首页 -> 安全研究
安全研究
绿盟月刊
绿盟安全月刊->第23期->技术专题
作者:warning3 < mailto: warning3@nsfocus.com >
主页:http://www.nsfocus.com
日期:2001-07-17
前言:
ypbind用来为NIS(网络信息服务)客户端与服务器端通信提供接口。它在NIS客户端和服
务器上都被运行。有人发现它存在一个缓冲区溢出漏洞,Sun还为此出了一个安全公告
#00203。
由于ypbind是以root身份运行,本地或者远程攻击者就可以利用这个漏洞获取root权限。
因此这个漏洞还是比较危险的。所幸的是,如果你没有启动NIS服务,ypbind缺省不会
启动。
起因:
让我们来看这个漏洞是怎么发生的。
问题出在函数 <ypbindproc_setdom_3+784>()中,它执行了类似这样的一条语句(可能不准
确)来进行串拷贝:
sprintf(buf, "%s/%s/cache_binding", "/var/yp/binding", user_string);
上面buf是一个300字节的缓冲区,user_string是一个客户端传送过来的字符串。
ypbind在传送这个字符串的时候没有检查串的长度,这使得远程溢出成为可能。
函数调用顺序为:ypbindprog_3()-->ypbindproc_domain_3()--><ypbindproc_setdom_3+784>()
相关的定义如下:
#define YPBINDPROG ((u_long)100007)
#define YPBINDVERS ((u_long)3)
#define YPBINDPROC_DOMAIN ((u_long)1)
如果远程用户调用YPBINDPROC_DOMAIN函数时,就会触发ypbindprog_3()以及后续的函数
执行。
重要的参数结构如下:
struct ypbind_domain {
char *ypbind_domainname;
long ypbind_vers;
};
其中ypbind_domainname是一个字符串指针,如果它指向一个较长的字符串(超过500字
节),就可能引发溢出。
攻击:
理论上说,我们可以传递一个很长的字符串以增大成功几率,但是由于buf缓冲区所在
的位置比较靠近堆栈的底端(高地址处),所以,如果字符串较长,比如超过2000字节,
往往导致覆盖到了栈底,发生段访问错误.
本来这是一个非常简单的堆栈溢出,但是我在测试TCP连接时,发现一个非常奇怪的现象。
例如,溢出是发生在<ypbindproc_setdom_3+784>()中的,它会覆盖它的调用函数
(ypbindproc_domain_3())的保存栈幀,按说在ypbindproc_domain_3()返回的时候就应该
跳到我们的shellcode去执行了。问题是如果我用gdb跟踪,将断点设在了
<ypbindproc_setdom_3+784>() 函数处,然后程序会在这里停一下,然后我在gdb中用"c"
命令继续执行,这就会发生正常的堆栈溢出过程,shellcode可以被执行。但是,如果我
不在<ypbindproc_setdom_3+784>()那里设置断点,溢出仍然发生了,但是奇怪的是,唯独
ypbindproc_domain_3()的保存栈幀(一块64字节长的内存)又恢复成正常的值了,它前后
的内存都被我的数据覆盖了,这导致程序正常返回到了ypbindprog_3()!
看起来好像存在一种保护机制,例如信号句柄,使得被覆盖的保存栈幀又恢复了。
比较奇怪。
幸运地是,在ypbindprog_3()中调用ypbindproc_domain_3()后会调用另一个函数
svc_sendreply(),它会调用一个函数指针,而这个指针可以为我们所控制:
(gdb)...
Program received signal SIGSEGV, Segmentation fault.
0xef73f714 in svc_sendreply ()
(gdb) bt
#0 0xef73f714 in svc_sendreply ()
#1 0x12b64 in ypbindprog_3 ()
#2 0xef73fcb0 in _svc_prog_dispatch ()
#3 0xef73fab0 in svc_getreq_common ()
#4 0xef73f9e8 in svc_getreq_poll ()
#5 0xef7435fc in _svc_run ()
#6 0xef743350 in svc_run ()
#7 0x12a24 in main ()
(gdb) x/20i $pc
[...]
0xef73f738 <svc_sendreply+56>: ld [ %i0 + 8 ], %o0
0xef73f73c <svc_sendreply+60>: ld [ %o0 + 0xc ], %o2
0xef73f740 <svc_sendreply+64>: call %o2
上面的$i0是我们可以覆盖的,所以我们要设法使%i0的值指向这样的一个模板:
+--+--+---+---+
|xx|xx|adr|ret|
+--+--+---+---+
^
|_adr
假设这个模板的地址是adr,那么
adr + 8(字节) = adr (%o0)
adr + 12(字节) = ret (shellcode所在地址,%o2)
ret地址可以通过使用NOP指令来增大命中几率。
现在问题是adr是一个未知的地址,如果保证这个模板的起始地址刚好在%i0所指的地址
上呢,如果是在本地的话,可以通过构造环境变量的方法来将我们的模板放在一个准确
的地址上。
但是远程就不行了,只能猜测这个adr地址。如果要增大命中几率,就要有很多16字节的
模板,如下图所示:
+--+--+---+---+--+--+---+---+--+--+---+---+--+--+---+---+
|xx|xx|adr|ret|xx|xx|adr|ret|xx|xx|adr|ret|xx|xx|adr|ret|
+--+--+---+---+--+--+---+---+--+--+---+---+--+--+---+---+
这要求我们猜测的地址要刚好在16字节的边界上,这个几率也不算太大,我发现标了"xx"
的字节我们并没有被用到,而且有用的两个字节又刚好是挨在一起的,那么我们就可以
将"xx"也利用一下,变成这样的模板:
ret
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---------------+
|adr|ret|adr|ret|adr|ret|adr|ret|adr|ret|adr|ret|adr|ret|...|nop...shellcode|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---------------+
^
|_adr
这样我们只要保证我们猜测的地址刚好落在任意一个"adr"的地址上就行了,这样的命中
几率就比较高了。
有意思的是,如果使用UDP进行连接,那么我们不会碰到上面svc_sendreply()的问题,
这里好像也有一个保护机制,反而是ypbindprog_3()的栈幀可以被覆盖,因此,从
ypbindprog_3 返回时,就可以跳到shellcode中去执行。
原理已经说完了,有兴趣的朋友不妨自己试着写一写测试程序。:)
解决:
Sun已经提供了补丁。根据我的分析,补丁很简单,只是在调用ypbindproc_setdom前检
查了一下字符串长度,如果长度小于或等于0x100(256)字节,就继续操作,否则就返回。
<...>
0x137d4 <ypbindproc_domain_3+28>: call 0x265cc <strlen>
0x137d8 <ypbindproc_domain_3+32>: ld [ %i0 ], %o0
0x137dc <ypbindproc_domain_3+36>: cmp %o0, 0x100 <-- 比较strlen的结果是否大于0x100
0x137e0 <ypbindproc_domain_3+40>: bleu 0x137f8 <ypbindproc_domain_3+64>
0x137e4 <ypbindproc_domain_3+44>: mov 2, %o0 <--- 大于0x100,返回
0x137e8 <ypbindproc_domain_3+48>: st %o0, [ %i1 ]
0x137ec <ypbindproc_domain_3+52>: st %o0, [ %i1 + 4 ]
0x137f0 <ypbindproc_domain_3+56>: ret
0x137f4 <ypbindproc_domain_3+60>: restore %g0, %i1, %o0
0x137f8 <ypbindproc_domain_3+64>: call 0x144e4 <ypbindproc_setdom_3+832>
^
|-- 否则,继续进行。
具体解决方法可以看参考资料中的内容,这里就不罗嗦了。
<参考资料>:
[1.] Sun Microsystems, Inc. Security Bulletin #00203
http://sunsolve.sun.com/pub-cgi/retrieve.pl?doctype=coll&doc=secbull/203&type=0&nav=sec.sba
[2.] Solaris ypbind 远程缓冲区溢出漏洞
http://security.nsfocus.com/showQuery.asp?bugID=1602
版权所有,未经许可,不得转载