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