Linux (x86) Exploit 開發系列教程之三(Off-By-One 漏洞 (基於棧))
(1)原理:
將源字符串復制到目標緩沖區可能會導致off by one。當源字符串長度等於目標緩沖區長度時,單個NULL字節將被復制到目標緩沖區上方。這裏由於目標緩沖區位於堆棧中,所以單個NULL字節可以覆蓋存儲在堆棧中的調用者的EBP的最低有效位(LSB),這可能導致任意的代碼執行。
(2)漏洞代碼
#include <stdio.h> #include <string.h> void foo(char* arg); void bar(char* arg); void foo(char* arg) { bar(arg); /* [1] */ } void bar(char* arg) { char buf[256]; strcpy(buf, arg); /* [2] */ } int main(int argc, char *argv[]) { if(strlen(argv[1])>256) { /* [3] */ printf("Attempted Buffer Overflow\n"); fflush(stdout); return -1; } foo(argv[1]); /* [4] */ return 0; }
編譯文件
(2)漏洞代碼的第[2]行是可能發生off by one溢出的地方。目標緩沖區長度為256,因此長度為256字節的源字符串可能導致任意代碼執行。如果調用者的EBP位於目標緩沖區之上,則在strcpy之後,單個NULL字節將覆蓋調用者EBP的LSB。反匯編漏洞代碼並繪制它的堆棧布局
gdb-peda$ disassemble main Dump of assembler code for function main: 0x08048497 <+0>: push ebp 0x08048498 <+1>: mov ebp,esp 0x0804849a <+3>: push edi 0x0804849b <+4>: sub esp,0x8 0x0804849e <+7>: mov eax,DWORD PTR [ebp+0xc] 0x080484a1 <+10>: add eax,0x4 0x080484a4 <+13>: mov eax,DWORD PTR [eax] 0x080484a6 <+15>: mov DWORD PTR [ebp-0x8],0xffffffff 0x080484ad <+22>: mov edx,eax 0x080484af <+24>: mov eax,0x0 0x080484b4 <+29>: mov ecx,DWORD PTR [ebp-0x8] 0x080484b7 <+32>: mov edi,edx 0x080484b9 <+34>: repnz scas al,BYTE PTR es:[edi] 0x080484bb <+36>: mov eax,ecx 0x080484bd <+38>: not eax 0x080484bf <+40>: sub eax,0x1 0x080484c2 <+43>: cmp eax,0x100 0x080484c7 <+48>: jbe 0x80484e9 <main+82> 0x080484c9 <+50>: mov DWORD PTR [esp],0x80485e0 0x080484d0 <+57>: call 0x8048380 <[email protected]> 0x080484d5 <+62>: mov eax,ds:0x804a020 0x080484da <+67>: mov DWORD PTR [esp],eax 0x080484dd <+70>: call 0x8048360 <[email protected]> 0x080484e2 <+75>: mov eax,0xffffffff 0x080484e7 <+80>: jmp 0x80484fe <main+103> 0x080484e9 <+82>: mov eax,DWORD PTR [ebp+0xc] 0x080484ec <+85>: add eax,0x4 0x080484ef <+88>: mov eax,DWORD PTR [eax] 0x080484f1 <+90>: mov DWORD PTR [esp],eax 0x080484f4 <+93>: call 0x8048464 <foo> 0x080484f9 <+98>: mov eax,0x0 0x080484fe <+103>: add esp,0x8 0x08048501 <+106>: pop edi 0x08048502 <+107>: pop ebp 0x08048503 <+108>: ret End of assembler dump.gdb-peda$ disassemble foo
Dump of assembler code for function foo:
0x08048464 <+0>: push ebp
0x08048465 <+1>: mov ebp,esp
0x08048467 <+3>: sub esp,0x4
0x0804846a <+6>: mov eax,DWORD PTR [ebp+0x8]
0x0804846d <+9>: mov DWORD PTR [esp],eax
0x08048470 <+12>: call 0x8048477 <bar>0x08048475 <+17>: leave
0x08048476 <+18>: ret
End of assembler dump.
gdb-peda$ disassemble bar
Dump of assembler code for function bar:
0x08048477 <+0>: push ebp
0x08048478 <+1>: mov ebp,esp
0x0804847a <+3>: sub esp,0x108
0x08048480 <+9>: mov eax,DWORD PTR [ebp+0x8]
0x08048483 <+12>: mov DWORD PTR [esp+0x4],eax
0x08048487 <+16>: lea eax,[ebp-0x100]
0x0804848d <+22>: mov DWORD PTR [esp],eax
0x08048490 <+25>: call 0x8048370 <[email protected]>
0x08048495 <+30>: leave
0x08048496 <+31>: ret
End of assembler dump.
(3)256字節的用戶輸入,用空字節可以覆蓋foo的EBP的LSB。所以當foo的存儲在目標緩沖區“buf”之上的EBP被一個NULL字節所覆蓋時,ebp從0xbffff2d8變為0xbffff200。從堆棧布局我們可以看到堆棧位置0xbffff200是目標緩沖區“buf”的一部分,由於用戶輸入被復制到該目標緩沖區,攻擊者可以控制這個堆棧位置(0xbffff200),因此他控制指令指針(eip )使用他可以實現任意代碼執行。
測試:可覆蓋返回地址。
(4)嘗試找出ret_addr的值。
將斷點下在Breakpoint 2, 0x08048495 in bar ()處。運行查看內存情況
可以看到EBP 的值從0xbffff158被覆蓋成0xbffff100。EIP指向的是0xbffff104地址。EIP所指地址與buf之間需要填充172個A。
嘗試找出ret_addr的地址。
(5)攻擊代碼
不知道為何,嘗試運行攻擊代碼失敗了。但是直接在調試界面輸入,可以獲得普通用戶的shell權限。
Linux (x86) Exploit 開發系列教程之三(Off-By-One 漏洞 (基於棧))