Teampass 220.127.116.11: Unauthenticated SQL Injection12 Dec 2016 by Martin Bednorz
The next gift in our advent calendar reveals security issues in Teampass, a collaborative password manager first published in late 2011. We detected a critical unauthenticated SQL injection and many file inclusions which could have led to many leaked passwords and angry users. The issues were reported and fixed earlier this year.
RIPS was able to analyze the whole project consisting of ~140,000 lines of code in only 25 seconds, uncovering a lot of severe security vulnerabilities. The two main types of issues was SQL injection and file inclusion. Luckily, most of the SQL injections were found in the installation / upgrade functionality and are less severe.
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.
Example 1: Authenticated Blind SQL Injection
This vulnerability is an excellent example of how RIPS is able to follow a complex data-flow and its understanding of PHPs unique features.
The code above is the main culprit of many vulnerabilities within this application. The function
parse_str()1 parses a query string to variables such that, for example, the argument
data=12&var=foo initializes the variables
$data = 12 and
$var = 'foo'. Here, the HTTP query string from the variable
$_SERVER['QUERY_STRING'] is used as an input in line 28 which allows an attacker to create arbitrary variables for the application on a global scale. Then, the function
rest_get() is called that creates a new object of class
table attribute is set to the result of the call to
prefix_table("nested_tree") (see lines 235 and 33). Looking closely at the function definition one can see that the global variable
$pre is prefixed to the table parameter of the function in line 1180. An attacker is now able to alter the contents of the global variable
$pre and thus the table name via simple GET parameters because of the
parse_str() call explained earlier. In addition, the usage of
htmlspecialchars() is not sufficient enough to sanitize against SQL injections, although it would not have mattered in this particular case because there are no quotes that an attacker needs to break out of. RIPS detected the insufficient sanitization due to its context-sensitive taint analysis.
In the end, the tainted table name is used unsanitized in the query executed by
mysqli_query() and can be exploited to read arbitrary values from the database. Since Teampass is a password manager, there is very likely sensitive data found in the database.
Interestingly enough, we were not able to exploit the vulnerability because another SQL injection was triggered every time we changed the global variable
$pre via the GET parameters. Luckily for us, the newly found SQL injection requires no authentication to be exploited and thus is even more critical. The following example of our case study describes the vulnerability.
Example 2: Unauthenticated Blind SQL Injection
The security issue in this example is very similar to the previous one, only that it is much easier to exploit because there is no authentication required. The following code lines are affected.
As previously, the main culprit is the call to the
parse_str() function, enabling an attacker to create arbitrary global variables. For every GET request to the API, the API key has to be checked for correctness using the
apikey_checker() function. Here, same as before, the function
prefix_table() prefixes the table name
api using the global variable
$pre and later executing the query with the
queryOneColumn() method. The attacker is able to alter the
pre variable and to select arbitrary data from the database without any requirements of authentication.
Example 3: File Inclusion
There is also an interesting file inclusion vulnerability using the same entry point as we saw in the examples before located in the following code lines. Many file inclusion operations in the application are similary affected and thus led to a high amount of valid issue reports.
Here, an attacker can set the variable
$_SESSION['settings']['cpassman_dir'] via GET parameters using the
parse_str() function and is thus able to include files unintended by the developer. Depending on the server configuration, it is possible to include arbitrary remote files, rendering this vulnerability highly critical. Luckily, the
require_once statement is only accessible with an authenticated user and the constant
true which is not the default value for the affected version.
Rescan of the fixed version
The diagram above depicts a consolidated statistic of the rescan of the updated version of Teampass (18.104.22.168 22.214.171.124). As described in our previous post, RIPS compares the analysis results of both scans and is able to precisely identify fixed, old, and new issues inside the updated application version. As can be seen in this case, the new version fixed a lot of issues and only introduced one new XSS issue into the code base. You can click on the labels in the legend (Old, Fixed, New) in order to alter the display.
|2016/06/15||First contact with vendor, initiated by vendor|
|2016/06/16||Exchange of details about vulnerabilities|
|2016/06/27||Vendor releases fixed version (changelog)|
Password managers are very sensitive tools and even more fragile when they are deployed as a web application. We found many critical issues due to one PHP built-in function used improperly which is why the precise understanding of all security-relevant PHP functions is a core feature in our analysis engine. Looking at the amount of issues fixed by the vendor, the release time of the updated version was very fast and we want to thank the Teampass developers for their professional and quick cooperation!
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.