我是网络安全新手,我试图理解为什么以下代码容易受到堆溢出攻击......
struct data {
char name[128];
};
struct fp {
int (*fp)();
};
void printName() {
printf("Printing function...n");
}
int main(int argc, char **argv) {
struct data *d;
struct fp *f;
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
f->fp = printName;
read(stdin,d->name,256);
f->fp();
}
是因为read(stdin, d->name, 256)
因为它读取的128
超过struct data
中char name
分配的缓冲区大小吗?
任何帮助都会很棒
堆溢出攻击类似于缓冲区溢出攻击,不同之处在于攻击者不会覆盖堆栈中的值,而是践踏堆中的数据。
请注意,在代码中有两个动态分配的值:
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
因此,d
现在在堆中保存 128 字节内存块的地址,而f
保存 8 字节(假设是 64 位计算机)内存块的地址。从理论上讲,这两个地址可能彼此不靠近,但由于它们都相对较小,因此操作系统可能会分配一个较大的连续内存块,并为您提供彼此相邻的指针。
所以一旦你运行f->fp = printName;
,你的堆看起来像这样:
注意:每行宽 8 个字节
| |
+------------------------+
f -> | <Address of printName> |
+------------------------+
| ▲ |
| 11 more rows |
| not shown |
| |
d -> | <Uninitialized data> |
+------------------------+
| |
您对漏洞来源的初步评估是正确的。d
指向 128 字节的内存,但您允许用户将 256 字节写入该区域。C 没有边界检查机制,因此编译器非常乐意让您写过d
内存的边缘。如果f
就在d
旁边,你会从d
的边缘掉下来,掉进f
。现在,攻击者只需写入d
即可修改f
的内容。
为了利用此漏洞,攻击者通过对所有 256 字节的输入重复输入来提供他们写入d
的某些代码的地址。如果攻击者在地址0xbadc0de
存储了一些恶意代码,他们会向 stdin 输入 32 次0xbadc0de
(256 字节),以便堆被覆盖。
| 0xbadc0de |
+-------------+
f -> | 0xbadc0de |
+-------------+
| ... |
| 0xbadc0de |
| 0xbadc0de |
d -> | 0xbadc0de |
+-------------+
| |
然后,您的代码到达该行
f->fp();
这是使用存储在f
中的地址的函数调用。计算机转到内存位置f
并检索存储在那里的值,该值现在是攻击者恶意代码的地址。由于我们将其调用为函数,因此机器现在跳转到该地址并开始执行存储在那里的代码,现在您手上有一个可爱的任意代码执行攻击媒介。