簡體   English   中英

使用system()執行具有緩沖區溢出漏洞的程序時沒有錯誤消息

[英]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

我有兩個問題:

  1. 為什么第二程序沒有生成錯誤消息? 和,
  2. 我需要使用-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/s‌ignum.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()的源代碼

由於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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM