
这是《【软件与系统安全】笔记与期末复习》系列中的一篇
2022-03-21 第四次课后半部分
mem-vul-part.pdf 内存破坏漏洞
回顾:
漏洞、攻击与危害
漏洞(vulnerability):可以被对缺陷具有利用能力的攻击者访问并利用的缺陷, 要素:
- 缺陷 (flaw)
- 攻击者能访问缺陷
- 攻击者有利用缺陷的能力
攻击(attack): 指攻击者尝试利用漏洞,例如 主动、被动、DoS…
危害(compromise):攻击成功则危害发生
在 C/C++ 程序中, 用 C 语言字符串编程时, 容易出现的错误
差一错误 是在计数时由于边界条件判断失误导致结果多了一或少了一的错误
gets:无边界的字符串复制
strcpy 和 strcat
“System V AMD64 ABI 调用惯例”2
缓冲区溢出可被利用于修改:
覆写返回指令指针

代码注入
将 ret 设置为被注入代码的起始地址, 被注入代码可以做任何事情,如下载和安装蠕虫
注入 Shell Code
shellcode: 注入的 payload 代码会执行起来一个 shell
在该 shell 中, 攻击者可以执行任何命令
该 shell 与当前进程具有相同的特权级
通常具有 root 权限的进程会被攻击
如何使用被注入的代码调用“execve”?
将函数“execve”的地址注入栈上
“execve”是一个 libc 中的函数, 动态链接到进程地址空间中
为了使得我们的可执行程序能够调用 libc 中的函数,可执行程序必须能找到被调用函数的地址
略
堆溢出: 在堆上开辟的缓冲区中的缓冲区溢出漏洞
溢出发生在堆缓冲区
Heap allocators (又称内存管理器)
Heap allocators 维护了元数据 (前块大小, 本块大小, previous 指针, next 指针)
堆溢出攻击会篡改元数据,并等待内存管理函数把被篡改的元数据写入目标地址

移除一个块

通过溢出 chunk2, 攻击者控制 chunk2 的 bk 和 fd 指针
假设攻击者想将 value 写入内存地址 addr
free():
变为
(*addr)=value第一个写操作实现了攻击者的目标, 实现了任意的内存写操作
因 write-what-where 操作由free()完成, 故需从free()向上寻找潜在溢出
当溢出与代码注入/代码重用结合时,
*(addr)=value”的addr是预先选择的函数指针、返回指令指针、影响控制流的变量地址等, value是恶意的跳转目标
定义: 程序在堆上释放内存, 然后引用该内存, 就好像该内存位置仍然合法:攻击者可以控制使用已释放的指针进行的数据写入
又称作悬挂指针使用,也是一个 write-what-where 漏洞
大多数有效的 use-after-free 攻击利用另一类型的数据
struct A {
void (*fnptr)(char *arg);
char *buf;
};
struct B {
int B1;
int B2;
char info[32];
};
// 释放A, 开辟B, 实际做了什么?
x = (struct A *)malloc(sizeof(struct A));
free(x);
y = (struct B *)malloc(sizeof(struct B));
// 如何利用此漏洞?
y->B1 = 0xDEADBEEF;
x->fnptr(x->buf);
Use-after-free 是类型混淆的一个实例
如何以较简单的方式禁止use-after-free?
设置所有被释放的堆空间为NULL
这时在使用被释放的堆空间时产生一个 null-pointer dereference
目前, 操作系统对 null-pointer deference 有内建的防护机制
复杂度: 需要设置所有别名指针(aliased pointers)为NULL
略
略
能够导致很灵活的恶意利用
int i; printf (“i = %d with address %08x\n", i, &i);
将格式化字符串的地址指针, i 和 &i 分别通过寄存器 rdi, rsi 和 rdx 传递, 并调用 printf
当程序运行至printf内部, 会在这些寄存器中查找参数;如果参数超过6个,还会在栈上查找参数
格式指示符字母的含义: printf - C++ Reference (cplusplus.com)


%n”能够将到“%n”位置为止已经由printf打印出的字节数写到一个我们选定的变量中
int i;
printf ("foobar%n\n", (int *) &i);
printf ("i = %d\n", i);
// i 的值最终为6
对于“format-string.c”程序, 如果用户输入“foobar%n”会怎样?
因此, 攻击者可以用任意内容更新任意内存。可否覆写一个函数指针并劫持控制流(且安装某些蠕虫代码)?
int main(int argc, char *argv[]) {
char buf[512];
fgets(buf, sizeof(buf), stdin); // no buffer overflow here
printf("The input is:");
printf(buf); // format string attacks here
return 0
}
攻击者可以


通过提供一个特殊的格式化字符串, 可以规避“%400s”的限制:
“%497d\x3c\xd3\xff\xbf”。创建一个497字符长的字符串,加上错误字符串(“ERR Wrong command: ”),超过了outbuf的长度4字节。虽然“user”字符串只允许 400字节,可以通过滥用格式化字符串参数扩展其长度。因为第二个sprintf不检查长度, 它可以用来突破outbuf的长度界限。此时我们写入了一个返回地址 (0xbfffd33c), 并可以以之前栈溢出的利用方式进行攻击
即限制攻击者控制格式化字符串的能力
%*”的格式化字符串printf(arg)”%n%s 和 sprintf 能够被用于构造栈内容披露攻击

