第五届强网杯线上赛冠军队 WriteUp – Pwn 篇 – 作者:北京长亭科技有限公司

本篇是强网杯线上赛WriteUp第二篇,将对其中的Pwn题进行解题思路的分析,欢迎大家继续关注。

另外,长亭科技现有多个岗位正在招聘,安全服务中心(没错,就是会出去参加各种CTF大赛的团队)、产品研发中心、技术架构与解决方案中心等众多热门岗位,欢迎投递简历,加入我们哦~
image

EzCloud

题目注册了一些路由,能未登录访问的除了/login,/logout外只有/notepad,而漏洞就发生在/notepad里唯一一处使用malloc的地方。程序中初始化字符串(地址为0x9292,这里命名为create_string)的函数存在两个为初始化,一是若传入的value为空时,函数不做任何工作直接退出,二是create_string中的malloc申请内存后没有初始化。

使用create_string中的第二个未初始化可以leak出heap地址,虽然低位会被覆写至少一位,但根据linux内存按页对齐的性质仍然可以得到完整的heap地址。使用create_string的第一个未初始化配合/notepad中的未初始化,可以得到一个没有初始化的string结构体,通过堆布局控制该结构体,然后使用edit note的功能可以实现任意地址写,配合之前leak出的heap地址,写session的第一个_DWORD(即authed字段)即可以调用/flag获得flag。

image

EzQtest

dma触发write io导致的数组越界问题,进行利用前需要对pci进行初始化。利用直接改mmio ops就可以getshell

image

notebook

分析

题目给了一个内核模块,实现了一个菜单题。虚拟机的 init 脚本里放了一份内核模块的加载地址在 /tmp/moduleaddr,可惜并没有什么用。

程序逻辑比较简单,并且没有 strip,不再赘述。

这个程序存在比较多的 bug ,比如:

  1. noteedit 和 noteadd 都修改了 note 数据,但却只 acquire 了一个读写锁的读侧。并且还非常刻意的在一些地方塞了 copy_from_user。

  2. mynote_read 和 mynote_write 都读了 note 数据,却没有 acquire 锁。

这导致(仅描述我认为最好用的一个利用路径):

  • noteedit 里,先修改了 note 的 size,把 note 数据给 krealloc 了,然后在把 realloc 出的新指针设置到 note 上之前运行了 copy_from_user,我们可以让它从一个 userfaultfd 代管的地方 copy,从而把这个线程卡死在这里,再也不会执行后面的代码。让 note 上还保留着一个已经 free 掉的数据指针。

  • noteadd 里,先修改了 note 的 size,在进行 alloc 和赋值到 note 结构体上之前先运行了 copy_from_user,同上可以让它卡在这里,相当于这里可以任意修改 note 的 size,但有一个限制是不能超过 0x60。

  • 虽然 mynote_read 和 mynote_write 里有 check_object_size 避免我们通过把 size 改大的方法简单的溢出,但利用 noteedit,可以制造一个 UAF。此时会挂在这个 check_object_size 的检查上。

  • 但是再利用 noteadd 把对象的 size 改成小于 realloc 前的 size 的值就可以通过 check 啦!

  • 由于 noteedit 和 noteadd 都只拿了读锁,只要小心的避免触发写锁(只有 notedel 里有),它们是可以并发的。

利用

由于 noteedit 里可以把管理的 note 给 krealloc 成任意长度,我们相当于有一个对任意长度的数据的任意多次读写完全控制的 UAF,但只能控制前 0x60 字节(足够)。我们制造 kalloc-1024 这个 slab 里的 UAF,再用 openpty() 创建 tty 对象把它们占回来,利用 mynote_read 读取 tty struct,即可 leak 处指向内核 text 段的指针,解决 kASLR。接下来,修改 tty 对象上 + 0x18 字节处的函数指针表,即可控制 rip。

利用代码编写的时候使用了 gift 功能可以告诉我们 note 数据指针的特性,利用 note 在堆上写了一个 tty_operations 表,但完全可以不用,tty struct 里有可以推断出自己的地址的指针(在 +0x50 处),可以直接把对应的函数指针塞在 tty struct 上的某位置。

控制 rip 之后,下一步就是绕过 SMEP 和 SMAP 了,这里介绍一种在完全控制了 tty 对象的情况下非常好用的 trick,完全不用 ROP,非常简单,且非常稳定(我们的 exploit 在利用成功和可以正常退出程序,甚至关机都不会触发 kernel panic)。

内核中有这样的一个函数:
image

其编译后大概长这样:
image

该函数位于 workqueue 机制的实现中,只要是开启了多核支持的内核 (CONFIG_SMP)都会包含这个函数的代码。不难注意到,这个函数非常好用,只要能控制第一个参数指向的内存,即可实现带一个任意参数调用任意函数,并把返回值存回第一个参数指向的内存的功能,且该 “gadget” 能干净的返回,执行的过程中完全不用管 SMAP、SMEP 的事情。由于内核中大量的 read / write / ioctl 之类的实现的第一个参数也都恰好是对应的对象本身,可谓是非常的适合这种场景了。考虑到我们提权需要做的事情只是 commit_creds(prepare_kernel_cred(0)),完全可以用两次上述的函数调用原语实现。(如果还需要禁用 SELinux 之类的,再找一个任意地址写 0 的 gadget 即可,很容易找)

最终利用代码如下,编译命令为 gcc -osploit -pthread -static -Os sploit.c -lutil:
image
[1] sched_setaffinity(0, 1, &cpu_mask) 绑核是为了增加占坑的稳定性,非必要。

dhd

一个 PHP 1day 题目, 预期解应该是使用这个漏洞https://bugs.php.net/bug.php?id=79818,但是这里我们使用了另外一个 1day, 通过绕过限制函数拿到了 flag。

image
注: 我们其实已经可以通过 SplFileObject 函数读flag了。

easywarm

逆向分析

题目实现的功能大概有:

  • 当程序带 666 参数启动时,进入所谓的 admin mode,实现了一个一次任意地址写最多 144 字节的任意不含换行符的内容,然后调用 exit() 的功能。

  • 当程序带 000 参数启动时,会保存 envp 和 argv 的指针到 .bss 上,并开始一个菜单形式的迷宫游戏。

  • 当程序收到 SIGFPE 信号的时候,会使用保存的 envp 和 argv 换为 666 参数 execve 自身,即重新运行程序并进入 admin mode 的逻辑。

  • 迷宫游戏可以指定大小和复杂度,大小最大 32,复杂度为 1 到 5。

  • 游戏目的为控制

来源:freebuf.com 2021-06-24 11:16:04 by: 北京长亭科技有限公司

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论