首页 -> 安全研究
安全研究
绿盟月刊
绿盟安全月刊->第44期->技术专题
作者:scz <scz@nsfocus.com>
主页:http://www.nsfocus.net
日期:2003-09-03
/*
* For x86/EWindows XP SP1 & VC 7
* cl vulnerable_0.c /nologo /Os /G6 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment( linker, "/INCREMENTAL:NO" )
#pragma comment( linker, "/subsystem:console" )
int __cdecl main ( int argc, char * argv[] )
{
unsigned char buf[8];
if ( 2 != argc )
{
fprintf( stderr, "Usage: %s <any string>\n", argv[0] );
return( EXIT_FAILURE );
}
strcpy( buf, argv[1] );
printf( "%s\n", buf );
return( EXIT_SUCCESS );
} /* end of main */
3) 利用监视点定位导致溢出的代码点
前面讨论了如何尽早中断在vulnerable_0.exe进程空间中,这次介绍如何利用监视点
定位导致溢出的代码点。
在<<SMB系列(11)--TRANSACT2_OPEN处理过程存在远程缓冲区溢出漏洞>>中介绍过GDB
调试技巧,利用watch功能定位导致溢出的代码点。windbg的ba不能在用户级调试中
使用,很奇怪为什么是这样。SoftICE的bpm断点等价于gdb的watch命令。
打开faults on,让EIP等于0x41414141时SoftICE弹出:
Break due to BP 00: BPX #001B:00401289 (ET=4.41 seconds)
:bl
00) BPX #001B:00401289
:faults on
:g
Break due to UnhandledException NTSTATUS=STATUS_ACCESS_VIOLATION
在当前栈顶(ESP)以低位置搜索特征字节,试图定位ret指令用过的那个栈帧:
:s -a esp&ffff0000 L esp&0000ffff "AAAAAAAAAAAAAAAA"
Pattern found at 0023:0012F948 (0000F948)
Pattern found at 0023:0012FA60 (0000FA60)
0000000002 occurances found
:db 0012F948 L 20
0023:0012F948 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0023:0012F958 0D 0A 00 00 A8 03 00 00-00 00 00 00 01 00 00 00 ................
:db 0012FA60 L 20
0023:0012FA60 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0023:0012FA70 0D 0A 02 03 02 03 02 03-02 03 10 02 10 02 10 02 ................
:faults off
:g
注意到这两处结尾都有"\r\n",从源代码可知这两处必不是buf[]所在,像printf()
所用缓冲区。一般没有源代码可供参考,只能试着先在这两外下写监视点,,找出向
此写入"AA..AA"的代码点。重新执行vulnerable_0.exe,中断在总入口点:
:bd 0
:bpmd 0012F948 w if *0012F948==41414141
:bpmd 0012FA60 w if *0012FA60==41414141
:bl
00) * BPX #001B:00401289
01) BPMD #0023:0012F948 W DR3 IF (*(0x12F948)==0x41414141)
02) BPMD #0023:0012FA60 W DR2 IF (*(0x12FA60)==0x41414141)
再次提醒,bpm断点随进程空间消失而消失,因此应该尽早中断在vulnerable_0.exe
进程空间中,然后设置bpm断点,而不是其它时刻。g继续执行,其中一个写监视点命
中:
:g
Break due to BP 02: BPMD #0023:0012FA60 W DR2 IF (*(0x12FA60)==0x41414141)
:d 0012FA60 L 20
0023:0012FA60 41 41 41 41 02 03 02 03-02 03 02 03 02 03 02 03 AAAA............
0023:0012FA70 02 03 02 03 02 03 02 03-02 03 10 02 10 02 10 02 ................
:d 0012F948 L 20
0023:0012F948 00 01 00 00 84 FD 12 00-00 01 00 00 84 FC 12 00 ................
0023:0012F958 00 01 00 00 A8 03 00 00-00 00 00 00 01 00 00 00 ................
:
有代码在向0x0012FA60写入"AA..AA",在这段代码附近F10单步跟几步,就发现源串
跟如下代码相关:
001B:00403C11 8B55F8 MOV EDX,[EBP-08]
001B:00403C14 FF45F8 INC DWORD PTR [EBP-08]
001B:00403C17 8A12 MOV DL,[EDX]
:dd ebp-8 L 4
0023:0012FE74 003718BC C0BDC4AA 0012FEE4 0040217A ..7.........z!@.
:db 003718BC-20 L 40
0023:0037189C 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0023:003718AC 00 00 00 00 01 02 01 01-00 01 08 00 41 41 41 41 ............AAAA
0023:003718BC 41 41 41 41 41 41 41 41-41 41 41 41 0A 00 00 00 AAAAAAAAAAAA....
0023:003718CC 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
这回发现0x003718B8处也有16个'A',后面还有一个'\n'。再次搜索内存:
:s -a 0 L 7fffffff "AAAAAAAAAAAAAAAA"
Pattern found at 0023:0012FEDC (0012FEDC)
Pattern found at 0023:00142371 (00142371)
Pattern found at 0023:00142FD9 (00142FD9)
Pattern found at 0023:00370B3D (00370B3D)
Pattern found at 0023:003718B8 (003718B8)
0000000005 occurances found
不必搜索0x80000000开始的空间,那是内核空间。
:stack
FrameEBP RetEIP Symbol
0012FE7C 0040217A vulnerable_0!.text+2C11
0012FEE4 41414141 vulnerable_0!.text+117A
查看调用栈回溯时意外发现某个返回地址已经被覆盖成0x41414141,与内存搜索结果
对照,显然我们应该在0x0012FEDC下写监视点:
:be 0
:bc 2
:bpe 1
:BPMD #0023:0012FEDC W DR3 IF (*(0x0012FEDC)==0x41414141)
:bl
00) BPX #001B:00401289
01) BPMD #0023:0012FEDC W DR3 IF (*(0x12FEDC)==0x41414141)
:g
不过这次bpm断点已经无效,0x0012FEDC已经被写过了,而且进程空间消失后bpm断点
随之消失。我用bpe命令先设置一次,留在命令缓冲中。下次因bpx中断后,只需用上
键头找出bpm命令重新设置即可,不必输入很长的一串。重新执行vulnerable_0.exe:
Break due to BP 00: BPX #001B:00401289 (ET=14.80 seconds)
:bl
00) BPX #001B:00401289
:BPMD #0023:0012FEDC W DR3 IF (*(0x0012FEDC)==0x41414141)
:bl
00) BPX #001B:00401289
01) BPMD #0023:0012FEDC W DR3 IF (*(0x12FEDC)==0x41414141)
:db 0012FEDC L 20
0023:0012FEDC 00 00 00 00 31 00 00 00-44 9F 59 80 02 00 00 00 ....1...D.Y.....
0023:0012FEEC FF FF FF FF 00 08 00 00-10 8C 18 FD B1 77 4F 80 .............wO.
g继续执行,写监视点命中:
:g
Break due to BP 01: BPMD #0023:0012FEDC W DR3 IF (*(0x12FEDC)==0x41414141)
:db 0012FEDC L 20
0023:0012FEDC 41 41 41 41 41 41 41 00-C0 FF 12 00 F9 13 40 00 AAAAAAA.......@.
0023:0012FEEC 02 00 00 00 20 0B 37 00-58 0B 37 00 94 00 00 00 .... .7.X.7.....
:stack
FrameEBP RetEIP Symbol
0012FEE4 004013F9 vulnerable_0!.text+011B
0012FFC0 77E814C7 vulnerable_0!.text+03F9
0012FFF0 00000000 kernel32!_BaseProcessStart+0023
在栈帧0x0012FEE4(EBP)所对应的函数中,有代码导致自己的栈帧被覆盖,这是最典
型的栈溢出。而我们已经找到导致溢出的代码点:
EAX=7EFEFEFE EBX=7FFDF000 ECX=00370B44 EDX=41414141 ESI=00000A28
EDI=0012FEDF EBP=0012FEE4 ESP=0012FECC EIP=0040111B o d I s Z a P c
CS=001B DS=0023 SS=0023 ES=0023 FS=0038 GS=0000
─────────────────────────────────PROT32─
001B:0040111B 83C704 ADD EDI,04
001B:0040111E BAFFFEFE7E MOV EDX,7EFEFEFF
001B:00401123 8B01 MOV EAX,[ECX]
001B:00401125 03D0 ADD EDX,EAX
... ...
001B:00401150 EBC7 JMP 00401119
001B:00401152 8917 MOV [EDI],EDX
(PASSIVE)-KTEB(8106B240)-TID(0164)──vulnerable_0!.text+011B──────
总结一下全过程:
a. 执行问题程序,EIP为0x41414141时SoftICE弹出,在内存中搜索特征字节,记录
地址,准备下次设置写监视点用。
用stack命令查看此刻调用栈回溯,获取更多有用信息。
立即根据PE头计算出程序总入口点,在程序总入口点设置bpx断点。
b. 重新执行问题程序,SoftICE因bpx断点而弹出。根据步骤a记录下来的地址设置写
监视点。
g命令继续执行问题程序。
c. SoftICE因写监视点命中而弹出。再次在内存中搜索特征字节,配合stack命令确
定被覆盖的栈帧以及相应的缓冲区起始地址。
清空那些无用的写监视点,保持bpx断点。
g命令结束本次执行。
d. 重新执行问题程序,SoftICE因bpx断点而弹出。根据步骤c记录下来的地址设置写
监视点。
g命令继续执行问题程序。
e. SoftICE因写监视点命中而弹出。此刻EIP附近的代码就是我们要找的代码。
这是一个逐步逼近的调试过程。vulnerable_0.c相当简单,因此逼近过程快。如果碰
上没有源代码可供参考的复杂应用程序,就远不是这样轻松了。不过整体抽象思路依
旧。
版权所有,未经许可,不得转载