This school CTF had a good set of challenges for beginners
pwn1
32-bit executable, dynamically linked, not stripped
When you run the executable in the terminal, the program simple asks for an input and checks whether it is the secret it is looking for or not.
debugging in GDB…
1 | gdb-peda$ checksec |
Going through the assembly code, we can see that see a gets() function. This gives us a chance to corrupt any stack address higher than the stack address where the gets() is to read into. We can also see a call to ‘print_flag’ function. This call instruction is executed only after a cmp instruction which compares [ebp-0xc] with ‘0xf007ba11’.
1 | 0x0804861d <+107>: cmp DWORD PTR [ebp-0xc],0xf007ba11 |
The value in the memory location ‘0xc’(12) bytes lower than the memory location that is stored in ebp is compared with ‘0xf007ba11’. If the compare statement is true it sets the ZF, and jne instruction is not executed and ‘print_flag’ is called.
EXPLOIT
Here the gets() function allows us to write into any higher stack memory location. Hence we can write ‘0xf007ba11’ in the required memory location to get the flag.
1 | from pwn import * |
## pwn2
32-bit executable, dynamically linked, not stripped
This challenge is similar to last one expect that there is a function ‘print flag’ that prints the flag.
debugging in GDB…
1 | gdb-peda$ checksec |
The program simply reads the input and prints it.
1 | 0x080485d2 <+32>: lea eax,[ebp-0xef] |
EXPLOIT
Again, a vulnerable gets() function is used to read the input. We can simply overwrite the return address of the function and get the flag as ‘print flag’ function is called.
1 | from pwn import * |
## pwn3
32-bit executable, dynamically linked, not stripped
pwn3 is similar to pwn2 in the way it reads the input, via a gets() function.
debugging in GDB…
1 | gdb-peda$ checksec |
The program when run gives us a leak of a stack address. This address is the same as the address that gets() reads into.
1 | 0x080484d7 <+12>: lea eax,[ebp-0xee] |
EXPLOIT
As we can see that NX is disabled, the stack is a executable section of memory. We can write opcodes on the stack and then overwrite the return address with the address given to us by the program. So the shellcode is executed and shell is pwned. I used pwntools to receive the leak and add it into my exploit.
1 | from pwn import * |
## pwn4
32-bit executable, dynamically linked, not stripped
Here we have a menu driven program that has bigger code but the vulnerability is again a gets() function that reads our the choice.
debugging in GDB…
1 | gdb-peda$ checksec |
We can control the flow of the program by overwriting the return address. We can see PLT addresses of system() as it is used in the code. To pwn the shell we need to call system with a pointer to “/bin/sh” as the argument.
EXPLOIT
Before calling system we need to somehow get a pointer to “/bin/sh”. We have a gets() function that can read to any memory location. So we overwrite the return address of the function with gets() and the argument as any address that has read-write permission like from the bss section or the data section. So we can now the write “/bin/sh” into that address. The return address of read() is right above return address of the function we overwrote. So we can write that stack location with the address of system() and the pointer to “/bin/sh” as argument. The return address of system() will now be a pointer to “/bin/sh”.
1 | from pwn import * |
## pwn5
32-bit executable, statically linked, not stripped
pwn5 is also a menu driven program. The vulnerability is a gets() function, but the gets() is in a function ‘change_major’ which is called when the corresponding option is selected.
debugging in GDB…
1 | gdb-peda$ checksec |
EXPLOIT
This time there is no system(). The binary is statically linked so we can use the ‘int 0x80’ instruction to get the shell. to get the shell when executing ‘int 0x80’ instruction we have to have the reference no. of ‘execve’ in eax, pointer to ‘/bin/sh’ in ebx and null pointers in ecx and edx. We have to prepare a rop chain to get everything ready before execution of ‘int 0x80’.
1 | from pwn import * |