繁体   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