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

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

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

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

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/zb3/tiandy-research
嵌入式系统安全密码破解权限提升漏洞分析漏洞利用渗透测试身份验证论文与研究红队固件分析
GitHubzb3/tiandy-research

tiandy-research

30296年前Kitploit 审核通过

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

该仓库包含了我2020年8月对Tiandy的IPC/NVR固件进行的研究结果。我发现了两处漏洞,可被用来远程恢复管理员密码并获取设备的root访问权限。

查看仓库

tiandy-research

本仓库包含我于 2020 年 8 月对 Tiandy 的 IPC/NVR 固件(这些设备也以 OMNY 品牌销售)的研究结果。这项“研究”并不详尽,但我确实找到了多种远程恢复管理员密码、启用 telnet 以及修改 root 密码的方法。

很难确切地说哪些版本受影响,因为我们只能下载到最近的版本。以下这些可下载版本均受影响:

root@kitploit:~
DVRS_V9.12.7.20200422
DVRS_V11.7.4.20200721
NVSS_V13.6.1.20200723
NVSS_V22.1.0.20200722

不仅不同设备有不同的分支,而且某些组件是单独版本化和升级的,例如 Web API。我发现在 Web API 中存在一个认证绕过漏洞,该漏洞仅对 2019 年中期之后发布的版本有效,与固件版本号无关。如果你恰好了解受影响的版本,我将非常感谢你的帮助。

我在这里进行完全披露,但这是合理的。厂商补丁(考虑到他们不作回应,这不太可能)并不会让问题消失,尤其是当联网设备中几乎没有设备运行最新固件(甚至远远不是)时。真正的漏洞在于这些设备暴露在互联网上。而这是最终用户需要修复的问题,而不是 Tiandy。

作为额外内容,我还附带了固件解包工具,以及一些关于如何通过 RTSP/RTMP 访问视频流的信息(祝你在手册中找到这些信息好运)。

这里有什么

首先介绍脚本:

  • 密码恢复
  • 获取 root

然后我会简要解释这些脚本的作用及其原因。我不会重复代码,但会提供足够的上下文,以便你理解代码:

  • 概述
  • 漏洞
  • 密码恢复之外

最后,这部分相对技术性较强:

  • 解包固件
  • 在互联网上寻找 Tiandy 设备
  • 额外内容:RTSP 和 RTMP URL

密码恢复

你需要安装带 PyCrypto 的 Python 3。

首先,尝试使用 recover.py。这要求端口 3001 可访问:

root@kitploit:~
python3 recover.py [HOST]

如果一切顺利,将会打印管理员凭证。

如果该端口不可访问,Web 方式或许可行。这需要 url:

root@kitploit:~
python3 cgi_recover.py http://123.45.67.89
python3 cgi_recover.py https://123.45.67.89

如果上述方法都不行,请检查 telnet 是否已启用。如果已启用,你可以直接 root 设备,只需破解这个哈希:

root@kitploit:~
support:$1$$AErA9BQgLjrxTJB1748k71:501:501:Linux User,,,:/home/support:/bin/sh

(如果你真的破解了它,请务必提交一个 PR :D)

获取 root

旧固件

在较旧的 V7 NVR 固件中,你可以直接运行命令:

root@kitploit:~
python3 ftpupdate.py [host] [adminpass] '[cmd]'

但它没有任何输出。为了方便,我加入了以下简写:

root@kitploit:~
python3 ftpupdate.py [host] [adminpass] adduser [username] [password]

这将添加另一个 uid 为 0 的用户。

较新的固件

首先,使用以下命令启用 telnet:

root@kitploit:~
python3 telnet.py [host] [adminpw]

或者对于较新的设备:

root@kitploit:~
python3 cgi_recover.py [host] telnet

然后你可以覆盖 /etc/passwd(我假设你知道其原理)。首先尝试 filetransport.py(适用于 NVR):

root@kitploit:~
python3 filetransport.py [host] [adminpass] put /etc/passwd <[source_file]

此命令没有反馈,你需要尝试登录来验证……

对于 filetransport.py 不起作用的 IPC 型号,请尝试 upgrade_rw.py:

root@kitploit:~
python3 upgrade_rw.py [url] [adminpass] /etc/passwd <[source_file]

(这同样没有反馈)

最后,对于更新的设备,也可以通过 Web API 完成:

root@kitploit:~
python3 cgi_recover.py [url] write /etc/passwd <[source_file]

