在qemu上运行exp

在 qemu 上编写、构建和运行exp很麻烦,因为每次内核崩溃时,都必须重新开始。所以在本地编译好exp后再把它发送到 qemu会方便的多。
又由于每次都输入一大串命令很麻烦,所以把这些命令放在一个 shell 脚本transfer.sh里,每次执行这个脚本就好了。

#!/bin/sh
gcc exploit.c -o exploit # gcc编译exploit.c
mv exploit rootfs # 将编译结果exploit移动到rootfs(之前第一篇文章解压的那个目录)
cd rootfs; find . -print0 | cpio -o --null --format=newc --owner=root > ../debugfs.cpio
cd ../

qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "console=ttyS0 loglevel=3 oops=panic panic=-1 nopti nokaslr" \
    -no-reboot \
    -cpu qemu64 \
    -gdb tcp::12345 \
    -smp 1 \
    -monitor /dev/null \
    -initrd debugfs.cpio \
    -net nic,model=virtio \
    -net user

exploit.c 代码如下

#include <stdio.h>

int main() {
  puts("Hello, World!");
  return 0;
}

将这两个文件放在qemu目录下,如下所示:

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu$ ls
bzImage  exploit.c  rootfs  rootfs.cpio  rootfs_updated.cpio  run.sh  transfer.sh

然后执行transfer.sh以启动qemu,最后在qemu中执行exploit,但是得到这样的错误:

[ Holstein v1 (LK01) - Pawnyable ]
/ # ./exploit
sh: ./exploit: not found
/ #

为什么?

因为这个镜像中使用的库为 uClibc ,而不是通常的 libc。而编译exploit的环境使用的是GCC,也就是libc,所以动态链接失败,exploit也不起作用。
因此,在 qemu 上运行 exploit 时,要注意静态链接它们。那么,修改脚本:

gcc exploit.c -o exploit -static

再次重新运行,那么程序应该可以正常工作:

[ Holstein v1 (LK01) - Pawnyable ]
/ # ./exploit
Hello, World!
/ #

小结:

  1. 将exp源文件和transfer.sh(视情况可能要做一定改变)放在qemu目录下
  2. 执行脚本transfer.sh

在远程机器上运行exp

本次分发的环境设置为允许网络连接,所以如果想远程执行,可以在qemu上使用wget命令传输exp。

但是在CTF等一些环境中,网络是不可用的。在这种情况下,有必要使用 busybox 中的命令远程传输二进制文件。一般使用base64,但是用GCC构建的文件有几百K到几十M,比如前面编译的exploit的大小就是826K,传输很费时间。文件大小的增加是由于外部库 (libc) 函数的静态链接。

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu/rootfs$ ls -lh | grep exploit
-rwxrwxr-x 1 lzx lzx 826K Mar 11 00:26 exploit

如果想使用 GCC,并保持较小的大小,那么应该避免使用 libc 并使用系统调用(内联汇编)来自己定义读、写等,当然这是非常困难的。
所以许多 CTFer 使用名为 musl-gcc 的 C 编译器来进行内核利用。它可以从 https://www.musl-libc.org/ 下载源码,然后根据文档 https://git.musl-libc.org/cgit/musl/tree/INSTALL 进行构建并完成安装。

# 下载
lzx@ubuntu:~/tools$ wget https://musl.libc.org/releases/musl-1.2.3.tar.gz
--2023-03-11 00:59:06--  https://musl.libc.org/releases/musl-1.2.3.tar.gz
Resolving musl.libc.org (musl.libc.org)... 45.63.0.111, 2001:19f0:4009:4061:5400:ff:fe11:6da2
Connecting to musl.libc.org (musl.libc.org)|45.63.0.111|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1058642 (1.0M) [application/gzip]
Saving to: ‘musl-1.2.3.tar.gz’

musl-1.2.3.tar.gz                                       100%[=============================================================================================================================>]   1.01M   881KB/s    in 1.2s

2023-03-11 00:59:09 (881 KB/s) - ‘musl-1.2.3.tar.gz’ saved [1058642/1058642]

# 解压
lzx@ubuntu:~/tools$ tar -xzf musl-1.2.3.tar.gz
lzx@ubuntu:~/tools$ cd musl-1.2.3/

# 运行配置文件
lzx@ubuntu:~/tools/musl-1.2.3$ ./configure
......

# 编译
lzx@ubuntu:~/tools/musl-1.2.3$ make
......

# 安装
lzx@ubuntu:~/tools/musl-1.2.3$ sudo make install
......
./tools/install.sh -D obj/musl-gcc /usr/local/musl/bin/musl-gcc

