7

I'm trying to exploit a binary file which I have access to the source code.

int flag = 0;

int main() {
    char buf[0x50];
    puts("Who are you? ");
    printf("> ");
    fgets(buf, 0x50, stdin);
    printf(buf);

    if (flag == 1337) {
        puts("Enjoy your shell!");
        system("/bin/sh");}
    else {
        puts("Not allowed");
    }
    return 0;
}

As you can see from source code, if flag is 1377 I'll get a shell. I can see that with gdb aswell.

 0x4007ec <main+175>       mov    eax, DWORD PTR [rip+0x200892]       # 0x601084 <flag>
 0x4007f2 <main+181>       cmp    eax, 0x539

Security measures are setup like this:

Canary                        : Yes
NX                            : Yes
PIE                           : No
Fortify                       : No
RelRO                         : Partial

So, first of all I cannot do the classic buffer overflow because the program uses fgets to gather input. Of course canary is there too, but will make no harm because If I was able to change flag's value (before Canary's check was made) I would be successful on getting that shell. I don't know If what I'm thinking this right, so please correct me If I'm wrong.

My first conclusion on this was that, I would not be able to exploit buf in order to rewrite flag's value. (I assumed that buf and flag would be placed right next to each other on the stack). I think I'm right on this because when I took a look at $rsp register and found that only the allowed amount of "A"'s were placed on the stack. So even if flag was placed right beneath it, flag's value would not be overwritten. Am I right so far? That would be my first question

0x7fffffffdaf0: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdb00: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdb10: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdb20: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdb30: 0x41414141  0x41414141  0x41414141  0x00414141
0x7fffffffdb40: 0x00400840  0x00000000  0x96703f00  0x948afed7
0x7fffffffdb50: 0xffffdc40  0x00007fff  0x00000000  0x00000000

So, how would I be able to range that value? I think that the exploit must come from the payload provided by a malicious user but buf is the only way to get that payload into the stack. Since I cannot overwrite registers by overflowing buf I'm a bit lost.

Anders
  • 64,406
  • 24
  • 178
  • 215
fish202
  • 119
  • 3
  • 7

2 Answers2

6

You are right that the buffer cannot overflow the flag because of the bounds check. Fgets also includes the null character in its bound check.
http://www.cplusplus.com/reference/cstdio/fgets/

However, there is a string format vulnerability here:

printf(buf);

The user-controlled variable buf is used in a printf statement, causing a format string vulnerability.

https://www.exploit-db.com/docs/28476.pdf

Using a combination of %x %n you can overwrite the flag with "1337." %x is used to pop values off the stack, and %n is used to write the number of characters into that address. The "1337u" expands the number of characters so you can write the correct value. For example, if the memory location of flag is "0xffffff80"

$(python -c 'print "\x80\xff\xff\xff"+"%x%1337u%n"')

This will write a number that is greater than 1337 because of the other stuff before the "1337u," so you just subtract that number by the amount you go overboard, and you'll have the right number. Or, if you want to do some math, the value of "u" is: “The byte to be written” – “the outputted byte” + “the width of the %x specified just before the %n”

Daniel Grover
  • 872
  • 5
  • 10
  • after some digging into `Format String exploit` I understand almost everything you suggested with just one exception. Why would I want to write 1337 to buf location?! Wouldn't that be flag's location instead? Clearly, flag's memory location is lower than buf location. That being said it suggests that flag isn't located at the runtime stack at all like someone suggested. If so, am I be able to exploit that only by changing that address? – fish202 Dec 01 '17 at 19:43
  • @fish202 it was not a suggestion, it was a statement of fact. `flag` is not in the stack. – julian Dec 01 '17 at 19:55
  • @SYS_V sure! I had that thought too. – fish202 Dec 01 '17 at 20:07
  • Simply writing 1337 to the stack is insufficient. Furthermore, the binary in question will execute in 64-bit address space, so the address `0xffffff80` will not be in the stack. – julian Dec 01 '17 at 20:08
  • @SYS_V how could I do it then? Besides patching binary – fish202 Dec 01 '17 at 20:20
  • @Daniel your answer was pretty close to solve this. I just had to take one thing in consideration. We all know that `printf` prints strings that are NULL terminated (will stop printing if reaches a `0`). In order to represent 0x601084 as an address I have to do it like `\x84\x10\.....\x00`. This means that I cannot embed the address first on the string, which means that everything after `0`, will not be printed. It should be the last thing on the payload. I managed to do it like with this `%1337d%9$n \x84\x10\x60\x00\x00\x00\x00\x00`. Padding was added in order to align registers. – fish202 Dec 07 '17 at 02:50
2

flag is not local to any function and global in scope. Therefore, it is not located on the runtime stack. Either patch the binary or take advantage of the fact that input to buf is not sanitized and that buf an argument to printf (manipulate the value of the format string argument such that 1337 is written to address 0x601084).


flag is a statically allocated variable whose value will be stored in the data or bss segment of the process rather than in the runtime stack. If you have access to the binary you can simply patch it such that the value 1337 is stored at address 0x601084, which should be in the .data or .bss section. Since here global variable flag is initialized to 0, it will likely be in the .bss section of the binary and the bss segment of the process (this would not be the case if it was initialized to some other value).

Even if one did not know how the compiler allocates memory for variables based on their location in the source code, one could still determine that flag is not stored on the runtime stack by comparing its address with that of the stack pointer %rsp: location 0x601084 is far lower in memory than 0x7fffffffdaf0.

julian
  • 1,269
  • 1
  • 8
  • 15
  • 1
    Curious - would patching a binary be kind of "not performable in a real world scenario"? Like... if you can patch a binary you'll just be able to run arbitrary code regardless , no? Just patch the entire binary into a program that just launches a shell and quits, or whatever? – Monica Apologists Get Out Dec 01 '17 at 15:06
  • 1
    @Adonalsium In a real-world scenario would one be trying to exploit a binary if they were in possession of its source code? If this is an exercise, which it seems to be, how concerned should we be about generalizing the approach to scenarios subject to different or more severe constraints? These considerations, while important, fall outside the scope of the problem. The point of proposing patching was to draw attention to the fact that the location of interest is not on the runtime stack and that the constraints are not clearly articulated. It's a big world, and there are options besides BoF – julian Dec 01 '17 at 16:21
  • That's a very good point. Thanks for the response. – Monica Apologists Get Out Dec 01 '17 at 19:34
  • @SYS_V Then... would this payload `\x84\x10\x60"+"%x%1337u%n` do the job? – fish202 Dec 01 '17 at 20:08
  • @SYS_V it's extremely common to have a program's source code and be trying to exploit it. – Joseph Sible-Reinstate Monica Dec 06 '17 at 16:02