| Beschreibung | ## Firmware Identification
The firmware file `2.1.1_20171024151200home` was downloaded from YI Technology's official page.
https://www2.yitechnology.com/support/firmware_home/id/9
SHA-256 hashes for independent verification at each extraction stage:
```
$ sha256sum 2.1.1_20171024151200home
a5fef3fda624f77dc4369f64b85e9cba79df02eb6298460eca8e7304db418fb0 2.1.1_20171024151200home
$ file 2.1.1_20171024151200home
2.1.1_20171024151200home: data
$ hexdump -C 2.1.1_20171024151200home | head -3
00000000 41 4e 54 53 49 4d 47 00 a0 4e 02 00 10 c9 33 01 |ANTSIMG..N....3.|
00000010 00 01 00 00 01 00 00 00 00 01 00 00 00 02 00 00 |................|
00000020 00 00 00 02 00 00 00 00 1b 8a f5 c2 32 2e 31 2e |............2.1.|
$ binwalk 2.1.1_20171024151200home
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
512 0x200 UBI erase count header, version: 1, EC: 0x0 [..]
```
The UBI image at offset 0x200 was extracted with `binwalk -Me` and the UBIFS volume unpacked with `ubireader_extract_files`
The `home/web/ipc` binary (ARM32 little-endian ELF) was loaded into Ghidra and three CGI handler functions were decompiled and reviewed. The CGI dispatch function was also decompiled to identify the URL matching mechanism. The log string confirming network accessibility of the HTTP server was identified via `strings` extraction.
The `ipc` binary contains an embedded HTTP server that exposes CGI endpoints accessible from the local network. Ghidra decompilation of three CGI handler functions confirms that none perform any form of authentication, session validation, or token verification before processing requests and performing privileged operations.
The 3 confirmed unauthenticated handlers are:
| `powerctrl.cgi` | `0x000a14a0` | Sets a device control flag (binary toggle) via `key_input=` POST parameter |
| `isxiaoyi.cgi` | `0x000a1850` | Sets a device state flag and responds with `timeout:90` (90-second countdown) |
| `bind_success_xm.cgi` | `0x000a1a24` | Completes device binding operation (internal name: `do_cgi_bind_success`) |
The HTTP server is confirmed to be network-accessible via the log format string `"httpd: client ip = %s"` found at binary address `0x398c30`.
---
## Evidence
### Decompiled `powerctrl.cgi` Handler (`FUN_000a14a0`)
```c
// FUN_000a14a0 @ 000a14a0 (660 bytes)
undefined4 FUN_000a14a0(undefined4 param_1,undefined4 param_2,
undefined4 param_3,undefined4 param_4)
{
int iVar1;
char *pcVar2;
char acStack_7cb4 [30320];
undefined1 auStack_644 [1516];
char acStack_58 [64];
undefined1 *local_18;
char *local_14;
memset(acStack_58,0,0x40);
memset(auStack_644,0,0x5ec);
memset(acStack_7cb4,0,0x7670);
local_14 = acStack_7cb4;
local_18 = auStack_644;
iVar1 = snprintf(local_14,(int)local_18 - (int)local_14,
"HTTP/1.0 200 OK\r\n\r\n{start:\"\",");
local_14 = local_14 + iVar1;
iVar1 = FUN_000a0f04(param_1,param_2,param_3,param_4,auStack_644,0x5ec);
if (-1 < iVar1) {
iVar1 = FUN_000a1320(&DAT_003986c4,acStack_58,0x40,auStack_644);
if (iVar1 == 0) {
iVar1 = snprintf(local_14,(int)local_18 - (int)local_14,"ret_code:\"%d\",",2);
local_14 = local_14 + iVar1;
}
else {
FUN_001ab540();
iVar1 = atoi(acStack_58);
if (iVar1 == 1) {
pcVar2 = "<mangled_symbol>" + DAT_00c88e18 + 0x101;
pcVar2[0] = '\x01';
pcVar2[1] = '\0';
pcVar2[2] = '\0';
pcVar2[3] = '\0';
}
else {
pcVar2 = "<mangled_symbol>" + DAT_00c88e18 + 0x101;
pcVar2[0] = '\0';
pcVar2[1] = '\0';
pcVar2[2] = '\0';
pcVar2[3] = '\0';
}
FUN_001a8770();
FUN_001ab554();
iVar1 = snprintf(local_14,(int)local_18 - (int)local_14,"ret_code:\"%d\",",1);
local_14 = local_14 + iVar1;
}
}
iVar1 = snprintf(local_14,(size_t)(acStack_7cb4 + -(int)local_14),"end:\"\"}");
local_14 = local_14 + iVar1;
FUN_000a0e50(acStack_7cb4,(int)local_14 - (int)acStack_7cb4,param_1);
return 0;
}
```
Note: The decompilation above is from the raw Ghidra headless output with two modifications: (1) the function signature is wrapped for readability, (2) `<mangled_symbol>` replaces a long C++ mangled name (`_ZNSt14_Function_base13_Base_manager...`) that Ghidra uses as a base address - this is a Ghidra decompiler artifact for resolving global data pointers. The `FUN_000a1320` parameter parser compares its first argument against the string literal `"key_input="` via `strcmp` (the literal is at `0x00398698` in the string table), confirming that `DAT_003986c4` passed by the caller contains `"key_input="`.
The function flow is: HTTP 200 response header -> read POST body -> parse `key_input=` parameter -> set a 4-byte device control flag (value 1 or 0). No authentication gate exists at any point in the function.
### Decompiled `isxiaoyi.cgi` Handler (`FUN_000a1850`)
```c
// FUN_000a1850 @ 000a1850 (468 bytes)
undefined4 FUN_000a1850(undefined4 param_1,undefined4 param_2,
undefined4 param_3,undefined4 param_4)
{
int iVar1;
char acStack_7c74 [30320];
undefined1 auStack_604 [1516];
undefined1 *local_18;
char *local_14;
memset(auStack_604,0,0x5ec);
memset(acStack_7c74,0,0x7670);
local_14 = acStack_7c74;
local_18 = auStack_604;
iVar1 = snprintf(local_14,(int)local_18 - (int)local_14,
"HTTP/1.0 200 OK\r\n\r\n{start:\"\",");
local_14 = local_14 + iVar1;
iVar1 = FUN_000a0f04(param_1,param_2,param_3,param_4,auStack_604,0x5ec);
if (-1 < iVar1) {
*(undefined4 *)(DAT_00c88e18 + 0xb4) = 1;
iVar1 = snprintf(local_14,(int)local_18 - (int)local_14,"timeout:\"%d\",",0x5a);
local_14 = local_14 + iVar1;
}
iVar1 = snprintf(local_14,(int)local_18 - (int)local_14,"end:\"\"}");
local_14 = local_14 + iVar1;
FUN_000a0e50(acStack_7c74,(int)local_14 - (int)acStack_7c74,param_1);
return 0;
}
```
The state flag at `DAT_00c88e18 + 0xb4` is written unconditionally with no credential check. The response includes `timeout:90` (0x5a = 90 decimal), suggesting a state transition or countdown is initiated.
Strings output:
```
$ strings home/web/ipc | grep -E "powerctrl|isxiaoyi|bind_success_xm|httpd.*client"
powerctrl.cgi
isxiaoyi.cgi
bind_success_xm.cgi
%s:%s(%d): httpd: client ip = %s
isxiaoyi.cgi
%s:%s(%d): httpd: isxiaoyi.cgi, free request
bind_success_xm.cgi
%s:%s(%d): httpd: bind_success_xm.cgi, free request
```
The CGI endpoint names are stored in a data table at `0x00448bc0` (confirmed via Ghidra xrefs), and the `httpd: client ip = %s` log format confirms the HTTP server accepts remote connections.
The CGI implementation source file is identified as `app_cgi.c` (string at `0x00398664`, referenced from `0x000a0eca`, `0x000a0f96`, `0x000a1b46`, `0x000a1b94`).
Theoretically, any device on the same local network as the camera can interact with these endpoints without providing credentials:
1. Device control manipulation An attacker sends `POST /powerctrl.cgi` with `key_input=0` or `key_input=1` to toggle a device control flag. No authentication is required.
2. Device state manipulation: An attacker sends a request to `isxiaoyi.cgi`, which unconditionally sets a device state flag and returns a 90-second timeout. No authentication is required.
3. Binding hijack: `bind_success_xm.cgi` completes a device binding operation (confirmed by the internal function name `do_cgi_bind_success` and the log string `"ipc has been binded(bind_success)"`). An attacker could leverage this to associate the camera with an attacker-controlled cloud account. |
|---|