[英]How to find the vulnerability of a program to exploit using buffer overflow attack
[英]No error message when using system() to execute program with buffer overflow vulnerability
考慮以下具有緩沖區溢出漏洞的程序( vul.c
)。
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buf[10];
strcpy(buf, argv[1]);
printf("%s\n", buf);
return 0;
}
上面使用gcc -o vul vul.c
編譯並在arch linux - linux 4.4.16-1-lts x86-64
上執行gcc -o vul vul.c
arch linux - linux 4.4.16-1-lts x86-64
在終端中以./vul $(perl -e 'print "A"x100')
命令:
AAAAAAAAAAA...A
Segmentation fault (core dumped)
然后使用echo $?
檢查程序狀態echo $?
命令給出了139
輸出。
跟隨程序( exp.c
)(用於使上述程序崩潰)
#include <stdlib.h>
int main(void)
{
printf("%d\n", system("./vul $(perl -e 'print \"A\"x100')"));
return 0;
}
在同一系統上用./exp
命令執行時,使用gcc -o exp exp.c
編譯得到的輸出如下:
AAAAAAAAAAAA...A
139
我有兩個問題:
-fstack-protector
標志編譯程序,以在arch linux
啟用*** stack smashing detected ***
錯誤消息,而在Ubuntu
不行。 在Ubuntu
,可能是gcc
默認包含此標志,還是有其他原因? 正如我在評論中指出的那樣, system
返回一個包含程序返回值的int值,該值通常是錯誤代碼(如果成功,則返回0)。
如果要將錯誤打印為美觀的消息,則可以使用strerror
。
根據@rht的評論(請參閱我的下一個編輯)和該評論中引用的問題的答案,成功時返回值為0,錯誤時返回error | 0x80
error | 0x80
。 要獲取原始錯誤代碼,請使用128 - err_code
。
嘗試這個:
#include <stdlib.h>
#include <errno.h>
int main(void)
{
int tmp = system("./vul $(perl -e 'print \"A\"x100)");
if(tmp < 0)
error("Couldn't run system command");
else if(tmp >0)
printf(stderr, "System command returned error: %s", strerror(128 - tmp));
else
; // nothing
return 0;
}
vul.c
會(或不會)顯示錯誤消息這一事實與您的exp.c
程序無關,因為它取決於vul.c
的編譯標志值和任何默認的編譯器標志exp.c
可以做到這一點t控制。
EDIT(2) -回應評論。
返回的錯誤消息可能不是errno
值,而是信號trap
值。
有時很難區分這些,並且我對如何在不使用memcmp
反對答案的情況下分辨出哪一個方面沒有很好的建議。
在這種情況下,您知道vul.c
將永遠不會返回它的errno
值,僅留下信號陷阱錯誤,因此您可以使用strsignal
打印錯誤消息。
正如@rht的評論中指出的那樣,它引用了這個問題 :
將tmp傳遞給
strsignal
生成相同的錯誤消息:“未知信號139”。 原因是沒有帶有該信號編號的信號。 /usr/include/bits/signum.h包含所有信號及其信號編號。 將tmp-128
傳遞給strsignal。
即
#include <stdlib.h>
#include <string>
int main(void)
{
int tmp = system("./vul $(perl -e 'print \"A\"x100)");
if(tmp < 0)
error("Couldn't run system command");
else if(tmp >0)
printf(stderr, "System command returned error: %s", strsignal(tmp - 128));
else
; // nothing
return 0;
}
編輯
該問題已被編輯,因為它的代碼被錯誤地復制了。 我更改了答案以反映這一變化。
從我對@Myst對“將tmp-128
傳遞給strsignal()”函數的回答的評論中,經過一些試驗后,我發現它在程序正常退出但返回非0狀態的情況下不起作用。
以下是我的/usr/include/bits/waitstatus.h
的內容:
/* If WIFEXITED(STATUS), the low-order 8 bits of the status. */
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
/* If WIFSIGNALED(STATUS), the terminating signal. */
#define __WTERMSIG(status) ((status) & 0x7f)
/* Nonzero if STATUS indicates normal termination. */
#define __WIFEXITED(status) (__WTERMSIG(status) == 0)
/* Nonzero if STATUS indicates termination by a signal. */
#define __WIFSIGNALED(status) \
(((signed char) (((status) & 0x7f) + 1) >> 1) > 0)
上面的代碼顯示,程序的退出狀態是一個16位數字,其中高8位是程序返回的狀態,如果由於信號而退出該程序,則剩余的一些/全部位將被置位,即7位其中表示導致程序退出的信號。 這就是為什么在如上所述的情況下,無法從system()返回的退出狀態中減去128的原因。
由於system()函數也使用fork()
創建新進程並等待進程終止,因此在此處也可以使用相同的方法來檢查子進程在父進程中的狀態。 以下程序演示了這一點:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main(void)
{
int status = system("prg_name");
if (WIFEXITED(status))
printf("Exited Normally, status = %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("Killed by Signal %d which was %s\n", WTERMSIG(status), strsignal(WTERMSIG(status)));
return 0;
}
回答我自己的第二個問題。
gcc -Q -v vul.c
命令顯示了傳遞給gcc
的選項。 Ubuntu
的選項包括-fstack-protector-strong
標志,而arch-linux
。 因此,在Ubuntu
,默認情況下將標志傳遞給gcc
。
您的vul.c和exp.c中存在兩個問題。
在vul.c中
char buf[10];
在這種情況下10是不夠的,因為argv [1],即$(perl -e'print“ A” x100',大於要分配的緩沖區。增大buf的大小應可修復分段錯誤。
在exp.c中,您缺少一個單引號,應按如下所示進行修改:
printf("%d\n", system("./vul $(perl -e 'print \"A\"x100')"));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.