Remotely Exploiting IoT Pet Feeders

Multiple vulnerabilities in Skymee Petalk AI and Petwant PF-103

Shaun Mirani
Independent Security Evaluators

--

Independent Security Evaluators (ISE) has discovered nine vulnerabilities in two IoT pet feeders: Skymee’s Petalk AI and Petwant’s PF-103. Marketed to owners of dogs and cats, these devices feature a camera and treat dispenser, controllable from their respective mobile apps.

ISE identified the flaws in firmware version 3.2.2.30 of the Petalk AI and 4.3.2.50 of the PF-103. We initially contacted both vendors between March and April 2019 in an attempt to conduct coordinated disclosure. However, as of November 2019, Petwant has only resolved CVE-2019-16734, while Skymee has not addressed any of the vulnerabilities.

Our findings are exploitable with the default configuration, and many allow remote attackers to execute arbitrary code with root privileges if certain ports are exposed. All disclosed issues are common to both devices due to their use of highly similar firmware. Since all of the unpatched vulnerabilities allow remote code execution without authentication, ISE strongly recommends that owners of these devices keep them behind a firewall and avoid opening any ports to the outside world.

Below are timelines of ISE’s communication with each vendor.

Skymee Disclosure Timeline

  • March 19, 2019: Emailed Skymee at
    wholesalesa@skymee.com. Inquired as to preferred medium for disclosure.
  • March 22, 2019: After no response from Skymee, sent vulnerability report in email attachment.
  • September 7, 2019: Followed up on previous disclosure and sent over the details for another vulnerability.

Petwant Disclosure Timeline

  • April 25, 2019: Emailed Petwant at service@petwant.com. Inquired as to preferred medium for disclosure.
  • April 25, 2019: Petwant requested findings over email.
  • April 25, 2019: Sent vulnerability report in email attachment.
  • September 7, 2019: Followed up on previous disclosure and sent over the details for another vulnerability.

Findings

Firmware Updates Over Unencrypted HTTP (CVE-2019-16732)

To check for and download firmware updates, the Petalk AI and PF-103 reach out to either 112.124.112.116 or 47.254.22.43 over plaintext HTTP. Furthermore, the devices perform no cryptographic validation of their own to ensure the integrity and authenticity of firmware downloads.

Unencrypted firmware upgrade traffic.

The lack of transit encryption in the upgrade process allows man-in-the-middle attackers to modify firmware images while they are in flight, resulting in the ability to execute arbitrary code on the pet feeder.

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: This vulnerability has not been patched in the PF-103.

Telnet Enabled with Default Credentials (CVE-2019-16734)

Both devices start a Telnet server on boot that prompts for a username and password:

$ telnet 192.168.2.3
Trying 192.168.2.3...
Connected to 192.168.2.3.
Escape character is '^]'.
IPCAM login:

It is possible to enumerate valid users and their password hashes by inspecting the firmware image’s /etc/shadow file, whose contents are shown below.

root:FCb/N1tGGXtP6:10957:0:99999:7:::
daemon:*:14576:0:99999:7:::
bin:*:14576:0:99999:7:::
sys:*:14576:0:99999:7:::
sync:*:14576:0:99999:7:::
ftp:*:14576:0:99999:7:::
nobody:*:14576:0:99999:7:::

The root user’s hash includes neither an $id or $salt value. According to the manual entry for crypt(3), a password entry without an $id prefix indicates that the hashing algorithm is the original DES-based one. The man page also warns that DES-crypt is vulnerable to brute-force attacks of its key space:

Warning: the key space consists of 2**56 equal 7.2e16 possible
values. Exhaustive searches of this key space are possible using
massively parallel computers. Software, such as crack(1), is
available which will search the portion of this key space that is
generally used by humans for passwords. Hence, password selection
should, at minimum, avoid common words and names.

However, there was an easier method available at the time of my research. By Googling the hash string “FCb/N1tGGXtP6”, I found that a helpful GitHub user had already cracked this password for a different manufacturer’s device:

