hackme
1 | ! /bin/sh |
- smep,smap,kaslr
漏洞分析
1 | if ( cmd == 0x30002 ) // HACK_WRITE |
1 | else if ( cmd == 0x30003 ) // HACK_READ |
userdata.offset
是符号数,并且没有限制其必须为正数- 令
userdata.offset
为负数就可以在kheap_ptr[userdata.offset]
中向上溢出
入侵思路 - Tty_struct Attack
tty_struct attack
可以用于泄露 kernel_base,如果想用它来提权,则需要泄露 heap_addr
本题目的溢出可以轻松泄露空闲块的 next 指针,然后泄露出 heap_addr
1 | kcreate(2,buf,0x100); |
- 释放 “内存块2” 后,通过 “内存块3” 向上泄露内存快2的 next 指针
- 这源自于 slab 的一个机制:
1 | pwndbg> x/20xg 0xffffa1f380179400 |
- 空闲块都会有一个 next 指针,用于指向下一个内存块
- Slab 将内核中经常使用的对象放到高速缓存中,并且由系统保持为初始的可利用状态,因此上图中的
0x400
0x600
0x700
都是 free 状态
由于系统开启了 smap,内核需要使用 copy_from_user
才能访问用户态数据,于是我们利用 kcreate
把 fake_tty_operations
指针和 rop
指针保存到内核的堆里:
1 | kcreate(2,(char *)rop,0x100); |
对于 tty_struct attack
,还需要一个关键的 gadget,其目的是为了栈迁移(把 RAX 中的数据转移到 RSP 中):
- 最直接的 gadget 就是
mov rax, rsp
- 如果没有就以其他寄存器为中介
- 如果还是没有就只能用
push rax + pop rsp
剩下的操作就比较套路化了,可以当做是 tty_struct attack
的模板,注意控制一下 CR4 寄存器就好
完整 exp:
1 |
|
- ha1vk 大佬的 exp 中有个 gadget 我找不到,于是我找了另一个来替代它(我先把所有包含
pop rsp
的 gadget 重定位到一个文件中,然后再这个文件中搜索push rax
)
入侵思路 - Modprobe_path Attack
modprobe_path
是用于在 Linux
内核中添加可加载的内核模块,当我们在 Linux
内核中安装或卸载新模块时,就会执行 modprobe_path
指向的程序
因此我们需要劫持 modprobe_path
,劫持的方法就是利用 Slab 空闲块的 next 指针
1 | /home/pwn |
- 这里可以计算出
modprobe_path
的偏移
如果通过劫持 next 指针实现 WAA,就需要注意一个细节:
- 通过 fake next 指针申请的内存块是不合法的,并且会破坏 Slab 原本的次序
- 这会导致在后续的
system
中出现错误(因为system
也会利用 Slab 来分配内存) - 因此,我们在执行
system
前需要先kfree
一些内存块,使system
优先申请这些合法的内存块,从而避免报错
完整 exp:
1 |
|
入侵思路 - Cred Attack
cred attack
的思路特别简单,扫描到 cred
然后将其修改
扫描的过程中,通常有两种定位方法:
- 在
task_struct
中,通过*real_cred
和*cred
下方的字符串间接定位到*cred
,然后利用该指针获取cred
的地址
1 | const struct cred __rcu *real_cred; |
- 在
cred
中,通过以下8个字段都等于 UID 来直接定位cred
(UID 通常为 1000)
1 | kuid_t uid; /* real UID of the task */ |
定位到 cred
以后,就可以把 [uid] - [fsgid] 这8个字段全部置空,然后把置空后的数据返回给内核
在这个过程中,唯一的问题就是需要的空间太大,在用户态必须用 mmap 进行分配,而 copy_from_user
在检测到 mmap 分配空间后就会报错
其实 Double Fetch
中的 userfaultfd
机制早就解决了这个问题,因为我们不需要使用它进行条件竞争,于是我们在 handler
里进行 sleep
就好
完整 exp:
1 |
|