StringIPC 复现
1 | qemu-system-x86_64 \ |
- smep,smap(这是我自己加的,原题没有)
1 | ! /bin/sh |
漏洞分析
和 qwb2018-solid_core
的漏洞点一样:
1 | buf_size = channel->buf_size; |
- CSAW_SHRINK_CHANNEL 会导致
ch2
负数溢出为“-1” krealloc(channel->data, 0, 0x24000C0LL)
返回“0”,使后面的channel->data = 0
,而channel->buf_size
非常大
1 | channel->data = data; |
- 进而绕过后面的检查:
1 | if ( channel_from.size + index_write > channel_write->buf_size ) |
入侵思路
在 qwb2018-solid_core
中,作者禁用了 “cred attack” 和 “vdso attack” 这两种方法,这里就来试一试
首先 WAA,RAA 的模板如下:
1 | void RAA(int fd, int channel_id, void *read_buff, uint64_t addr, uint32_t len) |
1 | void WAA(int fd, int channel_id, void* write_buff, uint64_t addr, uint32_t len) |
- PS:由于驱动使用的
strncpy_from_user
会被 “\x00” 截断,所以 WAA 最好单字节多次输入
Cred Attack:
内核结构体 task_struct
用于对进程/线程的所有的相关的信息进行维护,并进行管理:
- 其中有个很重要的条目就是
*cred
指针,内核会根据cred
结构体的内容来判断一个进程拥有的权限(如果cred
结构体成员中的 uid-fsgid 都为 0,那一般就会认为进程具有 root 权限) - 想要定位
task_struct
结构体中的cred
指针,需要用到task_struct
中的另一个条目comm[TASK_COMM_LEN]
:
1 | /* Objective and real subjective task credentials (COW): */ |
comm
字符数组就在*cred
相邻的下方,里面存放的这个字符串表示线程的名字(可以唯一确定),其内容可以通过 linux 的prctl(PR_SET_NAME,name)
来设置指定的值*real_cred
和*cred
的值相同,可以用于判断是否找到*cred
- 如果程序拥有局部 RAA,就可以通过扫描
comm
来找到*cred
最后还需要确定一下扫描的范围:
1 | 0xffffffffffffffff ---+-----------+---------------------------------------------- |
1 | 0x0000800000000000 ----+-----------+---------------------------------------------- |
- 直接映射区:0xffff880000000000 ~ 0xffffc80000000000(使用 kmalloc,分配的内存物理地址是连续的,虚拟地址也是连续的)
- 动态映射区:0xffffc90000000000 ~ 0xffffe90000000000(使用 vmalloc,分配的内存物理地址是不连续的,虚拟地址是连续的)
- PS:
cred
使用直接映射区
完整 exp 如下:
1 |
|
VDSO Attack:(Ret2dir 的一种)
VDSO 是内核为了减少内核与用户空间频繁切换,提高系统调用效率而提出的机制,支持的系统调用有4个:
- gettimeofday():把时间包装为一个结构体返回,包括秒,微妙,时区等信息
- time():获取当前的系统时间,返回一个大整数
- getcpu():获取CPU信息
- clock_gettime():用于计算精度和纳秒
入侵的思路很简单,就是利用 WAA 把 vdso 中用于替代系统调用的函数劫持为 shellcode,然后调用这些函数,获取 VDSO 基地址有如下步骤:
- 在高版本的 glibc 中,读取 ELF 辅助向量,计算
gettimeofday
字符串的偏移,用于在后续的爆破中判断是否找到 VDSO 基地址
1 | int get_gettimeofday_str_offset() { |
- 爆破获得 VDSO 地址,VDSO 是按页对齐的,且映射到空间的是个ELF文件
1 | int offset = get_gettimeofday_str_offset(); |
- PS:能劫持 vdso 的核心就是 vdso 在内核状态下是可写的,高版本内核就不可写了
如果爆破出了 VDSO 的内核地址,就使用 GDB 把 VDSO 给 dump 下来,然后拖入 IDA 寻找函数 gettimeofday
的偏移(在 get_gettimeofday_str_offset
中查找的是 gettimeofday
字符串偏移)
1 | vdso_addr in kernel: 0xffff880001e04000 |
1 | pwndbg> dump memory ./vdso.so 0xffff880001e04000 0xffff880001e05000 |
写入的 Shellcode 是一个反弹 shell,它将 root shell 反弹到本地端口3333,我们只需 nc 本地端口3333即可
- 如果有 root 权限的程序,调用我们的 shellcode,那么我们的 shellcode 也是以 root 权限执行
- 在 Linux 中,crontab 是带有 root 权限的,并且它会不断的调用 vdso 里的 gettimeofday 函数
- 在 qemu 里,使用了一个程序来模拟(本题目是
/sbin/init
)
完整 exp 如下:
1 |
|
小结:
尝试了一下 “cred attack” 和 “vdso attack”
本来还想试试 “HijackPrctl”,但在 qwb2018-solid_core
中已经复现过了