接下来我们复制exp到文件系统下,然后使用cpio命令重新打包:
unravel@unravel:~/pwn$ cp exp core/tmp/ unravel@unravel:~/pwn$ cd core/ unravel@unravel:~/pwn/core$ ls bin etc home init lib linuxrc proc rootfs.cpio sbin sys tmp usr unravel@unravel:~/pwn/core$ find . | cpio -o --format=newc > rootfs.cpio cpio: File ./rootfs.cpio grew, 3522560 new bytes not copied 14160 blocks unravel@unravel:~/pwn/core$ cp rootfs.cpio ..
下一步就可以重新运行内核了。执行boot.sh启动内核后,在刚才拷贝的/tmp目录下找到exp可执行程序:
/ $ ls -la /tmp/ total 864 drwxrwxr-x 2 ctf ctf 0 Dec 16 09:35 . drwxrwxr-x 13 ctf ctf 0 Dec 17 08:35 .. -rwxrwxr-x 1 ctf ctf 883168 Dec 17 08:30 exp
执行后可得到root权限,提权成功:
/ $ id uid=1000(ctf) gid=1000(ctf) groups=1000(ctf) / $ /tmp/exp [ 115.517513] device open [ 115.522342] device open [ 115.527241] alloc done [ 115.532132] device release [+] root now. / # id uid=0(root) gid=0(root) groups=1000(ctf) 调试
可以在boot.sh文件中添加-s参数来使用gdb调试,它默认端口1234。也可以指定端口号进行调试,只需要使用-gdb tcp:port即可。在启动的内核中使用lsmod查看加载的驱动基地址,得到0xffffffffc0000000,然后启动gdb,使用target remote指定调试IP和端口号进行调试,然后添加babydriver的符号信息,过程如下:
# 在QEMU运行的内核中运行如下命令 / $ lsmod babydriver 16384 0 - Live 0xffffffffc0000000 (OE) # 启动gdb,配置调试信息 gdb -q gef➤ target remote localhost:1234 Remote debugging using localhost:1234 gef➤ add-symbol-file pwn/core/lib/modules/4.4.72/babydriver.ko 0xffffffffc0000000 add symbol table from file "pwn/core/lib/modules/4.4.72/babydriver.ko" Reading symbols from pwn/core/lib/modules/4.4.72/babydriver.ko...
这里建议使用gef插件,pwndbg和peda调试内核总有一些玄学问题。如果gef报错context相关问题(如下图),在gdb中输入命令python set_arch()就可以查看调试上下文了:
我们之前在gdb中使用add-symbol-file命令加载了babydriver.ko的符号信息,并指定了加载基地址,在下断点的时候可以直接使用符号来打断点:
总结通过一道题认识了内核PWN的解题步骤,以及如何对内核进行调试。对于不知道用法的内核函数和结构体,可以在manned.org网站或者源码中查看。
参考资料CTF-WIKI链接:
Linux在线源码:
MannedOrg:https://manned.org/kmalloc.3
QEMU手册:https://www.qemu.org/docs/master/system/quickstart.html
UNICORN:https://www.unicorn-engine.org/docs/