如果上述方法都不起作用(检查你是否能登录),请重试所有这些方法,但改为覆盖 /config/etc/passwd。在某些固件版本中,/etc/passwd 是指向该文件的符号链接。最后,你还可以尝试覆盖 /tdfs/etc/passwd,但之后可能需要重启设备,因此请使用以下命令重启:

root@kitploit:~
python3 reboot.py [host] [adminpass]

概述

旧的 V7(IPC 和 NVR)固件似乎不受影响,但如果你拥有管理员密码,对于 NVR 存在一个经过认证的 RCE(ftpupdate.py),而对于 IPC,upgrade-rw.py 脚本可被用来覆盖 /etc/passwd。

较新版本的 NVR 固件(V9 和 V11)带有一个默认账户,结合“被动”特权提升,可以恢复管理员密码。然后我们可以使用 filetransport.py 覆盖 /etc/passwd。

虽然 IPC 固件中没有默认账户,但出现了另一种恢复方法——PSW 方法。这是一种完全没有安全性的密码恢复机制。自 V9 以来的所有可下载固件版本中都存在该机制。虽然 filetransport.py 仅适用于 NVR,但 upgrade_rw.py 通过利用升级机制在 IPC 上实现了相同目的,因此我们仍然可以获得 root 访问权限。

2019 固件引入了另一个攻击向量——利用 Web API 的认证绕过。通过在未经认证的情况下导出配置文件,我们可以恢复密码,并准备一个升级包来覆盖任意文件。

说到漏洞,共有 4 个:

  • 硬编码的 telnet 凭证(旧 NVR 固件)
  • 经过认证的特权提升(任何用户都能读取管理员密码)
  • 不安全的密码恢复(对称加密密钥嵌入在二进制中)
  • Web API 认证绕过(在 URL 路径中附加某些字符串即可禁用认证)

请注意,我没有调查“云”功能,即是否可能枚举设备,从而连接到未暴露在互联网上的设备(就像 Xiongmai 设备那样)。

漏洞

旧固件中硬编码的 telnet 凭证

在旧版本中,telnet 默认启用,我们可以在 /etc/passwd 文件中找到以下内容:

root@kitploit:~
support:$1$$AErA9BQgLjrxTJB1748k71:501:501:Linux User,,,:/home/support:/bin/sh

(root 密码是动态更新的,另外我没有破解这个哈希,因此非常欢迎提交 PR :D)

support 用户(实际上存在于所有固件版本中)看起来可能没有特权,但当然,该用户拥有足够的权限来读取 Admin 密码,并且可以覆盖 /etc/init.d 中全局可写的初始化脚本,甚至可以创建新的脚本 :)

默认账户 + 经过认证的特权提升

从概念上讲,这个方法非常简单。我们只需发送一个登录数据包并读取响应。就这样,因为“登录成功”的响应包含所有用户的凭证,无论我们拥有什么权限。虽然在 V7 中也是如此,但这个方法实际上直到 NVR 固件引入默认账户后才变得有用。无法移除的 “Default” 账户没有远程权限,所以你不能用它做任何事情。好吧,也许除了读取管理员密码之外……

虽然这听起来很简单,但实现起来并不简单。通信使用自定义协议,密码使用 DES 加密,但位序反转(最难的部分就是弄清楚这一点),密钥由服务器传输。由于没有密钥派生,窃听者可以轻松解密所有内容。然而,你仍然需要弄清楚位序反转,或者从头重新实现整个机制……

实现请参见 recover.py 文件中的 recover_with_default 函数。

不安全的密码恢复——PSW 方法

在分析二进制文件时,很难不注意到这个机制。它的全部目的是……让密码恢复成为可能,而且它确实做得很好。我会说,好过头了……

这是怎么回事?我认为这原本应该是一个密码恢复机制,大概是让厂商能够为设备所有者提供一种恢复他们自己密码的方法。

我可以推测,其流程原本应该是这样的:

  1. 你在配置设备时输入你的电子邮件或手机号
  2. 你请求 Tiandy 恢复你的密码
  3. Tiandy 向你的设备发送一个“魔法数据包”,并获取电子邮件/手机号以及用于推导安全代码的加密数据。
  4. Tiandy 解密并推导出安全代码,然后通过该通信渠道发送给你。
  5. 你在客户端程序中输入此安全代码,客户端会发送第二个数据包。
  6. 瞧。客户端解密响应并显示凭证。