I was then able to provide the credentials root:059AnkJ to access a shell via Telnet:

$ telnet 192.168.2.3
Trying 192.168.2.3...
Connected to 192.168.2.3.
Escape character is '^]'.
IPCAM login: root
Password:

BusyBox v1.20.2 (2016-08-18 18:04:09 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
skrip profile....
~ # pwd
/root

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: Version 4.22.2.42 of the PF-103 keeps the same DES-hashed password but disables Telnet by default, effectively mitigating this vulnerability.

No Authentication on udpServerSys Service (CVE-2019-16731)

The Petalk AI and PF-103 run a UDP server on 0.0.0.0:9003 called udpServerSys. It links a shared library called libcommon.so to provide a custom (but straightforward) binary protocol that requires no authentication.

While I never saw any communication between the mobile apps and either of the pet feeders on this port, the server is equipped to handle requests that modify device settings. The below decompilation of parseCommand() in libcommon.so shows a few of the commands that udpServerSys accepts.

Several commands accepted by udpServerSys (some IDs are unused).

As no authentication is required to interface with the server, remote attackers can use functionality (and exploit the attack surface) in these handlers if port 9003 is exposed. The following six vulnerabilities take advantage of this fact to gain RCE in the udpServerSys binary.

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: This vulnerability has not been patched in the PF-103.

Command Injection in processCommandSetMac() (CVE-2019-16737)

Inspecting the decompiled handler for command ID 1, processCommandSetMac(), we can see that the function runs a shell command to set the NVRAM value of ethaddr in the uboot zone (lines 27 and 28 below).

processCommandSetMac() passes user input to the shell without validation or sanitization.

The command receives a single shell argument (which param_7 + 1 points to) for the interface’s new MAC address. However, the user controls this value, and neither udpServerSys nor libcommon.so validates its format beforehand.

This appeared to be a clear instance of command injection, so I wrote a Python script that connects to port 9003 on the pet feeder and sends a “Set MAC” request with a malicious command instead of a MAC address. The following proof of concept breaks out of the hardcoded /usr/sbin/nvram_set command context to start telnetd on port 1337.

Proof-of-concept script for CVE-2019-16737.

Attackers can then use this vulnerability to run telnet and gain a root shell on the device without authentication.

$ ./CVE-2019–16737.py 192.168.2.3
$ telnet 192.168.2.3 1337
Trying 192.168.2.3…
Connected to 192.168.2.3.
Escape character is ‘^]’.
BusyBox v1.20.2 (2016–08–18 18:04:09 CST) built-in shell (ash)
Enter ‘help’ for a list of built-in commands.
/ #

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: This vulnerability has not been patched in the PF-103.

Command Injection in processCommandSetUid() (CVE-2019-16733)

This vulnerability is very similar to the previous command injection in CVE-2019-16737. It stems from another udpServerSys command called “Set UID”, which has an ID of 3 in the custom protocol. The decompiled form of processCommandSetUid() is given below.

Decompilation of processCommandSetUid().

Lines 27 and 28 contain the same form of vulnerability as CVE-2019-16737: a call to system_cmd() to update NVRAM storage settings with user-supplied input. By adjusting the previous PoC to send the little-endian integer representation of 3 for the command ID, we can adapt it to exploit the command injection in processCommandSetUid() instead of processCommandSetMac().

Proof-of-concept script for CVE-2019-16733.

After running the above script, attackers connect to Telnet on port 1338 to gain a root shell without authentication.

