linux kernal pwn STARCTF 2019 hackme(一) 劫持modprobe_path

在这里插入图片描述
从用户态传入了0x20的数据,其数据放在了pool里面
在IDA里面看的话就是
在这里插入图片描述
就是他传入的四个QWORD 。
我们可以看得出来30001的时候就是通过v17来找到相关地址,然后kfree,再清零
所以其实就看得出来v17是index。

功能30002
v8是slab的地址
v20是从哪开始写
v9[1]里面就是size
所以就是往里面写
在这里插入图片描述

功能30003
上面是写进去
所以这个自然就是读出来
在这里插入图片描述
功能30000
读入一段
根据这一段申请slab
在这里插入图片描述

漏洞有两个
首先我们显而易见的在读写slab的时候对offset没有检查
如果我们的offset是负数
就可以任意地址读写。

第二个是条件竞争
是双核的,对pool的时候缺少锁。
如果我们在一个线程释放slab的时候另一个线程去对他进行读写,就可以触发uaf。

首先我们要充分熟悉一下slab机制

在Linux中,伙伴系统(buddy system)是以页为单位管理和分配内存。但是现实的需求却以字节为单位,假如我们需要申请20Bytes,总不能分配一页吧!那岂不是严重浪费内存。那么该如何分配呢?slab分配器就应运而生了,专为小内存分配而生。slab分配器分配内存以Byte为单位。但是slab分配器并没有脱离伙伴系统,而是基于伙伴系统分配的大内存进一步细分成小内存分配。

https://blog.youkuaiyun.com/xiaoqiaoq0/article/details/122051942

再熟悉一下userfaultfd机制

内存页的分配是创建时先分配页表但并不会实际分配物理页面。在读写发生时,由于物理页面不存在,触发缺页异常进入内核处理该缺页中断,再实际分配物理页面进行相应的读写。这种延迟分配的机制使得系统性能在一定程序上得到了提升。
userfaultfd机制允许在用户态处理缺页异常,这个特性使得它在内核漏洞利用中可以发挥较大的作用。一个典型的场景时mmap出来一块内存,使用userfaultfd监视该地址,如果发生缺页异常先进入内核,再从内核到用户态定义的缺页处理程序,此时可以在用户态暂停从而间接的实现暂停内核态代码的运行。这种特性使得该机制在竞争以及double fetch类的漏洞利用中能够发挥较大作用。

思路一:任意地址读写。
因为开了kaslr,所以首先当然是需要泄露地址

泄露地址又包括泄露堆地址,内核地址,模块地址。
首先我们知道slub的分配很类似于glibc 的fastbin,里面会有指针fd指向下一块空闲的块
所以我们还是通过已经释放掉的slab来拿到堆地址,就通过任意读写。

在这里插入图片描述
slub机制不会像glibc一样加个头,是多少就是多少。

size = 0x100;
ko_malloc(fd, 0, data_ptr, size);
ko_malloc(fd, 1, data_ptr, size);
ko_malloc(fd, 2, data_ptr, size);
ko_malloc(fd, 3, data_ptr, size);
ko_malloc(fd, 4, data_ptr, size);

ko_free(fd, 1);
ko_free(fd, 3);

idx = 4;
offset = -0x100;
size = 0x100;
ko_read(fd, idx, data_ptr, size, offset);
uint64_t kheap_ptr = *(uint64_t *)data_ptr;
printf("[+] leaking kheap address: 0x%lx\n", kheap_ptr);

然后考虑泄露内核基地址。
我们可以用任意读,去泄露0号slab之前的数据。然后找个基地址相关的地址,通过偏移获得内核地址。

那我咋知道0号slab在哪?
推荐两种方法:
1、可以在slab里写一个类似0xdeadbeef的字符串然后在gdb中search搜索。
2、可以在kmalloc下断点看他的返回值

我们实际调试一下
首先我们可能比较需要一个有符号的内核
当然没有也勉强可以但是不好调不好看
编译linux内核
内核不要只顾着用最新的
因为驱动必须用编译它的内核模块跑
自己整个内核的话驱动挂载补上的。
这个题的内核模块是4.20.13
在这里插入图片描述

上面说的直接划线
有点问题

根据实验
就算我们用了跟它本来版本一模一样的bzimage
这是因为linux内部有模块检测机制,导致驱动挂不上去,就不赘述了
那我们会想能不能bzimage用它给的 vmlinux用我们自己的 反正版本一样
也哒咩
用不了 里面的很多函数偏移啥的都不一样。
亲测亲测。
可以考虑编译内核的时候关闭CONFIG_MODVERSIONS选项来关闭模块检测。

但是我们一般似乎更通用的是
只能用一些现有的工具从他给的bzimage中尝试恢复一些符号
能恢复的不多
但是也能凑或用吧
网上找了个教程
在这里插入图片描述
看得到一个是我自己编的有符号的
一个是它恢复的 差八九十倍…
但是对于一些简单的驱动题的话也够用

调试方法不再赘述,补充一个小trick
qemu启动脚本中中-s是-gdb tcp::1234缩写的意思;-S是freeze CPU at startup,等待调试。

如果多次启动可能碰到端口占用

kill -9 `lsof -ti:2222`

用这个命令杀死就好。

在这里插入图片描述gdb远程链接

在这里插入图片描述

我当然用的是自己编译的内核 有符号表。
方便调试
在这里插入图片描述
有符号表之后虽然有些地方看着还是没符号的样子比较丑
但是下断点啥的也还是方便了很多
在这里插入图片描述

那么我们就先kmalloc了五个0x100的slab

在这里插入图片描述

这是第五次kmalloc的返回值

我们看一下周围
在这里插入图片描述可以很清晰看到堆的结构
以及我们可以看到
确实内核里的slab机制就是的slab就是不会加很多头。

然后释放掉slab1 3
看地址下图是slab1
看得出来里面第一个地址是9900 这显然是800后面空闲的slab
在这里插入图片描述

释放掉3之后
我们显然看到slab指向被释放的slab1
在这里插入图片描述

然后我们整上向上越界就能独处heap的地址。

然后我们试图读kernal base
我们试图向上找
看看上面有什么好的地址
在这里插入图片描述
这个228的地方找到一个地址
经过测试之后是kernal中的一个地址

那为啥不用上面那个呢?
那一堆链表看起来就像slab中的。

减偏移的话我们需要知道kernal的基地址
也可以通过在/proc/kallsyms里面查看
在这里插入图片描述

然后我们要泄露mod的地址。
mod的地址在mod_tree中有写。
所以我们需要先劫持slab那个链条
申请到mod_tree
然后泄露一下mod的地址

怎么知道mod_tree的地址呢?
在这里插入图片描述
泄露kernal_base之后通过偏移就能找的到。

然后要申请到mod_tree

在这里插入图片描述这个地址改成mod_tree + 0x40

两次申请申请到
在这里插入图片描述
申请0x40是因为0x40这里的地址会循环起来
我们申请这里的话这个地址又会被写进链条里
方便下次利用。

然后泄露这个地址出来
在这里插入图片描述
这个就是hackme模块的基地址
那这个地址哪来的?
我们可以

cat kallsyms | grep hackme

当然也可以
在这里插入图片描述
是的没错
这个模块地址就是IDA里面驱动text基地址

memset(mem,'A',0x100);
*((size_t *)mem) = (0x811000 + kernel_addr + 0x40); // mod_tree +0x40
write_to_kernel(fd,4,mem,0x100,-0x100);
alloc(fd,5,mem,0x100);
alloc(fd,6,mem,0x100);
read_from_kernel(fd,6,mem,0x40
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值