Alright, after fd and col we arrive at the bof challenge. Bof stands for buffer overflow and to not make the previous mistake of not completely understanding how to abuse an error I first did some research (buffer overflows are not that common in high level languages so uncommon to a lot of ITers). Good thing nagarrossecurity has a very interactive tutorial.
Ok let us open the code in Ghidra, we immediatly go to the main function and see the following.
PUSH EBP
0001068a MOV EBP,ESP
0001068b AND ESP,0xfffffff0
0001068d 00010690 SUB ESP,0x10
00010693 MOV dword ptr [ESP]=>local_20,0xdeadbeef
CALL func
0001069a MOV EAX,0x0
0001069f LEAVE
000106a4 RET 000106a5
That is not a lot of code, we have our function prologue, a reservation of 16 bytes on the stack, a pre-defined hex code that we mov to the stack and a function call. After the function call we just set EAX to zero and exit main. Cool cool… let’s see what is in func
0001062c PUSH EBP 0001062d MOV EBP,ESP 0001062f SUB ESP,0x48 00010632 MOV EAX,GS:[0x14] 00010638 MOV dword ptr [EBP + local_10],EAX 0001063b XOR EAX,EAX 0001063d MOV dword ptr [ESP]=>local_4c,s_overflow_me_:0001 = “overflow me :” 00010644 CALL puts 00010649 LEA EAX=>local_30,[EBP + -0x2c] 0001064c MOV dword ptr [ESP]=>local_4c,EAX 0001064f CALL gets 00010654 CMP dword ptr [EBP + param_1],0xcafebabe 0001065b JNZ LAB_0001066b 0001065d MOV dword ptr [ESP]=>local_4c,s/bin/sh_0001079b = “/bin/sh” 00010664 CALL system 00010669 JMP LAB_00010677 0001066b MOV dword ptr [ESP]=>local_4c,s_Nah.._000107a3 = “Nah..” 00010672 CALL puts 00010677 MOV EAX,dword ptr [EBP + local_10] 0001067a XOR EAX,dword ptr GS:[0x14] 00010681 JZ LAB_00010688 00010683 CALL __stack_chk_fail 00010688 LEAVE 00010689 RET
So this is the part were we should be able to cause a buffer overflow. A quick look diagonal shows us that the only part that actually allows user input is the gets. But what it makes extra interesting is that there is a stack overflow check as well so everything must happen in this function and before the call to __stack_chk_fail!
Ok let us go over it 1. Function epilogue with a stack frame size of 0x48 (72 bytes) 2. The stack check will be stored inside local_10 3. A string is stored in local_4c before a puts call is made 4. We move the address of EBP – 0x2C to EAX (which is local_30) 5. We call gets. Gets accepts a pointer to which address it should write the user input. The thing about gets is is that it is quite unsafe as it has zero checks on the length of user input. Here is where the buffer overflow will happen. 6. So let’s calculate how much bytes we must fill before we can overwrite our param1. 1. Param one is located at EBP+0x4 2. Local_30 is located at EBP-0x30 3. Difference is (0x30 + 0x4) 0x34 or 52 bytes!
Now how you can solve this is quite easy, generate some random char data of the size of 52 bytes and then write 0xcafebaby
as a little endian (CHAR data isn’t read as little endian but the code looking at param_1
will expect this as a little endian as it expects an int) . Many other websites already show us how this can be done.