$ ./CVE-2019-16733.py 192.168.2.3
$ telnet 192.168.2.3 1338
Trying 192.168.2.3...
Connected to 192.168.2.3.
Escape character is '^]'.
BusyBox v1.20.2 (2016-08-18 18:04:09 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/ #

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: This vulnerability has not been patched in the PF-103.

Command Injection in processCommandUpgrade() (CVE-2019-16730)

The “Upgrade” command of udpServerSys, defined in libcommon.so, allows remote firmware upgrades of the Petalk AI or PF-103. The processCommandUpgrade() handler takes string arguments for the upgrade type (“SYS” or “UI”) and the filename of a firmware archive (the archive must already exist on the filesystem). It then starts a thread to perform the upgrade in the background, passing the string arguments to the thread in a single C structure (update_info1).

Lines 25 and 26 of the decompiled output show processCommandUpgrade() creating a new thread for a routine called thread_Upgrade().

processCommandUpgrade() starts a thread to perform a system upgrade.

Within thread_Upgrade, the application constructs a shell command string for /usr/sbin/upgrade.sh with the two user-supplied arguments and sends it to system_cmd() (lines 9 through 11, below).

The thread_Upgrade() routine contains a command injection vulnerability.

This results in another instance of exploitable command injection. The only limitation is that the payload must fit within 24 characters, due to the call to memcpy() with a length argument of 0x18 on line 25 of processCommandUpgrade(). The Python proof of concept below uses netcat to start a bind shell on port 1339.

Proof-of-concept script for CVE-2019-16730.

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: This vulnerability has not been patched in the PF-103.

Command Injection (CVE-2019-17364) and Stack Buffer Overflow (CVE-2019-16735) in processCommandUploadLog()

The “Upload Log” command (ID: 0x21) of udpServerSys allows remote users to upload a specified log file from the device to the HTTP server at 112.124.112.11:80. Line 61 of the function processCommandUploadLog() calls sprintf() to write the necessary curl command into a 384-byte local array (acStack432) for execution by system().

processCommandUploadLog() is vulnerable to command injection and a stack buffer overflow.

In the function above, param_7 is a pointer to the user-controlled log filename. The use of system() and sprintf() without any sort of input validation results in two vulnerabilities affecting processCommandUploadLog(): command injection and a stack-based buffer overflow.

The following Python proof of concept exploits the instance of command injection to start a Telnet server on port 1340.

Proof-of-concept script for CVE-2019-17364.

For the buffer overflow, the script below sends a 512-byte payload as the log filename, crashing udpServerSys with a segmentation fault.

Proof-of-concept script for CVE-2019-16735.

While address space layout randomization (ASLR) is present, the program A) immediately restarts upon crashing and B) uses only a 32-bit address space. As a result, a brute-force bypass of ASLR to perform return-oriented programming (ROP) is likely feasible. Furthermore, ASLR does not apply to the text segment because udpServerSys is not a position-independent executable (PIE) — any gadgets within the main executable are located at predictable addresses.

Skymee Remediation: These vulnerabilities have not been patched in the Petalk AI.

Petwant Remediation: These vulnerabilities have not been patched in the PF-103.

Stack Buffer Overflow in processCommandUploadSnapshot() (CVE-2019-16736)

The “Upload Snapshot” command (ID: 0x19) of udpServerSys uploads a user-specified file on the feeder to the HTTP server at 112.124.112.116. Line 142 of processCommandUploadSnapshot() uses sprintf() to construct a curl command string (only for debugging output; not actually executed) that represents this upload.

A stack buffer overflow in the processCommandUploadSnapshot() function.

Because sprintf() is writing to a 400-byte local array (acStack456) with user input as one of the arguments (param_7), this function is vulnerable to a stack-based buffer overflow. The PoC below sends a 512-byte UDP payload that overflows the array and crashes udpServerSys with a segmentation fault (the service immediately restarts).

Proof-of-concept script for CVE-2019-16736.

As with the issue detailed in CVE-2019-16735, attackers can most likely exploit this vulnerability to gain remote code execution.

Skymee Remediation: This vulnerability has not been patched in the Petalk AI.

Petwant Remediation: This vulnerability has not been patched in the PF-103.

Sign up to get our latest blogs.

Shaun Mirani is a Security Analyst at Independent Security Evaluators, a firm of security specialists that provide a wide range of services including custom security assessments and software development. ISE also runs IoT Village, which hosts talks by expert security researchers who dissect real-world exploits and hacking contests consisting of off-the-shelf IoT devices.

Twitter: @ISESecurity

--

--