Coppermine 1.5.42: Second-Order Command Execution2 Dec 2016 by Martin Bednorz
The second gift in our advent calendar contains descriptions of vulnerabilities in Coppermine, a very popular picture gallery application written in PHP and in active development since 2003. It consists of ~160,000 lines of code (medium-sized web application) and is downloaded roughly 1,200 times per week.
The analysis with RIPS took only 53 seconds to complete and it uncovered a lot of security vulnerabilities - although most of them require authentication. Nonetheless, these issues are severe because they can be combined with other security vulnerabilities that allow an attacker to access an account and to move on from there. Regardless, not all user accounts of Coppermine should be able to inject SQL queries or execute commands on the server.
The truncated analysis results are available in our RIPS demo application. Please note that we limited the results to the issues described in this post in order to ensure a fix is available.See RIPS report
In the following, we examine a few selected vulnerabilities found by RIPS. We prefer to describe more complex and interesting vulnerabilities over straight-forward exploits.
Example 1: Second-Order Command Execution
This example contains two second-order command execution vulnerabilities that work very similar, only differing in the operating system they can be executed in. Lets have a look at the vulnerable code.
In both cases, the config value
$CONFIG['im_options'] is used directly in the sensitive sink
exec(). If an attacker is able to control this value, it allows him to execute arbitrary commands on the server by simply attaching a system command to the previous one using the
; character. In the following, the code is shown that handles the configuration values.
With the help of the
cpg_config_set() function, an administrator can store arbitrary configration values in the config table. The values are correctly escaped in order to prevent SQL injection. During initialization of Coppermine, the values are then loaded from the database and propagated to the
Thus, a malicious administrator can execute arbitrary system commands by performing two steps (second-order vulnerability). First, he alters the
im_options configuration value with a payload that is stored persistently in the database. Second, he uses the picture editor that will load the payload from the database again and inject it into the system command.
RIPS was able to follow this complex data flow through the database by analyzing the writing SQL query
UPDATE and the reading SQL query
SELECT, and then matching table columns detected as taintable. In order to remedy the issue, the command arguments should be sanitized with the built-in PHP function
escapeshellarg(). But how can an attacker abuse this issue remotely without having administrator access and is this issue critical to fix?
Example 2: Non-Authenticated SQL Injection
As we have described in our FreePBX post, cross-site scripting can be used to take over user accounts. Further, RIPS detected several SQL injection vulnerabilities that can be exploited by an attacker to directly retrieve the user credentials from the database.
The SQL injection vulnerability described in the following is difficult to exploit because it requires a MD5 hash collision. However, it is a nice example of how the use of insecure hash algorithms can lead to security problems further down the road. Also it demonstrates why not all security issues detected by static code analysis are equally severe (but should be fixed nonetheless). The affected code lies in the forgot password function of Coppermine.
Here, the GET parameter
id is escaped, stored in
$CLEAN['id'], and used securely in the first SQL query in line 39. However, it is used insecurely at the end of the second SQL query in line 47 that does not use quotes around the value. Hence, the escaping is insufficient because an attacker does not need to break out of quotes when injecting new SQL commands. RIPS is able to differentiate between quoted SQL contexts and to decide whether escaping is applied correctly or insufficiently.
In order to exploit this issue, the difficulty is to trigger the injection point because the session id from the database has to match the MD5 hash value of the GET parameter
key concatenated with the payload injected into
id. This is theoretically possible for a determined attacker because he is in possession of his own session id and can find a collision for the MD5 hash built with his data (200 billion hashes per second, no size bounds for the paramter in the source code). MD5 is known to have many issues and one should not count on its long-time security. In addition, due to Moore’s law this will get increasingly easier from year to year. Clearly, though, the exploitation is strongly limited and an attacker would favor a more straight-forward SQL injection.
As with the previous example, the issue can be fixed by quoting the tainted data in the resulting SQL query because it is already in an escaped state.
Example 3: PHP Object Injection
Additionally, RIPS found quite a few object injections which are similar to the one presented in this example, using tainted variables in the
unserialize() function without any kind of validation.
As one can see, the cookie named
$CONFIG['cookie_name'] . '_fav' is base64 decoded and then directly used for deserializing data. The full cookie name can be easily retrieved from the browser and an attacker is able to search for gadget chains in the application code that can further compromise the application and the server it is running on. Although RIPS did not detect gadget chains in the code base for exploitation it is important to note that older installations of PHP (before 5.5.37, 5.6.x before 5.6.23, and 7.x before 7.0.8) are automatically vulnerable to arbitrary code execution due to a security issue in the
unserialize() function itself, rendering this issue very critical for many servers. The deserialization of tainted data should be avoided in general and it is much safer to use similar functions such as
json_decode(). We will cover the exploitation of gadget chains in our upcoming calendar posts in depth.
|2016/09/23||First contact with vendor|
|2016/09/26||Vendor released a fix (1.5.44)|
Cross-site scripting vulnerabilities were the most common issues found by RIPS (~500) which we did not review fully. The most critical issues, as the command execution described in this post, occured due to the fact that configuration values were stored in the database and then later were not sanitized before used in sensitive functions. The fact that such configuration values are commonly not under the control of an attacker (e.g. stored in local configuration files) likely led to a trusted use without further security checks.
All in all, the Coppermine team reacted very quickly and published a fix for the most critical issues only three days after our notification, which is not common at all. We would like to thank the team for their great collaboration.
Follow us on Twitter to be notified when the next gift of our advent calendar is opened!
APAV Time Table
Disclaimer: The information provided here is for educational purposes only. It is your responsibility to obey all applicable local, state and federal laws. RIPS Technologies GmbH assumes no liability and is not responsible for any misuse or damages caused by direct or indirect use of the information provided.