【BUUCTF】PWN-刷题记录-ciscn_2019_n_1
题目信息

解题步骤

1 | [*] '/home/kali/Desktop/buuctf/ciscn_2019_n_1' |
用 IDA 打开二进制文件,main 函数非常简单:

关键逻辑在 func():

v1[44]:输入缓冲区,位于[rbp-0x30]v2:float类型,位于[rbp-0x4],初始值0.0- 使用
gets(v1)→ 栈溢出漏洞 - 条件:
v2 == 11.28125才能执行 system("cat /flag")
直觉告诉我:不能直接 ROP 到 system,因为 v2 必须等于 11.28125。
但 v2 就在栈上,紧挨着 v1!
栈布局:
1 | +------------------+ |
也就是说,v1 和 v2 之间有 0x30 - 0x4 = 0x2c = 44 字节,但 v1 是 44 字节,v2 在 [rbp-4],所以:v1 的最后 4 字节可以直接覆盖 v2 的内存!
浮点数陷阱:11.28125 的二进制表示
v2 是 float,我们需要知道 11.28125 在内存中长什么样。
用 Python 计算:
1 | import struct |

所以 11.28125 的内存表示是:\x00\x80\x34\x41(小端)
我们只需要在 v1 的最后 4 字节写入 \x00\x80\x34\x41,就能让 v2 == 11.28125
从 v1[0] 到 return address 的距离:
v1:44 字节([rbp-0x30]~[rbp-0x1])v2:4 字节([rbp-0x4])saved rbp:8 字节([rbp+0])- 返回地址:
[rbp+8]
所以:
- 从
v1[0]到saved rbp:44 + 4 = 48 字节 - 到返回地址:48 + 8 = 56 字节
填充 56 字节后,接下来 8 字节就是返回地址。
目标:覆盖 v2 为 11.28125,并跳转到 system("cat /flag")
但 system("cat /flag") 已经在程序里了!我们只需要让函数正常返回,条件满足就会自动执行。
所以 payload 只需要:
- 前 44 字节:任意填充
- 接下来 4 字节:覆盖
v2为11.28125的二进制 - 8 字节:覆盖
saved rbp(可任意) - 8 字节:覆盖返回地址为
func函数末尾,让if条件成立后继续执行
但更简单的方式是:让函数返回后,直接跳回 func 中 if 之后的代码。
然而,最直接的方式是:不改变控制流,只让 v2 被覆盖,然后函数自然执行 system
但 gets 后函数就结束了,我们无法控制 return 到哪。
等等——我们不需要 ROP!
只要 v2 被覆盖为 11.28125,gets 返回后,if 条件成立,直接执行 system("cat /flag")!
所以:
我们甚至不需要控制返回地址!只需要在
v1的最后 4 字节写入11.28125的二进制即可!
但 gets 会读取到 \x00 吗?
会!gets 读取直到 \n,不会在 \x00 停止。
1 | from pwn import * |

1 | from pwn import * |











