Sqix

Return Oriented Programming 본문

Exploit Techniques/Pwnable

Return Oriented Programming

Sqix_ow 2019. 9. 20. 18:29

ROP 기법에 대해 공부한 것을 간략하게 정리해 보았습니다.

 

Return Oriented Programming은 GOT Overwrite 기법과 Return to Libc 기법을 응용한 공격기법입니다.

 

주로 NX bit와 같은 보호기법이 있는 실행 파일에서 gadgets를 이용해서 코드를 실행할 수 있게 해 줍니다.

1. 사전 지식

1. Gadgets와 Payload 구성

기본적으로 Return to Libc 기법을 사용할 때, 스택은 다음과 같은 구조를 가집니다.

 

... - [libc 시스템 함수 주소] - [시스템 함수 호출 이후 돌아갈 주소] - [system() 파라미터]

 

여기서 gadgets를 이용해서 여러 개의 함수를 실행할 수 있도록 스택을 바꿀 수 있습니다.

 

gadgets란, rop 혹은 rtl chaining 기법을 사용할 때 사용되는 코드 조각입니다.

 

일반적으로 gadgets는 parameter의 수 만큼의 pop + ret로 이루어집니다. 

 

우리가 gadgets를 찾아서 사용하는 이유는, 이전 함수에서 사용했던 인자들을 pop을 통해 스택 상에서 정리하고, ret를 통해서 다음 return address로 넘어가기 위함입니다.

 

가령, puts("AAAA");를 실행하고 puts("BBBB");를 실행하고 싶다면 다음과 같이 payload 구성을 하면 됩니다.

 

[ Buffer ] - [ SFP ] - [ puts() 주소 ] - [ pop - ret ] - [ "AAAA" 주소 ] - [ puts() 주소] - [ dummy ] - [ "BBBB" 주소 ]

 

Gadget은 기본적으로 objdump를 이용해 찾을 수 있습니다.(objdump -d "filename" | grep "ret")

 

peda의 ropgadget을 이용하면 더욱 편리하게 찾을 수 있습니다.

 

2. 쓰기 가능한 영역

 

주로 쉘을 얻기 위해서 우리는 "/bin/sh" 문자열을 이용합니다.

 

따라서 우리는 쓰기 가능한 영역에 "/bin/sh" 문자열을 올려 주고, 이를 이용해서 system("/bin/sh")를 실행합니다.

 

쓰기 가능한 영역을 찾기 위한 명령어는 다음과 같습니다.

 

cat /proc/"process number"/maps
objdump -h "filename"

 

우선 해당 파일의 프로세스명의 메모리 맵을 확인하고, 여기서 쓰기 권한이 있는 섹션을 확인합니다.

 

우리는 주로 .bss 영역을 사용하게 됩니다. 

 

.bss 영역은 초기화되지 않은 전역 변수가 들어가며, 일반적으로 0으로 초기화됩니다. 

 

이곳에 "/bin/sh" 문자열을 넣고, 이 주소를 가리키면 system("/bin/sh");를 실행할 수 있게 됩니다.

3. PLT, GOT

GOT Overwrite를 위해서 PLT(procedure linkage table), GOT(global offset table)에 대해 알아야 합니다.

 

Dynamic Link로 링킹된 경우, 외부 함수를 호출할 때 PLT, GOT를 이용한 함수 호출 과정을 거치게 됩니다.

 

이미 호출을 한 번 이상 한 함수의 경우 GOT에 함수 주소가 저장되어 바로 호출을 하게 됩니다.

 

하지만, 최초로 함수를 호출하는 경우, 함수의 주소를 구하는 메커니즘을 수행하게 됩니다.

 

함수의 최초 호출 과정을 요약하면 다음과 같습니다.

 

1. 함수의 PLT로 접근, GOT로 jmp.

2. GOT에서 jmp plt+6 명령어 수행. (plt+6)

3. PLT + 6 참조하여 reloc_offset을 스택에 push

4. link_map 구조체 포인터를 스택에 push

※ link_map : 라이브러리의 정보를 담는 구조체.

5. dynamic linker에 진입

6. dl_runtime_resolve 함수 호출 

7. _dl_fixup을 reloc_offset, link_map 구조체 포인터를 인자로 삼아 호출(JMPREL, STRTAB, DYNSYM 구조체 확인.)

※ JMPREL : 함수 재배치 테이블 / STRTAB : 함수 이름이 저장된 구조체

※ DYNSYM : 함수의 주소와 이름, 로드 여부의 정보를 담은 구조체 / reloc_offset : JMPREL에서 호출한 함수를 찾기 위한 값

8. _dl_fixup에서 구해온 함수명을 인자로 _dl_lookup_symbol_x 함수 호출

9. _dl_lookup_symbol_x 함수에서 라이브러리의 base 주소, SYMTAB에서 함수 정보가 담긴 구조체 주소 획득.

10. SYMTAB의 2번째 4byte 함수 오프셋 + 라이브러리 base 주소로 해당 함수의 주소 연산 (eax에 저장)

11. GOT에 해당 값 저장 후 main으로 복귀

 

여기서 got를 다른 값으로 변경하여 프로그램의 실행 흐름을 임의로 조작하는 것을 GOT Overwrite라고 합니다.

2. Exploit

1. Stage 1, Stage 0

 

Stage 1에서는 ROP를 진행하는 데 필요한 정보를 모아야 합니다.

 

1. 어디서 BOF가 터지고, RET값을 덮으려면 어떠한 input을 넣어야 하는지 확인하기

2. 쓰기 가능한 영역(ex : .bss 영역)의 주소 알아내기

3. 필요한 함수들의 주소(libc base 주소, 호출 함수들의 offset) 알아내기

4. 호출하려는 함수들에 알맞는 gadget 구하기

 

이후 Stage 0에서는 이 요소들을 모아 payload, exploit code를 구성해서 exploit를 진행합니다.

 

1. "/bin/sh" 명령어를 쓰기 가능한 메모리 영역에 저장하기.

2. 이용할 함수의 .got 영역에 system 함수 주소 삽입

3. 이용할 함수 호출 (system 함수가 저장되어 있기 때문에 system함수가 호출된다)

'Exploit Techniques > Pwnable' 카테고리의 다른 글

Stack Pivoting  (0) 2019.09.22
Comments