3

My Friend recently completed a hacking challenge and sent it to me (binary and source). I wanted to ask here before I asked him for tips as I want to do it myself :)

I've been going through it but I am struggling to find the vulnerability.

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

static void usage(const char *argv0) {
    printf("Build your own string!\n");
    printf("\n");
    printf("Usage:\n");
    printf("  %s length command...\n", argv0);
    printf("\n");
    printf("Each command consist of a single character followed by it's index.\n");
    printf("\n");
    printf("Example:\n");
    printf("  %s 11 h0 e1 l2 l3 o4 w6 o7 r8 l9 d10\n", argv0);
    exit(1);
}

int main(int argc, char **argv) {
    char *buffer;
    unsigned short buffersize, i, index, length;

    if (argc < 2) usage(argv[0]);

    length = atoi(argv[1]);
    if (length <= 0) {
            fprintf(stderr, "bad length\n");
            return 1;
    }

    buffersize = length + 1;
    buffer = alloca(buffersize);
    memset(buffer, ' ', buffersize);
    buffer[buffersize - 1] = 0;

    for (i = 2; i < argc; i++) {
            if (strlen(argv[i]) < 2) {
                    fprintf(stderr, "bad command \"%s\"\n", argv[i]);
                    return 1;
            }

            index = atoi(argv[i] + 1);
            if (index >= length) {
                    fprintf(stderr, "bad index in command \"%s\"\n", argv[i]);
                    return 1;
            }

            buffer[index] = argv[i][0];
    }

    printf("%s\n", buffer);
    return 0;
}

I think the vulnerability lies within the short int, and the use of alloca.

Entering ./app 65535 65535 can cause a segfault but I can't actually override anything since buffer will only ever be set to max 65535 or it loops around. This makes me think I can't override the EIP to inject shellcode.

Can anyone help me with where to look at?

Thanks!

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
JDoby
  • 49
  • 4
  • Try inputting the number defined as `USHRT_MAX` in your environment for the first argument, – MikeCAT Feb 27 '16 at 15:06
  • @MikeCAT Yeah I tried that, I can get a seg fault but cant override anything of value since the int will just loop around if I set it larger than the max. – JDoby Feb 27 '16 at 15:12
  • @JDoby, how will you get a segmentation-fault? – Box Box Box Box Feb 27 '16 at 15:12
  • Debugger should be useful to see where the variables and return address are located, and what is the value in `buffer`. – MikeCAT Feb 27 '16 at 15:15
  • since `buffersize` and `length` are `unsigned int`: setting `length` to 65535 will cause `buffersize` to be 0, so nothing will be allocated on the stack for `buffer`. However, since `index` is compared to `length`, `buffer[index]` can be used to override the first 64Kb of the stack. – isedev Feb 27 '16 at 15:26

1 Answers1

5

Actually, the vulnerability lies in the fact that you can store a character at any offset in the buffer allocated with alloca, but the test is done on length rather than size. passing arguments of 65535 and a1 invokes undefined behavior: size as value 0 because of arithmetic wraparound if unsigned short has 16 bits.

You can try passing a first argument of 65535 and subsequent arguments with increasing offsets, that will poke values beyond the end of buffer, possibly overwriting the return address of main and causing a crash:

myprog 65535 a3 a7 a15 a19 a23 a27 a31 a35 a39 a43 a47 a51 a55 a59 a63 ...

Depending on the actual local variable layout, the required offset may be larger than 17, but should be smaller than 80.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Awesome, I actually tried this but didn't enter enough to actually cause a crash! How would I go about entering 'A's for this to override EIP with x414141 as I can only seem to enter offset numbers like you said. – JDoby Feb 27 '16 at 15:49
  • @JDoby: you would first locate the return address, say 24 and then pass arguments A24 A25 A26 A27... 4 or 8 of them depending on the architecture. But a single poke into a more significant byte of the return address should suffice, and would be more elegant. – chqrlie Feb 27 '16 at 15:55