checksec发现是32位,没开栈保护和PIE
用IDA打开分析代码
可以看到这里fgets(v5,256,stdin)
stdin是标准输入流,也就是从控制台输入中读取
可以接收256个字节
但是后面有个strcat()向v5后面追加内容
所以此处会发生栈溢出
再往后的代码,打开了根目录下的password.txt文件
如果文件不存在程序会退出
随后从 stream(刚刚打开的文件) 中读取了64个字节到变量s中
最后,在标准输入流中读取64字节到s1
如果 s1 和 s 相同,就会执行flag()函数输出flag
我们分别查看v5的地址和s的地址
v5的地址是 0x160
v5的地址是0x60
两者刚好相差0x100个字节
也就是256
正好是v5可以装载的字节数
因此,我们只需要像程序中输入256个字节即可输出flag
为什么呢
我们观察程序
在分别读取v5,s之后,会使用puts函数输出v5
puts函数在输出字符串的时候遇到 0x00 的时候才会停止
假设我们的v5没有写满,只写了255个字节
是这样的情况
注意看第十行,最后一列数据
为什么是 0x00616161
因为程序是小端字节序
读取的时候从右往左读
但是不是 读成 16161600
而是61616100
这样可能还是不太直观
我们换一种
假设输出结果是
abcd
这里我们知道
a b c d 分别对应16进制的 61 62 63 64
那在32位的小端字节序的程序中
它们的存储是这样的
0x64636261
简单来说就是两位两位的从右往左读
我们回到主题
当puts在第10行遇到了 0x00 ,就会停止读取
但是程序允许我们写满
写满之后就会产生bug
变成
那这里没遇到 0x00 ,puts就会继续往下读取 s 的字节
直到读取到 0x00 之后,才会停止输出
所以我们只需要向程序中写入 256 个字节即可输出密码
这时候可能有的朋友会产生疑惑
我们先来说第二个问题(这里埋了一个小坑,在后面会讲)
我们看代码的先后顺序
程序是先写入了v5,然后追加
再然后写入s
因为在程序运行前
v5和s的位置会被预先确定好
当 v5 被写入
内存布局 是
追加字符串之后的内存布局是
写入s之后的内存布局是
也就是连带着 0x00 一并写入到它本来在的地方
程序输出的时候就会连带着变量s的字节一起输出,遇到 00 的时候停止
后面的部分也就不会输出了
现在再来说一下刚刚提到的小坑,逗号的16进制数值是 2c
但是在刚刚并没有看到
因为刚刚为了方便讲清楚,就先把那个东西忽略了
我们这里这次用gdb开始一次调试
在gdb中查看内存的布局
先设置断点在main函数
然后开始执行
到这里我们一直单步步过(n)
直到遇到让我们输入的时候
输入完一次n之后一直按回车即可,会重复上次的命令
到了这个位置开始让我们输出
我们可以看一下函数中的参数,也就是等会我们输入数据会被写入的地址
另开一个窗口
生成256个字符
复制(虚拟机中复制是 ctrl+shift+c)到gdb中输入
查看刚刚记录的地址
第一行 x/x 是调整我们输出地址的格式
像我在第53题的wp中使用的格式
如果是 0x00000061 会忽略成 0x61
在这题不够直观
我们可以看到,我们输入数据的最后一行最后一列不是0x61616161
而是0x00616161
这里的话是程序预留了一个字节用来装 0x00 ,让字符串能够正常停止
这里的漏洞产生点仍旧是在 strcat() 上
这里向 v5 的末尾追加字符串
我们继续运行到那个地方查看内存布局
运行完上面那些指令后,我们继续查看刚刚的内存部分
可以看到
2c写到了刚刚的00的部分
我们继续运行程序到fgets写入变量s的位置
运行完上面那条指令后
查看内存布局
可以看到我们的密码已经被写入
覆盖了刚刚追加的字符串
末尾的0a是由于当使用fgets读取文件时,每一行的末尾会包含一个\n(16进制的0a)
如果没有那行追加的字符串
这里仍然会是00,也就不会连带着输出变量s的内容
这也就解释了,为什么程序运行的时候会出现那个逗号了
知其然,然后知其所以然
如果有哪些地方出错
望师傅们指正
用户名 | 金币 | 积分 | 时间 | 理由 |
---|---|---|---|---|
Track-魔方 | 400.00 | 0 | 2023-09-06 14:02:32 | 技术深度 100 普适性 200 完整性 100 |
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.
Track-魔方
发表于 2023-8-17
本月奖金池已打空,文章于下个月进行打赏
评论列表
加载数据中...