deadbeef安全实验

First Post:

Last Update:

安全项目deadbeef报告

开始项目之前

vs项目属性中一定要关掉安全检查和随机基址

image.png

image.png

以下操作在x86环境下进行

代码审计

项目源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/* bufbomb.c
* Bomb program that is solved using a buffer overflow attack
* program for CS:APP problem 3.38
* used for CS 202 HW 8 part 2
*
* compile using:
* gcc -g -O2 -Os -o bufbomb bufbomb.c
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest) {
int c;
int even = 1; /* Have read even number of digits */
int otherd = 0; /* Other hex digit of pair */
char *sp = dest;
while ((c = getchar()) != EOF && c != '\n') {
if (isxdigit(c)) {
int val;
if ('0' <= c && c <= '9')
val = c - '0';
else if ('A' <= c && c <= 'F')
val = c - 'A' + 10;
else
val = c - 'a' + 10;
if (even) {
otherd = val;
even = 0;
} else {
*sp++ = otherd * 16 + val;
even = 1;
}
}
}
*sp++ = '\0';
return dest;
}

int getbuf() {
char buf[16];
getxs(buf);
return 1;
}

void test() {
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}

int main() {
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position */
int offset = (((int) buf) & 0xFFF);
int *space = (int *) malloc(offset);
space = 0; /* So that don't get complaint of unused variable */
test();
return 0;
}

代码实现了定义一个类似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

image.png

我们只能通过缓冲区溢出的方法修改数据,所以我们需要思考如何利用溢出来修改返回地址和对val赋值,函数的返回地址存在寄存器栈顶ESP中,所以我们需要查看寄存器

仍从val = getbuf();处调试,查看寄存器,发现栈底EBP = 0019FED8 栈顶 ESP = 0019FED4,

接着调试发现栈顶先上升到0019FED0,栈底不变(上低地址,下高地址),再逐语句到getxs处,发现ESP变为0019FEBC,EBP变为0019FECC(此时查看内存地址可以发现其储存的值就是上次EBP的地址0019FED8)

接着在反汇编中调试 我们逐语句,在弹出的命令窗口随便填些数字,直到箭头指向getbuf定义中的ret处

image.png

此时查看ESP地址,我们知道此时该地址存放的就是getbuf的返回地址查看内存发现确实是00401186

ESP地址为0019FED0,EBP为0019FED8,如此我们便了解了栈的变换过程和函数返回地址的位置

接下来看val和buf的相对位置以便我们将char buf[16]的值进行扩写,覆盖掉栈中返回地址和修改val的值。

查看val的地址为 0x0019FED4 buf的地址为0019FEBC,它会存储16个字节

我们来画栈图

image.png

通过画出栈图我们清晰的认识到各个内存地址的相对位置,我们根据这个来写payload传参

首先要写32个十六进制数字 12345678123456781234567812345678

之后是缓冲区溢出的内容EBP是栈底 防止栈紊乱,EBP不能修改,我们仍然填入0019FED8,不过由于是小端存储,我们传参的时候要传入 D8 FE 19 00

再往后是ESP 也就是我们想要修改的返回地址处,此处改为 89 11 40 00

然后是val的值 我们填入deadbeef 也就是 ef be ad de

以上就是所有填入内容,整合到一起:12345678123456781234567812345678D8FE190089114000efbeadde

image.png

传入参数后发现得到答案

总结

  deadbeef项目要求一定的栈帧知识(有但不多),这个项目的代码量较少,操作也比较少,还是比较简单的

  做完之后思考🤔 

   为啥要填deadbeef 有如此神力 填个死牛肉有啥用?

    DEADBEEF是一个著名的十六进制魔数(Magic Number)。虽然看起来像"死牛肉"的英文,但它实际上是一个特殊的调试标记值0xDEADBEEF。这个数值最早由IBM的RS/6000系统引入,用于标记已分配但未初始化的内存区域,帮助开发人员在调试时快速识别内存状态。

在现代计算机系统中,DEADBEEF的使用非常广泛:

  • 操作系统应用
    • Mac OS(32位PowerPC)用它标记未初始化内存
    • Solaris系统将其用作内核空闲内存标记
  • 调试场景
    • 嵌入式系统中用作崩溃或死锁指示器
    • 内存调试工具中的常用标记值

这类特殊的十六进制数被称为”魔数”(Magic Number),它们在程序设计中扮演着重要角色。例如,Visual C++使用0xCC填充未初始化内存,这个值在Unicode中恰好对应中文字符”烫”,这就是为什么VC++调试时经常能看到一串”烫”字的原因。