Smart Doorbell Security (Part 5) (LiteOS analysis)
In the previous part of this series, we analysed the bootloader of the device in order to understand whether the compression in use was trivial. We concluded it appeared to take place in hardware and as such, we didn’t have visibility of its underlying workings.
This post intends to analyse the firmware of the device post decompression, having been dumped from the device’s flash memory, with a focus on determining whether backdoor passwords or undocumented functionality is in use, that could leave the device open to compromise.
Extracting the firmware
We extract the firmware from the device similarly to how we extracted the bootloader. We use U-Boot’s md.b functionality following the issuance of a hwdec command to dump the decoded bytes.
# sf probe 0;sf read 0x82000000 0x1a0000 0x5b0000;hwdec 0x80008000 0x82000000;
[...]
# mb.d 0x80008000 0x5b0000
80008000: 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 …………….
80008010: 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 …………….
80008020: 5c 15 40 80 48 e7 48 80 54 e7 48 80 74 e7 48 80 .@.H.H.T.H.t.H.
80008030: 84 e7 48 80 94 e7 48 80 78 f4 3f 80 a4 e7 48 80 ..H…H.x.?…H.
80008040: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 …………….
80008050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
80008060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
[...]
This process takes about 20 minutes. Following extraction, we can use tools such as https://github.com/gmbnomis/uboot-mdb-dump/ to convert the ASCII hex data to binary form.
What is LiteOS?
Based on the shell prompt available via the serial port, we’re aware that we’re looking at a LiteOS system. Whilst this system is based on Linux, it doesn’t conform to the typical ‘files can be launched as processes’ style of operation. Instead, it’s an RTOS, meaning amongst other things, it uses the concept of tasks and commands compiled directly into the extracted firmware.
It’s also open source and available at https://github.com/LiteOS, this is useful and is something we’ll take advantage of later.
The following commands are available to us via the serial console:
Huawei LiteOS # help
*shell commands:*
always_rx always_tx arp board_on call cat cd cleanconf
clear close_ch14 cp cpup dns dump_host_info excInfo file2liteos
findsym flashcp fm_down forceslp format free get_channel get_country
get_station gpioh gpiol grtc gtm hcc_get_para hcc_set_para help
hiddrs himd himm hipriv hisilink hwi i2c_read i2c_write
ifconfig iperf lddrop log_level ls ltm mclose memcheck
mkdir mopen mount nand_bad netcfg netstat ntpdate oam_host_test
open_ch14 partinfo partition ping power_loop print2sdt_ctl pwd readreg
reset rm rmdir rx_info sddt sdio_reinit sem set_country
set_macaddr set_monitor set_pm_switch showhal sleep slp srtc ssp_read
ssp_read ssp_write start_hapd statfs stop_hapd swtmr sync systeminfo
task tcp_lose_id telnet tftp touch uart_close uart_config uart_file_print
uart_read uart_write umount uname updateconf updatemcu updateos updatewav
wifi_debug wifi_exit wkup wkup_reason wlan_power_off wlan_power_on wow_clear_ssid wow_set_ssid
wpa_cli wpa_connect wpa_disconnect wpa_get_status wpa_scan wpa_scan_result wpa_start wpa_stop
wps_ssid_key writeproc writereg
There are many commands including those responsible for handling wireless mode and connection, network services, updates and the file system, which is a minimal JFFS2 partition containing WAV files and exposing informational data.
It’s not uncommon for an RTOS to use the concept of tasks and scheduling, where each task is responsible for performing or responding to a given event and LiteOS is no exception. We note the ‘task‘ command is available and it lists the currently created tasks:
Huawei LiteOS # task
Name TID Priority Status StackSize WaterLine StackPoint TopOfStack EventMask SemID CPUUSE CPUUSE10s CPUUSE1s MEMUSE
---- --- -------- -------- --------- ---------- ---------- ---------- --------- ----- ------- --------- --------- -------
Swt_Task 0x0 0 QueuePend 0x6000 0x3a0 0x8078d890 0x80787920 0x0 0xffff 1.8 1.6 1.6 0
IdleCore000 0x1 31 Ready 0x800 0x5c 0x8078e0e4 0x8078d940 0x0 0xffff 42.8 45.3 46.7 0
system_wq 0x2 1 Pend 0x6000 0x130 0x80795198 0x8078f228 0x1 0xffff 0.0 0.0 0.0 0
wowalive 0x3 10 Pend 0x6000 0x428 0x8079aff8 0x80795240 0x0 0xdf 0.0 0.0 0.0 7327956
pth01 0x4 10 Delay 0x6000 0x120 0x807a32a0 0x8079d320 0x0 0xffff 0.0 0.0 0.0 0
ubia_wdg 0x5 10 Delay 0x6000 0x2c0 0x807a92f8 0x807a3378 0x0 0xffff 0.0 0.0 0.0 0
shellTask 0x6 9 Running 0x3000 0x520 0x807b07e4 0x807adbc8 0xfff 0xffff 0.5 0.0 0.0 -5496
cmdParesTask 0x7 9 Pend 0x1000 0x170 0x807b1b98 0x807b0c68 0x1 0xffff 0.0 0.0 0.0 420
tcpip_thread 0x8 5 PendTimeOut 0x6000 0x2ac 0x808e2998 0x808dcaa0 0xf 0xffff 0.1 0.1 0.1 292272
himci_Task 0x9 10 Ready 0x1800 0x138 0x808e5428 0x808e3cc0 0x0 0xffff 0.0 0.0 0.0 0
jffs2_gc_thread 0xa 10 Pend 0x1000 0x128 0x80977790 0x80976818 0x3 0xffff 0.0 0.0 0.0 0
pth03 0xb 10 Ready 0x6000 0x608 0x80e90c88 0x80e8add8 0x1 0xffff 2.4 2.3 2.4 0
venc_proc 0xc 10 Ready 0x6000 0x84c 0x80ea7aa8 0x80ea1e90 0x0 0xffff 0.7 0.7 0.8 888
SceneautoNormalThread 0xd 10 Ready 0x6000 0xe18 0x80ebee80 0x80eb9518 0x0 0xffff 0.0 0.0 0.0 0
SceneautoSpecialThread 0xe 10 Ready 0x6000 0xc80 0x80ec5060 0x80ebf538 0x0 0xffff 0.4 0.3 0.3 0
hi_Aenc_Get 0xf 10 Ready 0x12000 0x298 0x80f10028 0x80efe220 0x1 0xffff 0.0 0.0 0.0 0
FDKAACEncThread 0x10 10 Ready 0x6000 0x2b40 0x80f5de98 0x80f57f48 0x0 0xffff 3.3 3.3 3.4 5168
rmmAudioPlayTh 0x11 10 Ready 0x6000 0x5d0 0x80f63c18 0x80f5df60 0x0 0xffff 0.0 0.0 0.0 0
playSpecialAudio 0x12 10 Pend 0x6000 0x190 0x80f69e90 0x80f63f80 0x0 0xc0 0.0 0.0 0.0 2064
AudioEncThread 0x13 10 PendTimeOut 0x6000 0x3c8 0x80f6fdd0 0x80f69f98 0x0 0xd2 0.3 0.4 0.5 868
AudioIn 0x14 10 Ready 0x6000 0x4634 0x80f71d98 0x80f6ffb8 0x0 0xffff 44.6 43.7 43.2 0
MCUHOST_Thread 0x15 10 Pend 0x6000 0x380 0x80f82fb0 0x80f7d090 0x1 0xffff 0.0 0.0 0.0 0
check_reset 0x16 10 Ready 0x6000 0x2f0 0x80f89000 0x80f830b0 0x0 0xffff 0.0 0.0 0.0 0
hisi_frw0 0x17 5 Pend 0x2000 0x8a8 0x80fba190 0x80fb8228 0x1 0xffff 0.3 0.2 0.2 77776
wlan_pm_wq 0x18 1 Pend 0x6000 0x418 0x80fc0480 0x80fba510 0x1 0xffff 0.0 0.0 0.0 25448
hisi_hcc 0x19 5 Pend 0x2000 0x418 0x80fc2808 0x80fc08a8 0x1 0xffff 1.1 1.1 0.0 -860536
wal_txdata 0x1a 6 Pend 0x2000 0x244 0x80fcdc80 0x80fcbd08 0x0 0xcb 0.0 0.0 0.0 -1800
wpa_supplicant 0x1b 10 PendTimeOut 0x6000 0x900 0x80fd3d88 0x80fcdea8 0x7 0xffff 0.2 0.0 0.0 -28848
oal_gpio_rx_data 0x1c 2 Pend 0x6000 0x350 0x80fdaf30 0x80fd4fc8 0x1 0xcf 0.0 0.0 0.0 628988
netmgmt 0x1d 10 PendTimeOut 0x6000 0x1a58 0x80fe9c38 0x80fe4080 0x0 0xfc 0.1 0.0 0.0 171064
record_proc 0x1e 10 Ready 0x6000 0x1b0 0x80feff88 0x80fea098 0x0 0xffff 0.0 0.0 0.0 524304
pth17 0x1f 10 Impossible 0x6000 0x318 0x809877f8 0x80981848 0x0 0xffff 0.0 0.0 0.0 0
wowalive 0x20 10 Pend 0x6000 0x428 0x80b7cdc8 0x80b77010 0x0 0x102 0.0 0.0 0.0 716
wowalive 0x21 10 Pend 0x6000 0x428 0x80b82de8 0x80b7d030 0x0 0xfe 0.0 0.0 0.0 704
wowalive 0x22 10 Pend 0x6000 0x428 0x80b88e00 0x80b83048 0x0 0xe2 0.0 0.0 0.0 708
ubia_server 0x23 10 Ready 0x6000 0x730 0x80b8ef20 0x80b89068 0x0 0xffff 0.1 0.1 0.0 265536
sntp 0x24 10 Pend 0x6000 0x320 0x80b94f50 0x80b8f080 0x0 0xe7 0.0 0.0 0.0 0
pth25 0x26 10 Ready 0x6000 0xd40 0x80bcea50 0x80bc91b8 0x0 0xffff 0.0 0.0 0.0 -37136
pth26 0x27 10 Ready 0x6000 0x3d0 0x80bd50c8 0x80bcf1d0 0x0 0xffff 0.0 0.0 0.0 -216
pth27 0x28 10 Ready 0x6000 0x66c 0x80bdaf50 0x80bd51f0 0x0 0xffff 0.0 0.0 0.0 3276
There is a lot of useful information here. I’m most interested in the ‘ubia_server’ task, which is most likely to contain the functionality around authentication mechanisms that I’d like to review.
Previous research with RTOS’s commonly involves a live memory dump where the task structures are located in memory based on a unique identifier in the task’s low-level structure, often an ‘id’ or ‘name’ field for example. I might fallback to this method if I find a need to.
Finding a base for comparison
There is always value in having a base to compare against. With LiteOS being open source, we can obtain a compiled version with debug symbols, which will both assist in symbol recovery as well as understanding control flow.
Huewei’s developer portal gives access to the HiSilicon SDK1.0 (Huawei LiteOS) archive. It contains many libraries used by LiteOS and an ‘out’ directory, which in turns contains libraries used by sample code, a raw binary blob of the compiled sample and even raw assembler source of the same sample with symbols given.
An excerpt of the raw assembler is below:
/usr1/mayiheng/history/b013/Huawei_LiteOS/build/hi3516cv200/vs_server: file format elf32-littlearm
Disassembly of section .rom_vectors:
80008000 <__exception_handlers>:
80008000: e59ff018 ldr pc, [pc, #24] ; 80008020 <_reset_vector>
80008004: e59ff018 ldr pc, [pc, #24] ; 80008024
80008008: e59ff018 ldr pc, [pc, #24] ; 80008028
8000800c: e59ff018 ldr pc, [pc, #24] ; 8000802c
80008010: e59ff018 ldr pc, [pc, #24] ; 80008030
80008014: e59ff018 ldr pc, [pc, #24] ; 80008034
80008018: e59ff018 ldr pc, [pc, #24] ; 80008038
8000801c: e59ff018 ldr pc, [pc, #24] ; 8000803c
80008020 <_reset_vector>:
80008020: 8000b000 .word 0x8000b000
80008024 :
80008024: 80045840 .word 0x80045840
80008028 :
80008028: 8004584c .word 0x8004584c
8000802c :
8000802c: 8004586c .word 0x8004586c
80008030 :
80008030: 8004587c .word 0x8004587c
80008034 :
80008034: 8004588c .word 0x8004588c
80008038 :
80008038: 8004573c .word 0x8004573c
8000803c :
8000803c: 8004589c .word 0x8004589c
80008040 <_armboot_start>:
80008040: 80008000 .word 0x80008000
80008044 <_TEXT_BASE>:
80008044: 00000000 .word 0x00000000
Disassembly of section .text:
8000b000 :
8000b000: e59fd0c8 ldr sp, [pc, #200] ; 8000b0d0
8000b004: e3a000d2 mov r0, #210 ; 0xd2
8000b008: e129f000 msr CPSR_fc, r0
8000b00c: e59fd0c0 ldr sp, [pc, #192] ; 8000b0d4
8000b010: e3a000db mov r0, #219 ; 0xdb
Compared to our extracted firmware:
ROM:80008000 ; Segment type: Pure code
ROM:80008000 AREA ROM, CODE, READWRITE, ALIGN=0
ROM:80008000 ; ORG 0x80008000
ROM:80008000 CODE32
ROM:80008000
ROM:80008000 ; =============== S U B R O U T I N E =======================================
ROM:80008000
ROM:80008000 ; Attributes: thunk
ROM:80008000
ROM:80008000 sub_80008000 ; DATA XREF: ROM:off_80008040↓o
ROM:80008000 ; sub_803D96AC+14↓o …
ROM:80008000 LDR PC, =sub_8040155C
ROM:80008000 ; End of function sub_80008000
ROM:80008000
ROM:80008004 ; ---------------------------------------------------------------------------
ROM:80008004 LDR PC, =loc_8048E748
ROM:80008008 ; ---------------------------------------------------------------------------
ROM:80008008 LDR PC, =sub_8048E754
ROM:8000800C ; ---------------------------------------------------------------------------
ROM:8000800C LDR PC, =loc_8048E774
ROM:80008010 ; ---------------------------------------------------------------------------
ROM:80008010 LDR PC, =loc_8048E784
ROM:80008014 ; ---------------------------------------------------------------------------
ROM:80008014 LDR PC, =loc_8048E794
ROM:80008018 ; ---------------------------------------------------------------------------
ROM:80008018 LDR PC, =loc_803FF478
ROM:8000801C ; ---------------------------------------------------------------------------
ROM:8000801C LDR PC, =loc_8048E7A4
ROM:8000801C ; ---------------------------------------------------------------------------
ROM:80008020 off_80008020 DCD sub_8040155C ; DATA XREF: sub_80008000↑r
ROM:80008024 off_80008024 DCD loc_8048E748 ; DATA XREF: ROM:80008004↑r
ROM:80008028 off_80008028 DCD sub_8048E754 ; DATA XREF: ROM:80008008↑r
ROM:8000802C off_8000802C DCD loc_8048E774 ; DATA XREF: ROM:8000800C↑r
ROM:80008030 off_80008030 DCD loc_8048E784 ; DATA XREF: ROM:80008010↑r
ROM:80008034 off_80008034 DCD loc_8048E794 ; DATA XREF: ROM:80008014↑r
ROM:80008038 off_80008038 DCD loc_803FF478 ; DATA XREF: ROM:80008018↑r
ROM:8000803C off_8000803C DCD loc_8048E7A4 ; DATA XREF: ROM:8000801C↑r
ROM:80008040 off_80008040 DCD sub_80008000 ; DATA XREF: sub_8040155C+94↓o
ROM:80008040 ; sub_8040155C+98↓r …
ROM:80008044 dword_80008044 DCD 0 ; DATA XREF: sub_8040155C+9C↓o
ROM:80008044 ; sub_8040155C+A0↓r …
ROM:80008048 DCB 0
ROM:80008049 DCB 0
ROM:8000804A DCB 0
ROM:8000804B DCB 0
This is a good start and provides some insight into what we’re looking at. As per usual operation, we attempt to recover as many symbols as we can.
Symbol recovery
We note that we’re using a Hi3518E board and the sample data we have exists for Hi3516CV200. This discrepancy is likely to significantly reduce our chances of recovering many symbols, it is, however, a place to start.
The archive we have contains AR (.a) archives containing object files, which subsequently contain symbols.
We can quickly produce FLIRT signatures for all of these libraries as follows:
$ find . -name "*.a" -type f -not -path "./out/*" -exec cp {} tmp/ \;
$ cd tmp
$ ar x *.a;
$ pelf -v *.o liteos.pat
$ sigmake liteos.pat liteos.sig
Quick-note: FLIRT vs Diaphora
A very quick note. The astute reader will notice that during symbol recovery for U-Boot I used Diaphora and in this case, I’ve reverted back to FLIRT signatures. The two are not interchangeable and Diaphora is my preference in cases where it’s practical to use. It isn’t practical to use, however, when we’ve a large number of shared libraries as is the case with LiteOS. As such I’ve opted to use FLIRT for this case.
…Back to symbol recovery
The commands issued previously intend to locate all available AR archives in our working directory, extract the object files within and produce FLIRT signatures from them. Having repeated this process with libraries available in the ‘out’ directory (so as to be inclusive of all libraries) and resolved collisions, we’ve recovered a small number of symbols.
We also note that fortunately, string references are intact that help to locate much of the more interesting functionality:
In terms of string usage, we see many examples of the below throughout various functions:
dnprintf(
"\r\n[%9u][ULOG_INFO]%s[%d] %s[IOTC %2d]: SID[%d] entry \n",
time,
"thread_ForAVServerStart",
5277,
"thread_ForAVServerStart",
iotc_channel_id,
sid);
We use these string references to name functions we encounter throughout the binary data and infer any additional library usage.
Researching the functions used, we find references to TUTK’s IOTC library, where the documentation describes the parameters for the given functions and more importantly, the project also includes compiled libraries.
Whilst we’ve renamed a number of functions so far based on strings within the identified code, we can use the TUTK libraries to help verify and perform further symbol recovery. The following notable libraries were included within the TUTK GitHub project for example:
$ ./TUTK_IOTC_Platform_14W36/Lib/Linux/Arm9_Hi3518_4.4.1$ ls
libIOTCAPIsT.a libIOTCAPIs.a libAVAPIsT.a libAVAPIs.a libP2PTunnelAPIsT.a libP2PTunnelAPIs.a libP2PTunnelAPIs.so libRDTAPIs.a
libIOTCAPIsT.so libIOTCAPIs.so libAVAPIsT.so libAVAPIs.so libP2PTunnelAPIsT.so libRDTAPIsT.a libRDTAPIsT.so libRDTAPIs.so
There aren’t a huge number, so we can use Diaphora with fuzzy matching to try and recognise IOTC and AVAPI library methods at the least. The results:
We see that a small number of functions we’ve already named based on strings are recognised, as well as further yet to be identified functions.
Hard-coded secrets
We can also investigate to identify hard-coded secrets. Binwalk identifies many hard-coded RSA keys for example:
DECIMAL HEXADECIMAL DESCRIPTION
188329 0x2DFA9 Certificate in DER format (x509 v3), header length: 4, sequence length: 1300
1214265 0x128739 Certificate in DER format (x509 v3), header length: 4, sequence length: 1396
3850073 0x3ABF59 Certificate in DER format (x509 v3), header length: 4, sequence length: 5376
3910769 0x3BAC71 Certificate in DER format (x509 v3), header length: 4, sequence length: 5380
4872640 0x4A59C0 Unix path: /home/hd1/video
4890920 0x4AA128 Unix path: /home/libusr/sceneauto_ar0130.ini
4897088 0x4AB940 Unix path: /home/audio/wificonnecting.aac
4897404 0x4ABA7C Unix path: /home/audio/online.aac
4897844 0x4ABC34 Unix path: /home/audio/wificonnectfail.aac
4897936 0x4ABC90 Unix path: /home/audio/setupok.aac
4903404 0x4AD1EC Base64 standard index table
4903828 0x4AD394 Unix path: /home/tmpfs/API_DEBUG
4911156 0x4AF034 Unix path: /usr/local/etc/zoneinfo
4912764 0x4AF67C Unix path: /home/pub/platform-h6liteosbranch/mpp/code/mpi/src/mpi_vb.c
4913960 0x4AFB28 Unix path: /home/pub/platform-h6liteosbranch/mpp/code/shelf/venc/mpi/mpi_venc.c
4919712 0x4B11A0 Unix path: /home/pub/platform-h6liteosbranch/mpp/code/mkp/include/valg_ext.h
4926448 0x4B2BF0 Unix path: /home/pub/platform-h6liteosbranch/mpp/code/shelf/audio/audio/mpi/src/mpi_ao.c
4929776 0x4B38F0 Unix path: /home/pub/platform-h6liteosbranch/mpp/code/shelf/audio/audio/mpi/audio/audio_voice_adp.c
4931516 0x4B3FBC Unix path: /home/pub/platform-h6liteosbranch/mpp/code/mpi/src/mpi_bind.c
4999462 0x4C4926 PARity archive data - file number 18774
5069276 0x4D59DC Unix path: /home/pub/platform-h6liteosbranch/mpp/code/shelf/venc/include/venc_buf.h
5092964 0x4DB664 Unix path: /home/pub/platform-h6liteosbranch/mpp/code/mkp/include/valg_tool.h
5122326 0x4E2916 TIFF image data, big-endian, offset of first image directory: 8
5392757 0x524975 Copyright string: "copyright law and international treaties."
5471456 0x537CE0 PEM certificate
5472762 0x5381FA PEM certificate
5474068 0x538714 PEM certificate
5474956 0x538A8C PEM RSA private key
5476668 0x53913C PEM certificate
5477976 0x539658 PEM RSA private key
5479688 0x539D08 PEM certificate
5480912 0x53A1D0 PEM RSA private key
5482696 0x53A8C8 PEM certificate
5484004 0x53ADE4 PEM certificate
5485316 0x53B304 PEM certificate
5486628 0x53B824 PEM EC private key
5486868 0x53B914 PEM certificate
5487704 0x53BC58 PEM EC private key
5487944 0x53BD48 PEM certificate
5488784 0x53C090 PEM EC private key
5489152 0x53C200 PEM certificate
5491084 0x53C98C PEM certificate
5493036 0x53D12C CRC32 polynomial table, little endian
5497132 0x53E12C CRC32 polynomial table, big endian
5521460 0x544034 PEM RSA private key
5521524 0x544074 PEM EC private key
5524028 0x544A3C SHA256 hash constants, little endian
5524816 0x544D50 Base64 standard index table
5540947 0x548C53 Copyright string: "copyright holder(s) nor the"
5541452 0x548E4C Copyright string: "copyright"
5541587 0x548ED3 Copyright string: "copyright"
5542080 0x5490C0 Copyright string: "Copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors"
5598000 0x556B30 Neighborly text, "Neighboring BSS: freq=%dd"
5606688 0x558D20 Neighborly text, "Neighboring BSS: %02x:%02x:%02x:%02x:%02x:%02x freq=%d pri=%d sec=%dec=%d"
5607296 0x558F80 Neighborly text, "neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)"
5608473 0x559419 Neighborly text, "neighboring BSSes prior to enabling 40 MHz channelquest a scan of neighboring BSSes ret=%d (%s) - try to scan again"
5608552 0x559468 Neighborly text, "neighboring BSSes ret=%d (%s) - try to scan againequest a scan of neighboring BSSes ret=%d (%s)"
5608632 0x5594B8 Neighborly text, "neighboring BSSes ret=%d (%s)d"
5657740 0x56548C SHA256 hash constants, little endian
5672604 0x568E9C Unix path: /home/xwx514094/0413/b060_sdk_fabu/source/wifi_project/drv/sdio_hi1131sv100/hi1131_driver/driver/platform/inc/oal/liteos/arch/oa
5672984 0x569018 Unix path: /home/xwx514094/0413/b060_sdk_fabu/source/wifi_project/drv/sdio_hi1131sv100/hi1131_driver/driver/platform/inc/oal/liteos/arch/oa
5686708 0x56C5B4 Unix path: /home/xwx514094/0413/b060_sdk_fabu/source/wifi_project/drv/sdio_hi1131sv100/hi1131_driver/driver/platform/inc/oal/oal_list.h
We won’t pay much attention to these during our analysis, but note their existence.
Checking for undocumented functionality
In an earlier part of the series, we briefly mentioned that the client Android application defines many IOCTL codes to perform specific functions, such as a firmware update for example. Recall the client code:
package com.ubia.IOTC;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.util.Base64;
import android.util.Log;
import cn.ubia.UbiaApplication;
import cn.ubia.fragment.MainCameraFragment;
import cn.ubia.util.StringUtils;
import java.io.PrintStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class AVIOCTRLDEFs
{
public static final int AVIOCTRL_AUTO_PAN_LIMIT = 28;
public static final int AVIOCTRL_AUTO_PAN_SPEED = 27;
public static final int AVIOCTRL_AUTO_PAN_START = 29;
public static final int AVIOCTRL_CLEAR_AUX = 34;
public static final int AVIOCTRL_ENVIRONMENT_INDOOR_50HZ = 0;
public static final int AVIOCTRL_ENVIRONMENT_INDOOR_60HZ = 1;
public static final int AVIOCTRL_ENVIRONMENT_NIGHT = 3;
public static final int AVIOCTRL_ENVIRONMENT_OUTDOOR = 2;
public static final int AVIOCTRL_EVENT_ALL = 0;
public static final int AVIOCTRL_EVENT_EXPT_REBOOT = 16;
public static final int AVIOCTRL_EVENT_IOALARM = 3;
public static final int AVIOCTRL_EVENT_IOALARMPASS = 6;
public static final int AVIOCTRL_EVENT_MOTIONDECT = 1;
public static final int AVIOCTRL_EVENT_MOTIONPASS = 4;
public static final int AVIOCTRL_EVENT_SDFAULT = 17;
public static final int AVIOCTRL_EVENT_VIDEOLOST = 2;
public static final int AVIOCTRL_EVENT_VIDEORESUME = 5;
[...]
Through string references and symbol XREFs, we’re able to trace back through the firmware and locate the function responsible for processing these IOCTL codes. This is always a good location to inspect for undocumented functionality, as well to verify the implementation of sensitive functions.
Having performed a light skimming sweep and named some obvious variables, we’re left with code like below:
if ( ioctl_code > 0x350 )
{
if ( ioctl_code > 0x1101 )
{
if ( ioctl_code != 0x1215 )
{
if ( ioctl_code > 0x1215 )
{
if ( ioctl_code != 0x1221 && ioctl_code != 0x3000 )
{
if ( ioctl_code != 4631 )
goto process_other_ioctl;
result = ubia_FirmwareUpdateProc(result, a2, a3);
}
}
else if ( ioctl_code != 0x1210 )
{
if ( ioctl_code != 0x1212 )
goto process_other_ioctl;
result = dnprintf("\n 0x1212");
}
}
}
Looking at the code residing at the ‘process_other_ioctl‘ goto directive:
process_other_ioctl:
result = (unsigned int)process_other_ioctl((char *)result, a2, a3, ioctl_code);
goto LABEL_16;
}
result = ((int (__cdecl *)(unsigned int))loc_8003CF20)(result);
LABEL_16:
if ( v89 != dword_806717B4 )
{
sub_803FEFE8();
JUMPOUT(&dword_806717B4);
}
return result;
}
This calls another handler which processes further control codes, we verify our assumed names align with what’s in the client code:
if ( ioctl_code > 0x312 )
{
if ( ioctl_code == 0x332 )
{
result = (char *)set_password((int)result, a2, a3);
}
[...\
And the client definition for the change password IOCTL code:
public static final int IOTYPE_USER_IPCAM_SETPASSWORD_REQ = 818; // 0x332
Inspecting the other handlers, there doesn’t seem to be any undocumented or potentially harmful handlers defined in this region.
Analysing the client authentication callback
After more backtracing and cursory analysis, we identify the handler for connecting clients:
int __fastcall thread_ForAVServerStart(unsigned int *sid_1)
{
unsigned int sid; // r6
void *v2; // r5
int time; // r0
int v4; // r3
int server_type; // r7
signed int timeout; // r3
int avindex; // r4
int time_; // r0
int v9; // r0
int time____; // r0
_BOOL1 v11; // zf
signed int bytes_received; // r7
int v13; // r0
_BOOL1 v14; // zf
int v15; // r0
int v16; // r0
int v17; // r0
signed int v19; // r9
int time___; // r0
int v21; // r0
signed int iotc_channel_id; // [sp+24h] [bp-30h]
int a2; // [sp+28h] [bp-2Ch]
int resend_bytes; // [sp+2Ch] [bp-28h]
sid = *sid_1;
iotc_channel_id = sid_1[1];
sub_8004A930((int)sid_1);
v2 = (void *)malloc(1312);
if ( !v2 )
{
v21 = GetTimeOSD();
dnprintf("\r\n[%9u][ULOG_INFO]%s[%d] %s allocate %d memory error! ", v21);
sub_8004AF34(dword_8063D548);
pthread_close(0);
}
resend_bytes = -1;
time = GetTimeOSD();
dnprintf(
"\r\n[%9u][ULOG_INFO]%s[%d] %s[IOTC %2d]: SID[%d] entry \n",
time,
"thread_ForAVServerStart",
5277,
"thread_ForAVServerStart",
iotc_channel_id,
sid);
sub_8004AF34(dword_8063D548);
memset((int)v2, 0, 1312u);
snprintf((int)v2, "tAVServer_%02d:%02d", sid, iotc_channel_id);
sub_803E6DAC(15, v2);
if ( g_p_ubia_hal_mgmt && (v4 = *(unsigned __int16 *)(g_p_ubia_hal_mgmt + 438), v4 != 25) )
{
server_type = (v4 << 16) | 0x101;
timeout = (v4 << 16) | 0x101;
}
else
{
timeout = 0x130101;
server_type = 0x130101;
}
avindex = AVServerStart2(sid, (int)pfxAuthCallback, 0, timeout, iotc_channel_id);
time_ = GetTimeOSD();
dnprintf(
"\r\n[%9u][ULOG_INFO]%s[%d] AVServerStart resend[%d] serverType:0x%08x\n",
time_,
"thread_ForAVServerStart",
5296,
resend_bytes,
server_type);
sub_8004AF34(dword_8063D548);
if ( avindex < 0 )
{
time___ = GetTimeOSD();
dnprintf(
"\r\n[%9u][ULOG_INFO]%s[%d] %s[IOTC %2d]: SID=%d, avIndex=%d < 0, error exit !!\n",
time___,
"thread_ForAVServerStart",
5301,
"thread_ForAVServerStart",
iotc_channel_id,
sid,
avindex);
sub_8004AF34(dword_8063D548);
sub_8004A930((int)v2);
pthread_close(0);
}
[...]
Notably, we see a call to ‘AVServerStart2‘ which is my assigned name for IOTC function ‘AvServStart2′:
avindex = AVServerStart2(sid, (int)pfxAuthCallback, 0, timeout, iotc_channel_id);
The function prototype of AvServStart2 is notable and documented:
Per the above, the pfxAuthFn parameter is a pointer to the routine responsible for verifying the connecting user’s password. We analyse this function to make sure it isn’t accepting any backdoor passwords. It’s defined as follows in our firmware:
bool __fastcall pfxAuthCallback(char *username, char *password)
{
return strcmp(password, (char *)(g_p_ubia_hal_mgmt + 620)) == 0;
}
Whereas through reverse engineering the change password IOCTL routine, we can ascertain that ‘g_p_ubia_hal_mgmt + 620‘ is indeed our current password.
int __fastcall set_password(int sid, int cid, int user_struct)
{
int time; // r0
int wrong_password; // r3
int time___; // r0
signed int ret; // r4
int n_bytes_sent; // r5
int result; // r0
_BYTE *old_password; // r0
int time_; // r0
int v14; // r0
int v15; // r4
const char *domain; // r1
int user_struct_; // [sp+24h] [bp-B0h]
unsigned int user_struct_a; // [sp+24h] [bp-B0h]
unsigned int v19; // [sp+28h] [bp-ACh]
int pkt_respone_buff; // [sp+2Ch] [bp-A8h]
int local_stack_cookie; // [sp+ACh] [bp-28h]
local_stack_cookie = stack_cookie;
time = GetTimeOSD();
dnprintf(
"\r\n[%9u][ULOG_INFO]%s[%d] set password [SID%d:%d] old:%s, new:%s magic:%04x len=%d countrycode=%d\n",
time,
"ubia_SetPasswordReq",
2446,
sid,
cid,
user_struct, // old password
user_struct + 32, // new password
*(unsigned __int16 *)(user_struct + 64), // magic
*(unsigned __int8 *)(user_struct + 67), // len
*(unsigned __int8 *)(user_struct + 66)); // country code
sub_8004AF34(dword_8063D548);
user_struct_ = g_p_ubia_hal_mgmt + 620;
wrong_password = strcmp(g_p_ubia_hal_mgmt + 620, user_struct);// check old password
if ( wrong_password ) // bad password! strcmp returns 0 on match
{
send_response:
((void (__fastcall *)(signed int))loc_80029494)(4);
memset((int)&pkt_respone_buff, 0, 8u);
ret = 0;
pkt_respone_buff = 0;
n_bytes_sent = AVSendIOCtrl(cid, 0x333, (int)&pkt_respone_buff, 8);
if ( !n_bytes_sent )
{
sid_ = sid;
pthread_create(&v19, 0, (int)thread_ForStopAllAVServer, 0);
result = sub_803E4704(v19);
goto finish_change_password;
}
LABEL_11:
[...]
Assurances
Assuming the library is doing its job correctly (verification is beyond the scope of my analysis), I’m at the least assured that:
- The device isn’t exposing undocumented IOCTL routines that could compromise it in some way
- The device does not contain any obvious backdoor authentication passwords
- Stack cookies are in use, which could complicate attempts at exploitation to an extent
This is good enough for me for this part of the series. There are still questions around the cloud related functionality and perhaps we’ll inspect these areas at a later date.