BUUCTF ciscn_2019_n_8 wp
1.使用checksec命令查看文件的保护机制开启情况:
显示STACK CANARY 、NX 、PIE 均开启 ,说明此题 非常难 或者只是 “下马威” 而已。
2.利用IDA静态分析:
main函数中直接出现了system("/bin/sh");,且该命令调用条件是(*(_QWORD *)&var[13] == 17LL),即从数组 var
的第14个元素开始,连续8字节内存所存储的64位整数值等于17。
【表达式分解:
var[13]
: 这表示数组var
的第14个元素(索引从0开始)。&var[13]
: 使用取地址运算符&
获取var[13]
的内存地址。(_QWORD *)&var[13]
: 这是一个强制类型转换。它将获取到的地址(原本可能是指向某种类型的指针,比如char*
或int*
)强制转换为_QWORD *
类型的指针。_QWORD
通常表示一个 64位(8字节) 的无符号整型(类似于unsigned long long
或uint64_t
),常见于Windows或底层编程。*(_QWORD *)&var[13]
: 这里的*
是解引用运算符。它访问刚才转换得到的_QWORD *
类型指针所指向的内存地址,并将该地址开始的 8个字节 的数据解释为一个_QWORD
类型的值。== 17LL
: 这将解引用得到的_QWORD
值与17LL
进行比较。LL
后缀表示这是一个long long
类型的常量(通常也是64位整数)。】
再向上分析可知程序首先利用 scanf 将数据读入var,没有限制输入长度的 scanf 便是突破口。但是,我们并不知道var的实际构造:长度和类型,所以无法得知如何输入可以满足(*(_QWORD *)&var[13] == 17LL),那么接下来便要寻找 var 的定义。
——通过双击 var 可以跳转到相关.bss
段:
这便给出了var
的明确信息:
变量类型与大小:
_DWORD var[15]
表明var
是一个包含15个双字(DWORD)的数组,每个 DWORD 占 4 字节(32位)。因此,
var
的总大小为 15 × 4 = 60 字节。这在内存中是一块连续的存储空间,可用于存放整数或其他32位数据。
存储位置与属性:
位于
.bss
段(bss:00004060
),这意味着var
是未初始化的全局变量或静态变量(程序加载时自动初始化为0)。public var
表明该变量是全局可见的(可能被多个函数访问)。
偏移计算(关键点):
代码
*(_QWORD *)&var[13]
试图从var[13]
开始读取8字节(QWORD)。var[13]
是数组的第14个元素(索引从0开始),偏移为 13 × 4 = 52 字节(从var
起始地址算起)。因此,
*(_QWORD *)&var[13]
会读取 偏移52到59字节 的数据(共8字节)作为一个64位整数。
3.编写python脚本:
通过以上分析,我们的payload的目标就是让输入到相对于var[0]偏移52到59字节的数据可被识别为long型的17:
var 本身类型规定每个元素占4字节,而目标数据读取8字节,那么可以考虑p32()和p64()的搭配。
【p32()
和 p64()
的函数本质是 pwntools 提供的打包函数,用于将整数按指定字节序转换为字节流。p32(数值)
生成 4字节 数据 (32位),p64(数值)
生成 8字节 数据 (64位)。在32位的程序中使用p64() 并不冲突】
from pwn import *
r = remote("node5.buuoj.cn", 26184)# 先利用p32()填满前13个元素
# 再利用p64()注入目标数据
offset = 13
payload = p32(17) * offset + p64(17)r.sendline(payload)
r.interactive()
4.获取flag:
system 函数中调用的是 "/bin/sh" ,仅让我们获取了控制权,所以我们需要搭配 ls(显示子目录)和 cat(读取)命令获取flag。