heap null byte off-by-one
分析环境:
- Linux parrot 4.17.0-parrot17-amd64 #1 SMP Parrot 4.17.17-1parrot17 (2018-08-27) x86_64 GNU/Linux
- pwntools:3.12.1
- gdb + gef, IDA
- libc:2.27
checksec:1
2
3
4
5
6
7
8$checksec ./b00ks
Arch: amd64-64-little
Canary No
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Fortify No
保护全开,ret2plt,覆写 got table 就不要想了
定位漏洞点
问题出在 book name read() 控制上,也就是 offset+0x9f5 这个函数上:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19signed __int64 __fastcall sub_9F5(_BYTE *a1, int a2){
int i; // [rsp+14h] [rbp-Ch]
_BYTE *buf; // [rsp+18h] [rbp-8h]
if ( a2 <= 0 )
return 0LL;
buf = a1;
for ( i = 0; ; ++i ){
if ( (unsigned int)read(0, buf, 1uLL) != 1 )
return 1LL;
if ( *buf == 10 )
break;
++buf;
if ( i == a2 )
break;
}
*buf = 0;
return 0LL;
}
a2 是输入的 author name size - 1;a1 是 malloc(a2+1) 返回的指针.
从这段 for 循环中可以看出,实际可以 read() a2+1 次,那么 ‘\x00’ 自然也会被写入到内存中了。这是一种很典型的栅栏错误。
author name 位于 bss 段,其后是一个 book struct 指针表
通过 4 选项,可以知道 book detail:1
2
3
4
5
6struct book{
int id;
char *name;
char *description;
int size;
};
分析过程
在以下地方下断比较好分析:
- b *0x555555554af1,获取功能号前
- b *0x123b,0x555555554240,选项前一行
- b *0x9f5,0x0x55555555549f5,读取字符串函数
关键点:如果要堆分配的内存很大的话,那么会使用 mmap 来重新申请内存空间,并且这块区域邻近 bss 段。offset == mmap ret addr - libc base addr,那么就可以推算出 libc base addr。
关于这个点,我一直很疑惑,通过实际代码在不同的 Linux 发行版上测试,大部分情况下这个偏移是固定,但在 wsl debian 这个偏移就不固定了。
linux mmap ASLR 失效漏洞
这里介绍一个 Linux mmap ASLR 失效的漏洞,CVE-2016-3672。
影响范围:Linux kernel <= 4.5.2
验证方法:
- 设置栈空间为不限制大小ulimit -s unlimited
- 使用ldd看动态库加载的地址是否发生变化
1 | $ uname -r |
利用思路
泄露 book1 结构体堆指针地址。具体步骤
- author name 输入 32 个字符
- 选择 1.create book1
选择 4.print book1 struct,”Author: “ field 可泄露出 book1 结构体堆指针
为什么可以泄露 book1 结构体指针呢,这是因为32个字符连着地址,没有结束符 “\x00”
- 计算得到要伪造的 fake book addr及偏移量
- 创建 book2 with 0x21000 的 book2 desc 和 “/bin/sh” 的 book2 name
- 构造 fake book1。编辑 book1 ,实际就是对 book1 desc 写入数据,,fake book1 name 存储着 *book2 desc addr
- leak mmap 分配的内存地址。print fake book,泄露 book2 desc addr,由此推算出 libc base addr ,然后再计算出 system addr , /bin/sh , __free_hook 的地址
- 覆写 free hook。分两步,先修改 fake book desc 存储的指针为 free book 的地址;然后修改 book2 desc ,改写 __free_hook 存储的地址为 system 地址
- 执行 shell。delete book2, free book2 name 的时候执行的就是 system(book2 name)
内存管理器 Hook 机制
在 Linux 下,内存管理器一般通过 HOOK 来实现自定义的malloc函数,具体就是通过覆盖 hook 函数指针来实现。glibc 提供四个全局函数 hook 指针
1 | __malloc_hook |
所以像 jemalloc 或者 tcmalloc 等堆管理器通过覆盖这些 hook 函数指针使程序调用到它们自定义的malloc。当然在漏洞利用的时候也常被用来覆写为 system() 等地址。
所以在这里,既然不可以覆写 got 表,那么就覆写 __free_hook 函数地址。
脚本及 getshell
以下脚本在 ASLR == 0 或 1 时可 getshell,在开启 ASLR == 2 时一定几率可 getshell
1 | #-* encoding:utf-8 *- |