Vulnerability title: TP-LINK Cloud Cameras NCXXX Bonjour Command Injection  
Author: Pietro Oliva  
CVE: CVE-2020-12109  
Vendor: TP-LINK  
Product: NC200, NC210, NC220, NC230, NC250, NC260, NC450  
Affected version: NC200 <= 2.1.9 build 200225, NC210 <= 1.0.9 build 200304,  
NC220 <= 1.3.0 build 200304, NC230 <= 1.3.0 build 200304,  
NC250 <= 1.3.0 build 200304, NC260 <= 1.5.2 build 200304,  
NC450 <= 1.5.3 build 200304.  
Fixed version: NC200 <= 2.1.10 build 200401, NC210 <= 1.0.10 build 200401,  
NC220 <= 1.3.1 build 200401, NC230 <= 1.3.1 build 200401,  
NC250 <= 1.3.1 build 200401, NC260 <= 1.5.3 build_200401,  
NC450 <= 1.5.4 build 200401  
The issue is located in the swSystemSetProductAliasCheck method of the  
ipcamera binary (Called when setting a new alias for the device via  
/setsysname.fcgi), where despite a check on the name length, no other checks  
are in place in order to prevent shell metacharacters from being introduced.  
The system name would then be used in swBonjourStartHTTP as part of a shell  
command where arbitrary commands could be injected and executed as root.  
Attackers could exploit this vulnerability to remotely execute commands as root  
on affected devices.  
An attacker would first need to authenticate to the web interface and make a  
request such as the following (the request contents might change slightly  
between cameras):  
POST /setsysname.fcgi HTTP/1.1  
Host: x.x.x.x  
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0  
Content-Type: application/x-www-form-urlencoded  
Cookie: sess=xxxxx  
Content-Length: xxxx  
In a device where telnetd has not been removed from the release firmware (such  
as NC200), this would spawn the telnetd deamon. Default root/root credentials  
could then be used to obtain a root shell via telnet.  
The disassembly of affected code from an NC200 camera is shown below:  
0x0049f1cc lui gp, 0xa  
0x0049f1d0 addiu gp, gp, -0x3ebc  
0x0049f1d4 addu gp, gp, t9  
0x0049f1d8 addiu sp, sp, -0x28  
0x0049f1dc sw ra, (var_24h)  
0x0049f1e0 sw fp, (var_20h)  
0x0049f1e4 move fp, sp  
0x0049f1e8 sw gp, (var_10h)  
0x0049f1ec sw a0, (alias_arg)  
0x0049f1f0 lw v0, (alias_arg)  
0x0049f1f4 nop  
,=< 0x0049f1f8 beqz v0, 0x49f218  
| 0x0049f1fc nop  
| 0x0049f200 lw v0, (alias_arg)  
| 0x0049f204 nop  
| 0x0049f208 lb v0, (v0)  
| 0x0049f20c nop  
,==< 0x0049f210 bnez v0, 0x49f224  
|| 0x0049f214 nop  
|`-> 0x0049f218 addiu v0, zero, 0x42f  
|,=< 0x0049f21c b 0x49f258  
|| 0x0049f220 sw v0, (arg_18h)  
`--> 0x0049f224 lw a0, (alias_arg)  
| 0x0049f228 lw t9, -sym.imp.strlen(gp)  
| 0x0049f22c nop  
| 0x0049f230 jalr t9  
| 0x0049f234 nop  
| 0x0049f238 lw gp, (arg_10h)  
| 0x0049f23c sltiu v0, v0, 0x81  
,==< 0x0049f240 bnez v0, 0x49f254  
|| 0x0049f244 nop  
|| 0x0049f248 addiu v0, zero, 0x430  
,===< 0x0049f24c b 0x49f258  
||| 0x0049f250 sw v0, (arg_18h)  
|`--> 0x0049f254 sw zero, (arg_18h)  
`-`-> 0x0049f258 lw v0, (arg_18h)  
0x0049f25c move sp, fp  
0x0049f260 lw ra, (var_24h)  
0x0049f264 lw fp, (var_20h)  
0x0049f268 jr ra  
0x0049f26c addiu sp, sp, 0x28  
0x0043a008 addiu v0, fp, 0x20  
0x0043a00c move a0, v0  
0x0043a010 addiu a1, zero, 0x88  
0x0043a014 lw t9, -sym.swBonjourGetName(gp) ; <= get the system name in fp+20  
0x0043a018 nop  
0x0043a01c jalr t9  
0x0043a020 nop  
0x0043a024 lw gp, (arg_10h)  
0x0043a028 addiu v0, fp, 0x20 ; <= put ptr to name in v0  
0x0043a02c lw a0, -0x7fdc(gp)  
0x0043a030 nop  
0x0043a034 addiu a0, a0, 0xd10  
; a0 => "mDNSResponderPosix -n \"%s\" -t _http._tcp -p %d -x path=/login.html &"  
0x0043a038 move a1, v0 ; <= a1 points to system name  
0x0043a03c lw a2, (arg_b0h)  
0x0043a040 lw t9, -sym.cmCommand(gp) ; Execute the command  
0x0043a044 nop  
0x0043a048 jalr t9  
0x0043a04c nop  
Mitigating factors:  
-NC210 Cameras have a filter for "bad chars". This means the payload cannot  
contain any of the following characters: dot(.), at(@), dash(-), underscore(_),  
whitespace( ), and single quote(').  
-Some cameras do not ship with telnetd, so other methods such as using wget or  
curl to download a payload from the network might be required to obtain a shell.  
Install firmware updates provided by the vendor to fix the vulnerability.  
The latest updates can be found at the following URLs:  
Disclosure timeline:  
29th March 2020 - Vulnerability reported to vendor.  
10th April 2020 - Patched firmware provided by vendor for verification.  
10th April 2020 - Confirmed the vulnerability was fixed.  
29th April 2020 - Firmware updates released to the public.  
29th April 2020 - Vulnerability details are made public.