Talos Vulnerability Report

TALOS-2017-0352

June 19, 2017

CVE Number

CVE-2017-2850

Summary

An exploitable injection vulnerability exists in the web management interface used by the Foscam C1 Indoor HD Camera running application firmware 2.52.2.37. A specially crafted HTTP request can allow for a user to inject arbitrary characters in the pureftpd.passwd file during a username change, which in turn allows for bypassing chroot restrictions in the FTP server. An attacker can simply send an HTTP request to the device to trigger this vulnerability.

Tested Versions

Product URLs

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

Details

Foscam produces a series of IP-capable surveillance devices, network video recorders, and baby monitors for the end-user. Foscam produces a range of cameras for both indoor and outdoor use and with wireless capability. One of these models is the C1 series which contains a web-based user interface for management and is based on the ARM architecture. Foscam is considered one of the most common security cameras out on the current market.

When various services are started, a service will first register a callback using the CMsgClient::registerMsgHandle function [1]. This will register a function to be called [2] when another service dispatches a message of the specified code [3]. An example of this registration process is handled inside the FCGI_Init function of the "CGIProxy.fcgi" service using the following code:

After the "CGIProxy.fcgi" service decodes an http request that's forwarded from the http daemon, the service will copy the decoded query into a buffer on the stack [4]. Once this is done, the buffer will then be used to pass the decoded query to CMsgClient::sendMsg. This will dispatch the query to the shared messaging subsystem using the code 0x4001 at [5]. At this point, the service that handles the specified code will be woken up to handle the specified request.

The handler for code 0x4001 is in the "webService" binary and is done by the function executeCGICmd at address 0x1e5a4. At the beginning of this function, the service will call a function [6] that's responsible for extracting the user name, password, and command that was specified within the user's query. Once the parameters have been extracted and copied into a local buffer on the stack, the command will be passed to the function call at [7] in order to determine the correct command function which is stored to funcptr. If authentication is not required for the command, then the branch at [8] will execute the function pointer returned by findJsonCallbackCommand at [7]. If authentication is required from the command, then the user name and password will be checked via strcmp and then the function call at [9] will execute the function pointer.

When handling the "CGIProxy.fcgi" command "changeUserName", the function changeUserName_39544 will be called. This function is responsible for changing the username of an existing user account. At the beginning of the function, the parameters [10] for "usrName", "newUsrName", "usr", and "callbackJson" are extracted from the query.
The function then checks that the user (given by the "usr" parameter) either has privilege 2 [11] (i.e. administrator) or that the account that he's changing the username for is his [12]. This last check is useless since this command is only allowed for users with privilege 2.
Finally it checks that the "usrName" [13] and "newUsrName" [14] parameters are not null and passes them to the changeAccountUsername_1d6e8 function [15].

changeAccountUsername_1d6e8 first checks that "newUsrName" doesn't already exists [16] and that "usrName" exists [17], using a loop over the user-account object that was passed via r0. Then the new username is saved in the file "/mnt/mtd/app/config/UserAccountConfig.bin" [18].

The last operation needed to successfully update the username is an update of the FTP database. To do this, the function first ensures that the user's privilege is 2, then it opens the "pureftpd.passwd" [20] and reads its whole content in a local variable [21].
Then the username line of interest is searched with strstr [22] using "\n%s", where "%s" is the old username that has to be changed. The pointer to the old username is saved in r5 [23], and the "passwd" contents are split before r5 [24] by copying the preceding contents in a buffer [25]. The function then searches for ":" [26] (i.e. the pointer to the rest of the file after the old user name). The new "passwd" file is then rebuilt using snprintf [27] and concatenating the buffer [25], the new username and the contents after the old username [26].
Finally the "pure-pw mkdb" [28] command is ran to apply the database modifications without restarting the FTP daemon.
Up until this point no checks are performed on the contents of the "newUsrName" parameter, which allows for arbitrarily injecting any characters in the "pureftpd.passwd" file.

By injecting the character ":" it's possible to add new fields to a line in the "passwd" file. This allows for modifying the uid, gid and directory used for chroot.
Nevertheless, when using the "puredb" authentication method, "pure-ftpd" doesn't allow to log-in using uid 0, so the uid with highest privilege that can be used in this injection is 1000.
By default the uid set for ftp users is 1001, which only has permission to write inside "/mnt/sd". Whereas users with uid 1000 have permission to write almost anywhere in the filesystem. This can be leveraged by an attacker to escalate privileges to root.

Exploit Proof-of-Concept

This vulnerability is reachable by the "changeUserName" command and requires a valid user account with privilege level 2. For clarity, the following proof-of-concept adds a new account with privilege level 2 before exploiting the vulnerability.