考虑以下具有缓冲区溢出漏洞的程序(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
我有两个问题:
-fstack-protector
标记编译程序,以便在*** stack smashing detected ***
中启用arch linux
错误消息,但不在Ubuntu
中启用。在Ubuntu
中,可能默认情况下此标记包含在gcc
中,还是有其他原因?答案 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()函数也使用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')"));