使用system()执行具有缓冲区溢出漏洞的程序时没有错误消息

时间:2016-08-17 05:33:40

标签: c linux gcc buffer-overflow

考虑以下具有缓冲区溢出漏洞的程序(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上执行的上述程序在使用./vul $(perl -e 'print "A"x100')命令的终端中执行时给出了以下输出:

AAAAAAAAAAA...A
Segmentation fault (core dumped)

然后使用echo $?命令检查程序状态,输出139

关注程序(exp.c)(用于崩溃上述程序)

#include <stdlib.h>

int main(void)
{
    printf("%d\n", system("./vul $(perl -e 'print \"A\"x100')"));
    return 0;
}
在同一系统上使用gcc -o exp exp.c命令执行时使用./exp编译的

给出了以下输出:

AAAAAAAAAAAA...A
139

我有两个问题:

  1. 为什么2 nd 程序没有生成错误消息?和,
  2. 我需要使用-fstack-protector标记编译程序,以便在*** stack smashing detected ***中启用arch linux错误消息,但不在Ubuntu中启用。在Ubuntu中,可能默认情况下此标记包含在gcc中,还是有其他原因?

4 个答案:

答案 0 :(得分:1)

正如我在评论中指出的那样,system返回一个包含程序返回值的int,通常是错误代码(如果成功则为0)。

如果您想将错误打印为漂亮的消息,可以使用strerror

根据@ rht的评论(参见我的下一个编辑)以及该评论中引用的问题的答案,返回值在成功时将为0,如果出错,则为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无法控制。

编辑(2) - 回答评论。

可能是返回的错误消息不是errno值,而是信号trap值。

这些有时难以区分,我没有很好的建议,如果不使用memcmp来回答问题,你怎么能分辨出它是什么。

在这种情况下,您知道vul.c将永远不会返回errno值,这样您只会遇到信号陷阱错误,因此您可以使用strsignal来打印错误消息。

正如@ rht的评论所指出的那样,它引用了this question

  

将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;
}

修改

问题已被编辑,因为它的代码被错误复制了。我改变了答案以反映这种变化。

答案 1 :(得分:1)

从我的评论到@Myst的回答&#34;将tmp-128传递给strsignal()&#34;函数,经过一些实验后我发现它在程序正常退出但返回状态不是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()'s source code

由于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;
}

答案 2 :(得分:0)

回答我自己的2 nd 问题。

gcc -Q -v vul.c命令显示传递给gcc的选项。 Ubuntu中的选项包含-fstack-protector-strong标记但不包含arch-linux。因此,在Ubuntu中,标志默认传递给gcc

答案 3 :(得分:0)

你的vul.c和exp.c中存在两个问题。

在vul.c中,

char buf[10];
在这种情况下,

10是不够的,因为argv [1],即$(perl -e&#39; print&#34; A&#34; x100&#39;)大于缓冲区已放大buf大小应修复分段错误。

在exp.c中,您缺少一个单引号,应按以下方式修改:

printf("%d\n", system("./vul $(perl -e 'print \"A\"x100')"));