题目中上,但各种乱象毁了这个比赛。
概览
程序是静态链接的,利用缓解机制只启用了NX,程序只是一个简单的读取输入并返回。
1 2 3 4 5 6
| $ checksec NotFormat Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE
|
1 2 3 4
| $ ./NotFormat Have fun! test test
|
漏洞
程序main函数很短,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void main() { double v8; double v9; char buf[264]; __int64 v11; v11 = *MK_FP(__FS__, 40LL); setvbuf((int *)off_6CB740, 0LL, 2, 0LL); puts("Have fun!"); read_line(buf); printf(buf, 0LL); exit(0LL); }
|
存在一个很明显的格式化字符串漏洞。
利用
因为格式化字符串使用之后立即就调用exit退出了,所以目标是直接通过一次printf劫持控制流。
控制RIP
我用到的是修改FILE *stdout结构的vtable到我们构造的位置。由于没有开启PIE,所以这些内容的位置都可以确定。
栈迁移
当控制RIP之后,程序的栈的位置与我们输入内容的位置差距很大,但是正好找到这个gadget可以迁移到我们输入的buffer。
ret之后正好会跳到0x0457fc1位置,实现栈迁移。
1
| 0x457fc1 : add rsp, 0x2120 ; mov eax, r12d ; pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; re
|
之后可以再次调用read,并再次迁移到一个大buffer。最后调用execve("/bin/sh", 0, 0)
uctf2017_notformat.py
babydriver
概览
这是一个简单的驱动题,只开启了SMEP(貌似也用不到……)。
提供了/dev/babydriver
设备:
- read/write操作可以将buf从
babydev_struct.device_buf
读取或写入,长度限制为babydev_struct.device_buf_len
。
- ioctl的cmd为0x10001时可以对
babydev_struct.device_buf
重新分配。
- close时会将
babydev_struct.device_buf
释放。
漏洞
一个最明显的漏洞是babydev_struct
是一个全局变量,所有的文件描述符都共享一个babydev_struct
,当一个文件描述符被调用close时,babydev_struct.device_buf
会被释放。当其他未关闭的文件描述符调用时,会触发UAF,可以对一段内核空间进行读写。
利用
当device_buf被释放掉后,这个指针成为悬空指针,扔可以进行读写,这时候希望可以有一个结构被申请然后占到原来的这个位置。最直观的想法就是如果能有cred结构直接占上来,然后就可以对cred内容修改,达到获取root权限的目的。
cred结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| struct cred { atomic_t usage; #ifdef CONFIG_DEBUG_CREDENTIALS atomic_t subscribers; void *put_addr; unsigned magic; #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif kuid_t uid; kgid_t gid; kuid_t suid; kgid_t sgid; kuid_t euid; kgid_t egid; kuid_t fsuid; kgid_t fsgid; unsigned securebits; kernel_cap_t cap_inheritable; kernel_cap_t cap_permitted; kernel_cap_t cap_effective; kernel_cap_t cap_bset; kernel_cap_t cap_ambient; #ifdef CONFIG_KEYS unsigned char jit_keyring; * keys to */ struct key __rcu *session_keyring; struct key *process_keyring; struct key *thread_keyring; struct key *request_key_auth; #endif #ifdef CONFIG_SECURITY void *security; #endif struct user_struct *user; struct user_namespace *user_ns; struct group_info *group_info; struct rcu_head rcu; };
|
它的大小是0xa8。于是利用的步骤如下:
- open两个fd,fd1及fd2。通过ioctl设置device_buf为0xa8大小。
- close(fd1),device_buf指针被释放。
- 现在可以通过fd2继续操作device_buf指向的内容。
- 通过fork一堆进程试图在申请cred结构的时候占到device_buf指向的位置。
- 修改占上的cred,获取root。
做的时候粗暴地试试结果发现就可以占上了……然后粗暴的把ctf用户的所有的id为0x3e8的都改成了0。
uctf2017_babydriver.py
P.S. 出题时希望可以把网卡驱动带进去,不然传程序进去也挺麻烦……