使用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
上执行的上述程序在使用 ./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;
}
在同一系统上使用 ./exp
命令执行时使用 gcc -o exp exp.c
编译给出以下输出:
AAAAAAAAAAAA...A
139
我有两个问题:
- 为什么 2nd 程序没有产生错误信息?并且,
- 我需要使用
-fstack-protector
标志编译程序以在 arch linux
中启用 *** stack smashing detected ***
错误消息,但在 Ubuntu
中不启用。在 Ubuntu
中,可能是 gcc
中默认包含此标志,或者还有其他原因吗?
正如我在评论中指出的那样,system
return 是一个具有程序 return 值的整数,通常是错误代码(如果成功则为 0)。
如果您想将错误打印为漂亮的消息,您可以使用 strerror
。
根据@rht 的评论(请参阅我的下一个编辑)和该评论中引用的问题的答案,returned 值在成功时将为 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
无法控制。
EDIT(2) - 回应评论。
错误消息 returned 可能不是 errno
值,而是信号 trap
值。
这些有时很难区分,关于如何在不使用 memcmp
反对答案的情况下判断它是哪一个,我没有好的建议。
在这种情况下,您知道 vul.c
永远不会 return 它是 errno
值,这只会给您留下信号陷阱错误,因此您可以使用 strsignal
来打印错误信息。
正如@rht 的评论中所指出的,其中引用了 this question:
Passing tmp to strsignal
generates the same error message: "unknown signal 139". The reason is that there is no signal with this signal number. /usr/include/bits/signum.h contains all the signals with their signal numbers. Passing tmp-128
to strsignal works.
即
#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;
}
编辑
问题已编辑,因为它的代码被错误复制。我更改了答案以反映该更改。
回答我自己的第 2 个 个问题。
gcc -Q -v vul.c
命令显示传递给 gcc
的选项。 Ubuntu
中的选项包括 -fstack-protector-strong
标志但不在 arch-linux
中。所以在Ubuntu
中,flag默认传给了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')"));
根据我对@Myst 对 "passing tmp-128
to 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位是程序返回的状态,如果程序退出则设置其余位some/all因为一个信号,其中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;
}
考虑以下存在缓冲区溢出漏洞的程序 (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;
}
在同一系统上使用 ./exp
命令执行时使用 gcc -o exp exp.c
编译给出以下输出:
AAAAAAAAAAAA...A
139
我有两个问题:
- 为什么 2nd 程序没有产生错误信息?并且,
- 我需要使用
-fstack-protector
标志编译程序以在arch linux
中启用*** stack smashing detected ***
错误消息,但在Ubuntu
中不启用。在Ubuntu
中,可能是gcc
中默认包含此标志,或者还有其他原因吗?
正如我在评论中指出的那样,system
return 是一个具有程序 return 值的整数,通常是错误代码(如果成功则为 0)。
如果您想将错误打印为漂亮的消息,您可以使用 strerror
。
根据@rht 的评论(请参阅我的下一个编辑)和该评论中引用的问题的答案,returned 值在成功时将为 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
无法控制。
EDIT(2) - 回应评论。
错误消息 returned 可能不是 errno
值,而是信号 trap
值。
这些有时很难区分,关于如何在不使用 memcmp
反对答案的情况下判断它是哪一个,我没有好的建议。
在这种情况下,您知道 vul.c
永远不会 return 它是 errno
值,这只会给您留下信号陷阱错误,因此您可以使用 strsignal
来打印错误信息。
正如@rht 的评论中所指出的,其中引用了 this question:
Passing tmp to
strsignal
generates the same error message: "unknown signal 139". The reason is that there is no signal with this signal number. /usr/include/bits/signum.h contains all the signals with their signal numbers. Passingtmp-128
to strsignal works.
即
#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;
}
编辑
问题已编辑,因为它的代码被错误复制。我更改了答案以反映该更改。
回答我自己的第 2 个 个问题。
gcc -Q -v vul.c
命令显示传递给 gcc
的选项。 Ubuntu
中的选项包括 -fstack-protector-strong
标志但不在 arch-linux
中。所以在Ubuntu
中,flag默认传给了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')"));
根据我对@Myst 对 "passing tmp-128
to 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位是程序返回的状态,如果程序退出则设置其余位some/all因为一个信号,其中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;
}