安装完以后,就来使用了。使用musl-gcc编译exploit,然后移动到rootfs目录下(当然这一步可以省略,我只是为了保持统一):

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu$ /usr/local/musl/bin/musl-gcc exploit.c -o exploit -static

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu$ mv exploit rootfs

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu$ ls -lh rootfs | grep exploit
-rwxrwxr-x 1 lzx lzx  11K Mar 11 01:16 exploit

可以看到exploit的大小由826K变成了11K。如果想让它更小,可以用 strip 等删除调试符号。

有些头文件(Linux内核类型)不在musl-gcc中,所以需要设置include路径或者用gcc编译。在这种情况下,先通过gcc汇编器构建.S文件,然后使用musl-gcc进行编译,就可以在使用 gcc 函数的同时,又减小文件大小。

$ gcc -S sample.c -o sample.S
$ musl-gcc sample.S -o sample.elf

最后编写一个脚本来使用 base64 远程传输(通过 nc)二进制文件。每次CTF都可能会用到这个上传器,所以建议大家自己制作一个模板。

from ptrlib import *
import time
import base64
import os

def run(cmd):
    sock.sendlineafter("# ", cmd) # 这个需要根据情况改变,我这里的run.sh是以root启动qemu的,所以是#
    sock.recvline()

with open("./rootfs/exploit", "rb") as f: # 同样,这个exploit的路径也是要根据情况改变
    payload = bytes2str(base64.b64encode(f.read()))

#sock = Socket("HOST", PORT) # remote
sock = Process("./run.sh")

run('cd /tmp')

logger.info("Uploading...")
for i in range(0, len(payload), 512):
    print(f"Uploading... {i:x} / {len(payload):x}")
    run('echo "{}" >> b64exp'.format(payload[i:i+512]))
run('base64 -d b64exp > exploit')
run('rm b64exp')
run('chmod +x exploit')

sock.interactive()

然后执行这个脚本,要注意的一点是最好使用3.8及之后的python,不然 pip 安装完 ptrlib 之后,使用的时候仍可能报错。

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu$ python -V
Python 3.9.16 (feeb267ead3e6771d3f2f49b83e1894839f64fb7, Dec 29 2022, 14:23:21)
[PyPy 7.3.11 with GCC 10.2.1 20210130 (Red Hat 10.2.1-11)]

lzx@ubuntu:~/LKPWN/pawnyable/LK01/qemu$ python upload_exp.py 
[+] __init__: Successfully created new process (PID=83381)
[+] <module>: Uploading...
Uploading... 0 / 3780
Uploading... 200 / 3780
Uploading... 400 / 3780
Uploading... 600 / 3780
Uploading... 800 / 3780
Uploading... a00 / 3780
Uploading... c00 / 3780
Uploading... e00 / 3780
Uploading... 1000 / 3780
Uploading... 1200 / 3780
Uploading... 1400 / 3780
Uploading... 1600 / 3780
Uploading... 1800 / 3780
Uploading... 1a00 / 3780
Uploading... 1c00 / 3780
Uploading... 1e00 / 3780
Uploading... 2000 / 3780
Uploading... 2200 / 3780
Uploading... 2400 / 3780
Uploading... 2600 / 3780
Uploading... 2800 / 3780
Uploading... 2a00 / 3780
Uploading... 2c00 / 3780
Uploading... 2e00 / 3780
Uploading... 3000 / 3780
Uploading... 3200 / 3780
Uploading... 3400 / 3780
Uploading... 3600 / 3780
[ptrlib]$ /tmp # ls -lah
ls -lah
[ptrlib]$ total 44K    
drwxrwxrwt    2 root     root         100 Mar 10 18:28 .
drwxrwxr-x   17 root     dhcpcd       420 Mar 10 18:28 ..
-rwxr-xr-x    1 root     root       10.4K Mar 10 18:28 exploit
-rw-r--r--    1 root     root       27.9K Mar 10 18:28 messages
-rw-r--r--    1 root     root         149 Mar 10 18:28 resolv.conf
/tmp # ./exploit
.[ptrlib]$ /exploit
Hello, World!
/tmp # 

最后小结一下:

  1. 在本地用musl编译好exp(如果有需要,将编译好的文件移动到某个目录下)
/usr/local/musl/bin/musl-gcc exploit.c -o exploit -static
  1. 根据实际情况,稍微修改上传脚本,然后执行脚本,将exp发送到qemu中:
python upload_exp.py