这一切都很好,只是缺少一样东西……安全在哪里? 事实证明,哪里都没有。没有什么能阻止我们发送这个数据包、推导出安全代码,并恢复任何可访问设备的密码。

我觉得这很令人震惊,因为这并不是机制中存在某个能破坏安全性的缺陷。而是安全性根本不存在。没有需要修复的东西,但在我看来,这也不像是一个明显的后门。它会在日志中留下痕迹,并且有 3 种不同的推导方案,每一种都比前一种更复杂。这实际上花费了不少时间来实现……

回到这个方法,要使其生效,设备需要关联一个手机号/邮箱。查看旧版本时,我发现只有管理员才能设置,但后来我找到了一个绕过方法。令人惊讶的是,在较新的固件中,不再需要该绕过方法,因为明确可以在无需认证的情况下更改设备邮箱。现在,_这_才是我认为后门本该存在的地方 :)

在技术层面,这个机制实际上相当复杂,也是逆向和重新实现难度最大的一个。该机制有 3 个版本,每个版本使用不同的算法来推导安全代码。除了位序反转的 DES 之外,还涉及使用硬编码密钥的自定义替换密码。但这一切都白费了,因为无论怎么努力,Tiandy 都无法让使用硬编码密钥的对称加密变得安全。

我观察到的一点是,由于安全代码每分钟都会变化,原始流程有可能因为代码在步骤 3 和步骤 5 中发送的数据包之间发生变化而失败,无论发送这些数据包之间经过的时间有多短。我考虑到了这一点,因此如果代码无效,我的脚本会重试该流程。

整个过程,包括设置邮箱(我们不需要拥有该邮箱),都在 recover.py 文件中实现。

Web API 认证绕过

这个漏洞适用于拥有“现代”Web 界面(带“地图”的那个界面)的较新固件版本(2019 年及以后)。我喜欢那张地图,尽管澳大利亚看起来有点变形。

这个绕过很简单。虽然大多数 API 端点都经过认证,但也有一些例外。然而,是否跳过认证的检查与激活哪个端点的匹配是在不同位置实现的。在大多数情况下,当 URL 路径等于给定字符串时,认证会被跳过,这是安全的。

但较新的版本引入了另一个例外,当字符串 Record/DownLoad 和 ID= 仅仅出现在 URL 路径中的某个位置时,该例外就会被激活。

对于需要匹配完整路径的端点来说,这仍然是安全的。由于 Security/users 就是这样的端点之一,我们无法直接恢复密码。幸运的是,配置导出端点是通过检查 URL 路径是否以给定字符串开头(使用 strncmp)来选择的,因此我们只需将这些字符串附加到路径中,即可导出配置文件。

利用此缺陷恢复密码的实现位于 cgi_recover.py 中。

密码恢复之外

理论上,管理员可以升级固件,而且固件既没有签名也没有加密。但我们真的需要准备自定义固件包吗?有时不需要。有时需要,而我已经疯狂到真的去实现了它……

对于旧 NVR 固件

如果你拥有密码,那么有一个命令注入漏洞可以利用,即 ftpupdate.py 脚本。其原理是,我们让设备通过 FTP 获取升级包,而 ftpget 被用来执行该任务。毫不意外,我们的参数会直接流入 system() 函数。

较新的固件

在 NVR 固件中,二进制协议具有 FILETRANSPORT 命令,其作用正如其名。实际上没什么可说的了,因为你也可以下载 SDK 并使用相同的命令。我当然想重新实现它,所以要了解其工作原理,请看 filetransport.py。

不过 IPC 型号没有这个命令,但正如我之前所说,我们总能升级固件。虽然准备整个闪存镜像不现实,但 Tiandy 的升级包允许我们替换单个文件,这正是我们所需要的(参见解包固件)。

只不过,事情并没有那么简单。“box”文件格式包含一些元数据,然后是一个文件数组。第一个文件必须命名为 ProductModule,并且必须包含匹配的设备参数,否则升级不会继续。我们不仅需要这些参数的值,还需要这些参数本身。此外,元数据部分(包括 box 文件版本)也会被验证。

手动组装这些似乎不现实,但幸运的是还有另一种方法。配置文件导出使用相同的“box”文件格式,包含所有匹配的元数据和 ProductModule 文件,只是没有升级类型字段。

我能够找到如何填充该字段,并因此实现了这个过程。虽然该机制在 NVR 固件中也存在,但其工作方式不同,不过既然 NVR 具有前面描述的 FILETRANSPORT 命令,就没有必要进一步分析了。

