格式化字符串利用,意外结果

Format String Exploit, unexpected result

我正在尝试在 64 位 Linux 上用 C 实现格式字符串漏洞的简单示例。这是我的源代码:

void not_called() {
    printf("Exploited\n");
}

int main(int argc, char **argv) {

    // Buffer overflow vulnerability
    char buffer[256];
    gets(buffer);

    // User may input the format string
    printf(buffer);
}

我输入以下字符串进行编程

AAAABBBB-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x

结果是

AAAABBBB-8608002c-f458fe00-fbad2288-78252d78-252d7825-5e5c51a8-0-41414141-2d78252d-78252d78-252d7825-2d78252d

我可以将 AAAA 识别为 41414141,但不能像我预期的那样识别后面的 BBBB,因为它是缓冲区中同一输入字符串的组成部分。

这是某种保护机制还是我误解了什么?我的目标是覆盖 return 地址,我需要读取 8 个字节 (AAAABBBB),因为我在 64 位机器上工作。

利用字符串的伪代码看起来像这样

RETRNPTR%[decimal address of not_called - 8]u%[offset of RETRNPTR]$n

RETRNPTR是当前栈帧中存放return地址的内存地址。

所以我最担心的是,RETRNPTR 的 8 个字节似乎并不像我预期的那样连续存在于内存中。第二个问题是,%n 是 int 指针,意味着只写入了 4 个字节。我也很困惑,AAAA 之前的那些值来自哪里。

首先,您询问的是具有未定义行为的代码;代码的大部分行为确实应该被普通的 C 程序员认为是未定义的。

现在,至于你为什么看不到BBBB。我想这是因为你是 运行 x86-64 系统上的这个程序。那里的调用约定是对堆栈中的值进行 64 位推送;但是 %x 打印 32 位 int; 64 位堆栈值的 32 个最低有效位。要打印 64 位 long 值,请使用 %lx.

(此外,在 System-V API 上,对于 x86-64,前 6 个 integer/pointer 参数根本不进入堆栈;它们在 RDIRSIRDXRCXR8R9 寄存器)。

例如我输入:

AAAABBBB %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx

得到输出

AAAABBBB 7f7c0a88f030 7f7c0a66b980 7f7c0a669980 7f7c0a88f031 786c2520786c2520 7ffc1456b6f8
100000000 4242424241414141 786c2520786c2520 786c2520786c2520