kangsecu's B1og

Pwnable - Return Oriented Programming(ROP) 본문

System

Pwnable - Return Oriented Programming(ROP)

Kangsecu 2020. 3. 10. 18:01

드디어 rop를 정리한다. 공부해가면서 계속해서 내용을 추가할 예정이다.

이전 내용 복습 -

간단하게 RTL Chaining을 훑고 가보자.

RTL Chaining은 RTL을 pop ret을 이용해서 여러 함수의 호출을 연계하는 것이다. 예를들어 

read함수를 이용하여 bss영역에 /bin/sh을 입력하고 이를 system함수의 인자로 넘겨주기 위해서는

dummy + sfp + read@plt + pop pop pop ret Gadget + 0x0  + &bss + 0x8 + system@plt + pop ret Gadget + &bss 

이렇게 페이로드를 구성하고 read는 0일때 표준입력이니까 이를 이용해서 bss에 /bin/sh을 넘겨주면 된다.

- bss영역을 조회할때는 아이다로 보거나 readelf -s [파일명]를 한다.


0. ROP?

내부의 코드들을 이용해서 콜스택을 제어하는 공격기법

우선 rop를 공부하기 위해 나는 rtl , got overwrite , rtl chaning을 우선 공부했다. 

ROP는 NX bit랑 code signing, ASLR을 우회하기 위하여 사용된다.

1. Gadget

rop를 하기 위해선 rop가젯이 필요한데 가젯이란 해당 프로그램이 사용하는 메모리에 이미 존재하는 명령어들을 말한다.

가젯들은 대부분 ret;로 마무리되며, 공유libc나 기존 프로그램에 존재 

pr , ppr ,pppr 등은 pop; ret; 의 갯수를 나타낸걸로 전달받는 인자갯수를 나타낸다.

pop을 이용해서 esp레지스터 값을 조정해 호출할 인자수를 조절하기 때문에 굉장히 중요하다.

간단한 페이로드를 통하여 생각해보자

payload = "A"*140
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)

이러면 dummy + sfp를 140개로 채워주고, ret를 pop_rdi로 덮어씌우면서 다음 puts_got에 인자로 puts_plt주소를 넘겨주게 되며 main이 실행된다. 이런식으로 가젯들을 이용하여 계속해서 무언가를 실행시킬 수 있다.

2. find Gadget

이제 rop를 하기 위해서는 위에서 말한 가젯을 찾아야한다. 

- peda

peda에서는 ropsearch, ropGadget을 이용해서 아래와 같이 찾아준다. 

ropgadget는 유용한 gadget을 한번에 찾아주고 ropsearch는 각자 검색 가능

ropsearch "pop rdi"
ropGadget
ROPgadget --binary (파일명) | grep '(찾을 가젯)'

 하지만 보통은 그냥 rp++를 사용한다.

- rp++

./rp-lin-x86 -f [파일이름] -r 4 | grep "찾을 가젯" 

이런식으로 가젯을 찾아준다.

-objdump

objdump -d 파일명 | egrep '찾을 가젯'
ex) objdump -d rop | egrep 'pop|ret'

그리고 이 가젯들을 드래곤볼마냥,, 모아서 익스를 하면 된다. 

3. find got , plt , offset

이제 가젯을 찾았다면 plt와 got의 주소를 찾아야한다. 

plt는 보통 그냥 IDA로 문제파일에서 얻거나 gdb를 통해서 p system, p read등으로 얻는다.

got는 got overwrite를 하기 위해서 찾는데 이것도 objdump를 이용하면 쉽게 찾을 수 있다.

objdump -R 파일명 | grep 함수명
ex ) objdump -R rop  | grep read

offset도 찾아야 하는데 오프셋을 찾을때는

뭐 peda에서 실행 시킨 후 p system -read 이런식으로 구해도 되겠지만 objdump에서

objdump -d libc | grep 함수명
ex ) objdump -d libc.so.6 | grep __write(__read , system)

이렇게 찾는것도 나쁘지 않다. 보호기법이 바뀐다 해도 오프셋을 일정하니까 미리 구해놓고 그걸로 함수를 호출하자.

사실 위에 plt got는 그냥 pwntool에서 제공되는 symbol을 이용해서 구하는게 더 편한 것 같다.

4. 문제풀이를 통한 exploit

64bit rop - function@plt + rdi + rsi + rdx + rcx

HackCTF에 rop 문제를 이용해서 예시를 들어보자.

binary :

NX bit , Parial RELRO

main.c :

vuln.c : 

read에서 bof

그렇다. 이제 우리는 

1. gadget을 찾는다. (write에 넘길 인자가 3개이니 pppr 을 찾는다.)

2. plt , got , offset을 구한다. (read_plt , read_got, read_offset etc)

3. bss를 구해서 /bin/sh을 저장한다. (bss영역은 주소가 바뀌지 않는 전역변수 영역)

1. 가젯을 찾아보자

pppr이 필요하니 0x8048509

2. plt , got , offset 을 찾자

 

read_plt , write_plt
write_got
write_offset
read_offset
system_offset

3. bss에 /bin/sh을 하자

bss가 있다.

페이로드 작성 

간단

간단한 설명을 하면 140개의 A를 넣어 bof를 발생시키고, write_plt를 이용해서 read_got의 실제 주소를 get

그 후 read_plt로 bss영역에 /bin/sh을 저장하고 다시 read_plt로 read_got에 system함수를 overwrite

마지막으로 read_plt로 got overwrite된 system함수에 bss영역에 있는 /bin/sh을 넘겨줌으로써 완성 

5. Reference

rop공부를 하면서 굉장히 유용했던 자료들을 정리해뒀다. 나중에도 중간중간 확인할 생각.

- lazenca : http://lazenca.net/display/TEC/02.ROP%28Return+Oriented+Programming%29-x6

- dreamhack.io : 32bit , 64bit ROP


데몬팀에서 업로드한 rop를 이해한 것을 정리해본다.

글 링크는 https://d4m0n.tistory.com/84 인데 내가 멍청해서 이해하기가 좀 힘들었다. 

read_got의 실제 주소를 구한 이유는 sys_addr을 오프셋과 연산해서 구하기 위함

일단 처음 프로그램이 실행이 되면서 read_got 랑 write_got값이 바뀜, 그 후로 안바뀜 
그리고 pppr은 문제파일이 아닌 libc에서 구한거라 안바뀜 
bss는 원래 안바뀜, 그리고 오프셋도 절대 안바뀜 

stage1에서 write_plt를 통해서 실제 바뀐 read_got를 얻음 > 이후로 안바뀜 

stage0에서 read_plt를 통해서 bss영역에 /bin/sh을 입력 
그 후 read_plt를 다시해서 system함수의 실제주소를 write_got에 overwrite 
그리고 그 밑에 write_plt에 sfp로 A 4개를 넣고 bss에 있는 /bin/sh을 인자로 넘겨줌으로써 
system("/bin/sh")이 완성 

 

p.s 

rop랑 rtl chaning 의 차이는 그냥 got overwrite를 하느냐 아니냐인가?

'System' 카테고리의 다른 글

one_gadget  (0) 2020.04.27
함수 네이밍 이용 .dynstr exploit  (0) 2020.04.19
Pwntools 정리 - 1  (1) 2020.03.08
메모리 보호기법(Memory Mitigation) for Pwn  (0) 2020.02.19
포너블 공부,,  (0) 2020.02.19