upgrade_rw.py 脚本利用了升级过程。不过,这个名字暗示了更多东西……这是因为在导出配置文件时,我们按名称指定要导出的文件,而毫不意外的是,任何文件都可以,因此我们可以下载 box 文件,然后使用与解包固件相同的代码读取该文件。

导出和升级都可以通过 Web API 完成。在这种情况下,无法读取任意文件。我仍然想实现这一点,因为 API 似乎更稳定,请参阅 cgi_recover.py。

我没有研究的内容

在支持 FTP 的型号中,可能可以在用户密码中注入 shell 命令(当执行添加该用户以便其通过 FTP 登录的命令时)。

在互联网上寻找 Tiandy 设备

Tiandy 设备开放了 3001 端口。这是非 Web 方法运行所需的端口。 较新的型号除了 554 端口外,还在 9100 端口提供 RTSP,并且还在 1935 端口提供 RTMP。IPC 型号使用 8082 端口提供 ONVIF。HTTP 和 HTTPS 运行在其标准端口上。

使用旧版 Web 界面(采用我们钟爱的 ActiveX 技术)的设备在其 HTTP 响应中包含以下内容之一:

root@kitploit:~
<title>Net Video Browser</title>
root@kitploit:~
tdvideo.css

使用新版 Web 界面(这次用的是……Flash)的设备在响应中包含以下内容:

root@kitploit:~
res/app-0.1.0.css

(Last-Modified 头很乐意向我们透露确切的发布日期)

我们还可以通过证书来识别这些设备,尽管 HTTPS 并不总是启用。所有设备仅使用两种证书,它们可以在下载的固件中找到,这使得证书固定毫无意义。

较旧的那个:

root@kitploit:~
C=CN, ST=Tianjin, O=Tiandy Tech, CN=dvr_ui

较新的那个:

root@kitploit:~
C=CN, ST=Tianjin, L=Tianjin, O=Tiandy Tech Ltd, CN=NetDevice

顺便说一下……HTTPS 支持是通过独立的 stunnel 进程实现的。这很有效,但不出所料,IP 地址在这个过程中丢失了,所以日志总是显示 127.0.0.1。

Tiandy 的 RTSP 和 RTMP URL

Tiandy 声称其设备支持 RTSP 和 RTMP。这很好,但我们在手册中找不到的是如何实际使用这些协议,因为我们找不到所需的 RTSP 和 RTMP URL。 幸运的是,作为分析的副产品,我获得了这些信息,因此我可以分享出来。

RTSP URL

对于 NVR: 要查看通道 C(从 1 开始)且流类型为 S(1、2、3)的实时流:

root@kitploit:~
rtsp://username:password@host/C/S

对于 IPC:
要查看流类型为 S 的实时流:

root@kitploit:~
rtsp://username:password@host/S

RTMP URL

RTMP URL 并不那么简单,因为它们需要自定义哈希以便请求通过认证。不过,RTMP 还允许我们回放录制的视频。

实时流的 URL 是:

root@kitploit:~
rtmp://host/live/C/S/authstring

其中 C 是通道,S 是流类型。

回放的 URL 是:

root@kitploit:~
rtmp://host/vod/START-STOP/C/S/authstring

其中 START 和 STOP 都是 Unix 时间戳。

authstring 的计算方式如下:

root@kitploit:~
base64("username:"+md5("username:password")+":unix_timestamp")

rtmpauth.py 工具可以生成它:

root@kitploit:~
python3 rtmpauth.py username password

由于该时间戳会被检查,且差值不能超过 2 天,因此存在限制:

  • 摄像头需要设置正确的时间
  • RTMP URL 将在 2 天后失效

解包固件

固件升级包采用专有的“box”文件格式打包,该格式既没有签名也没有加密。从解包者的角度来看,这种格式实际上非常简单。有一个我们可以跳过的头部,然后是一个需要解包的文件数组,其中每个文件都有一个固定大小的头部,包含文件名和大小(两次),接着是数据。

unbox.py 工具将文件解包到一个与 box 文件同名的目录中,或解包到指定目录:

root@kitploit:~
python3 unbox.py [box_file]
python3 unbox.py [box_file] [target_dir]

这个工具使用起来应该是安全的(我写下这句话,然后再次检查了该工具,发现了一个漏洞……哎呀),因为绝对路径会被转换为相对路径,.. 会被替换为 __,并且没有符号链接。

有时你需要运行该工具两次,因为你可能会发现 .box 内部的文件是另一个 .box。

下载工具