题目中上,但各种乱象毁了这个比赛。
NotFormat
概览
程序是静态链接的,利用缓解机制只启用了NX,程序只是一个简单的读取输入并返回。
1 | checksec NotFormat |
1 | ./NotFormat |
漏洞
程序main函数很短,如下
1 | void main() |
存在一个很明显的格式化字符串漏洞。
利用
因为格式化字符串使用之后立即就调用exit退出了,所以目标是直接通过一次printf劫持控制流。
控制RIP
我用到的是修改FILE *stdout结构的vtable到我们构造的位置。由于没有开启PIE,所以这些内容的位置都可以确定。
栈迁移
当控制RIP之后,程序的栈的位置与我们输入内容的位置差距很大,但是正好找到这个gadget可以迁移到我们输入的buffer。
1 | 0x43f17d : ret 0x6b8 |
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)
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 | struct cred { |
它的大小是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。
P.S. 出题时希望可以把网卡驱动带进去,不然传程序进去也挺麻烦……