我们准备创建 badfile 的内容。由于内容涉及二进制数据(例如,libc 函数的地址),我们可以使用 Python 进行构造。我们提供了以下代码框架,关键部分留待你填写。
#!/usr/bin/env python3
import sys
# 用非零值填充
content = bytearray(0xaa for i in range(300))
X = 0
sh_addr = 0x00000000 # "/bin/sh" 字符串的地址
content[X:X+4] = (sh_addr).to_bytes(4,byteorder='little')
Y = 0
system_addr = 0x00000000 # system() 函数的地址
content[Y:Y+4] = (system_addr).to_bytes(4,byteorder='little')
Z = 0
exit_addr = 0x00000000 # exit() 函数的地址
content[Z:Z+4] = (exit_addr).to_bytes(4,byteorder='little')
# 保存内容到文件
with open("badfile", "wb") as f:
f.write(content)
你需要找出三个地址和 X、 Y 和 Z 的值。如果你的值不正确,你的攻击可能不会成功。在你的报告中,你需要描述是如何决定 X、Y 和 Z 的值的,并展示你的推理过程。如果你使用试错方法,展示你的尝试过程。
如果你使用 gdb 来找出 X、Y 和 Z 的值,应当注意到 Ubuntu 20.04 中的 gdb 行为与 Ubuntu 16.04 中的略有不同。在我们设置断点在函数 bof 后,当 gdb 在 bof() 函数内部停止时,它会在 ebp 寄存器设置指向当前堆栈帧之前停止,所以如果我们在这里打印出 ebp 的值,我们将得到调用者的 ebp 值,而不是 bof 的 ebp 。我们需要输入 next 执行一些指令后,在 ebp 寄存器指向 bof() 函数的栈帧后停止。SEED 书(第1版)是基于Ubuntu 16.04,没有这个 next 步骤。
同时我们有以下两个问题:
1. 攻击变种1:exit() 函数真的必要吗?请尝试在 badfile 中不包括这个函数的地址,再次运行你的攻击,报告并解释你的观察结果。
2. 攻击变种2:在你的攻击成功后,将 retlib 的文件名更改为不同的名称,确保新文件名的长度不同。例如,你可以将其更改为 newretlib。重复攻击(不改变 badfile 的内容)。你的攻击会成功吗?如果没有成功,解释原因。