deadbeef安全实验
Last Update:
安全项目deadbeef报告
开始项目之前
vs项目属性中一定要关掉安全检查和随机基址
以下操作在x86环境下进行
代码审计
项目源码
1 |
|
代码实现了定义一个类似gets(被列为不安全函数)的getxs函数来读入16字节16进制字符存入buf[]数组中,并且getxs会读入地址dest后且无长度限制,这就有缓冲区溢出的风险,我们运行代码,随便输入一串字符,发现都会返回0x1,这是因为最后会打印0x+val的值,而val被赋值为getbuf的返回值,这个返回值为1。
明确目的
我们想要程序输出0xdeadbeef,就要让val的值为deadbeef,并且跳过getbuf()对val的赋值操作
直接进行打印操作(需要查看反汇编),并且我们需要利用缓冲区溢出来完成这一操作。
具体实现
我们在val = getbuf()赋值操作处打断点调试
有了第一题的修改返回地址的基础 我们的目的应该很明明确, getbuf()函数返回地址原来应该在这句赋值语句,我们要让getbuf返回地址跳过赋值,直接进行printf操作,查看反汇编
getbuf()返回地址为00401186
我们想要它跳到赋值后的printf的第一个语句 00401189
我们只能通过缓冲区溢出的方法修改数据,所以我们需要思考如何利用溢出来修改返回地址和对val赋值,函数的返回地址存在寄存器栈顶ESP中,所以我们需要查看寄存器
仍从val = getbuf();处调试,查看寄存器,发现栈底EBP = 0019FED8 栈顶 ESP = 0019FED4,
接着调试发现栈顶先上升到0019FED0,栈底不变(上低地址,下高地址),再逐语句到getxs处,发现ESP变为0019FEBC,EBP变为0019FECC(此时查看内存地址可以发现其储存的值就是上次EBP的地址0019FED8)
接着在反汇编中调试 我们逐语句,在弹出的命令窗口随便填些数字,直到箭头指向getbuf定义中的ret处
此时查看ESP地址,我们知道此时该地址存放的就是getbuf的返回地址查看内存发现确实是00401186
ESP地址为0019FED0,EBP为0019FED8,如此我们便了解了栈的变换过程和函数返回地址的位置
接下来看val和buf的相对位置以便我们将char buf[16]的值进行扩写,覆盖掉栈中返回地址和修改val的值。
查看val的地址为 0x0019FED4 buf的地址为0019FEBC,它会存储16个字节
我们来画栈图
通过画出栈图我们清晰的认识到各个内存地址的相对位置,我们根据这个来写payload传参
首先要写32个十六进制数字 12345678123456781234567812345678
之后是缓冲区溢出的内容EBP是栈底 防止栈紊乱,EBP不能修改,我们仍然填入0019FED8,不过由于是小端存储,我们传参的时候要传入 D8 FE 19 00
再往后是ESP 也就是我们想要修改的返回地址处,此处改为 89 11 40 00
然后是val的值 我们填入deadbeef 也就是 ef be ad de
以上就是所有填入内容,整合到一起:12345678123456781234567812345678D8FE190089114000efbeadde
传入参数后发现得到答案
总结
deadbeef项目要求一定的栈帧知识(有但不多),这个项目的代码量较少,操作也比较少,还是比较简单的
做完之后思考🤔
为啥要填deadbeef 有如此神力 填个死牛肉有啥用?
DEADBEEF是一个著名的十六进制魔数(Magic Number)。虽然看起来像"死牛肉"的英文,但它实际上是一个特殊的调试标记值0xDEADBEEF。这个数值最早由IBM的RS/6000系统引入,用于标记已分配但未初始化的内存区域,帮助开发人员在调试时快速识别内存状态。
在现代计算机系统中,DEADBEEF的使用非常广泛:
- 操作系统应用
- Mac OS(32位PowerPC)用它标记未初始化内存
- Solaris系统将其用作内核空闲内存标记
- 调试场景
- 嵌入式系统中用作崩溃或死锁指示器
- 内存调试工具中的常用标记值
这类特殊的十六进制数被称为”魔数”(Magic Number),它们在程序设计中扮演着重要角色。例如,Visual C++使用0xCC填充未初始化内存,这个值在Unicode中恰好对应中文字符”烫”,这就是为什么VC++调试时经常能看到一串”烫”字的原因。