Search code examples
c++cbinarybuffer-overflowghidra

Why is this binary vulnerable to buffer overflow?


This is an extract of a binary that is buffer overflowed. I decompiled it with Ghidra.

  char local_7 [32];
  long local_78;

  printf("Give it a try");
  gets(local_7);
      if (local_78 != 0x4141414141414141) {
        if (local_78 == 0x1122334455667788) {
          puts ("That's won")
        }
        puts("Let's continue");
      }

I'd like to understand why it is possible to make a buffer overflow.
I checked the "0x4141414141414141" hex value and saw it was related to "A" string. But what the conditions related to "0x4141414141414141" and "0x1122334455667788" exactly do ? And to be more precise, what the user could answer to get the message ("That's won") ?
Any explanations would be greatly appreciated, thanks !

___EDIT___
I have to add that I see these two hex values at using "disas main" command :

0x00000000000011a7 <+8>: movabs $0x4141414141414141,%rax  
0x00000000000011e6 <+71>: movabs $0x4141414141414141,%rax  
0x00000000000011f6 <+87>: movabs $0x1122334455667788,%rax

I tried a buffer overflow using python3 -c "print ('A' * 32 +'\x88\x77\x66\x55\x44\x33\x22\x11')" | ./ myBinary.
But I always have the "Let's continue" message. I'm not that far from the solution but I guess I miss a thing.. Could you help me what ?

___EDIT 2___ Before the gets :

  char local_7 [40];
  long local_78;
  
  local_78 = 0x4141414141414141;
  printf("Give it a try");
  fflush(stdout);
  gets(local_7);
  [... and so on]

Solution

  • This binary is vulnerable to a buffer overflow because it uses the gets() function, which is vulnerable, and deprecated because of that reason.

    It will copy the user input to the passed buffer, without checking the size of the buffer. So, if the input of the user is larger than the available space, it will overflow in memory and potentially, overwrite other variables or structures that are located after the buffer.

    That is the case of the long local_78; variable, which is in the stack after the buffer, so we can potentially overwrite its value.

    To do so, we need to pass an input that is:

    • minimun 32 bytes, to fill the actual buffer. (A char (ASCII character) should usually equivalent to 1 byte)
    • plus, an additional variable number of bytes to fill the space between the buffer and the long variable (this is because a lot of times, compilers make optimization and may add other variables between those two, even if we haven't placed them just there in the code. The stack is a dynamic memory region so it's not often possible to 100% predict its layout)
    • plus, 8 bytes, which is typically the size of a long in most computer architectures (though it could be different, but let's assume this is x86/64). This is the value we will be overflowing the variable with.

    We don't care about the stuff we put in the first 32+X bytes (except for the null byte). The program then checks for some special value of local_78, and if that check passes, it will execute puts ("That's won"); saying that you have "won" or successfully exploited the program and overwrote the memory.

    The problem here, is that such value is 0x1122334455667788 (again, a long which is 8 bytes). We could read this separating its bytes: 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88, and trying to see which byte corresponds to which character in ASCII The issue is that bytes like 0x22 are not ASCII representable characters, so you cannot type them directly into the console, because normal keyboards don't have a key that inputs the character 0x11 as it doesn't have a visual representation. You will need an additional program to exploit the program. Such program will need to use any mechanisms available in the Operating System to pass such values. In Linux for example this can be done using pipes / output redirection