usd-2024-0016 | Vtiger Open Source Edition 8.2.0 - Authenticated Remote Code Execution
Product: Vtiger
Affected Version: Open Source Edition 8.2.0
Vulnerability Type:Unrestricted Upload of File with Dangerous Type (CWE-434)
Security Risk: Critical
Vendor: Vtiger
Vendor URL: https://www.vtiger.com/
Vendor acknowledged vulnerability: Yes
Vendor Status: Fixed
CVE Number: Requested
CVE Link: Requested
Description
Vtiger Open Source Edition 8.2.0 allows low-privileged authenticated users to execute arbitrary code. The calendar module is vulnerable to a path traversal when creating short-lived temporary files. By winning a race condition, these files can be renamed due to an insufficient deny list and made executable, allowing the execution of arbitrary code. The default Docker image is vulnerable, as well as all systems that are configured to evaluate .phar files.
Proof of Concept
In summary, the vulnerability works as follows:
- A path-traversal vulnerability in the calendar module allows creating short-lived .ics files with attacker-controlled name and some content.
- An insufficient deny list for file extensions does not contain .phar, which is executed as PHP code by default in the official docker image.
- The rename operation needs to win a race condition to rename the .ics file before it is deleted again.
Relevant source code is located in modules/Calencar/CalendarCommon.php:
function sendInvitation($inviteesid,$mode,$recordModel,$desc) { global $current_user,$mod_strings; require_once("vtlib/Vtiger/Mailer.php"); $invitees_array = explode(';',$inviteesid); if($desc['mode'] == 'edit') { $subject = vtranslate("LBL_UPDATED_INVITATION", "Calendar").' : '; } else { $subject = vtranslate("LBL_INVITATION", "Calendar").' : '; } $subject .= $recordModel->get('subject'); $attachment = generateIcsAttachment($desc); foreach($invitees_array as $inviteeid) { if($inviteeid != '') { $description=getActivityDetails($desc,$inviteeid,"invite",$recordModel); $description = getMergedDescription($description, $recordModel->getId(), 'Events'); $to_email = getUserEmailId('id',$inviteeid); $to_name = getUserFullName($inviteeid); $mail = new Vtiger_Mailer(); $mail->IsHTML(true); $currentUserModel = Users_Record_Model::getCurrentUserModel(); $userName = $currentUserModel->getName(); $fromEmail = Emails_Record_Model::getFromEmailAddress(); $mail->ConfigSenderInfo($fromEmail,$userName); $mail->Subject = $subject; $mail->Body = $description; $mail->AddAttachment($attachment, '', 'base64', 'text/calendar'); $mail->SendTo($to_email, decode_html($to_name), false, false, true); } } unlink($attachment); }
The function triggers the generation of an ics file in Line 161 and, after sending it via email to invited users, deletes it in Line 180.
function generateIcsAttachment($record) { $fileName = str_replace(' ', '_', decode_html($record['subject'])); $assignedUserId = $record['user_id']; $userModel = Users_Record_Model::getInstanceById($assignedUserId, 'Users'); $userLabel = $userModel->entity->column_fields['userlabel']; $email = $userModel->entity->column_fields['email1']; $fp = fopen('test/upload/'.$fileName.'.ics', "w"); fwrite($fp, "BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\n"); fwrite($fp, "ORGANIZER;CN=".$userLabel.":MAILTO:".$email."\n"); fwrite($fp, "DTSTART:".date('Ymd\THis\Z', strtotime($record['st_date_time']))."\n"); fwrite($fp, "DTEND:".date('Ymd\THis\Z', strtotime($record['end_date_time']))."\n"); fwrite($fp, "DTSTAMP:".date('Ymd\THis\Z')."\n"); fwrite($fp, "DESCRIPTION:".$record['description']."\nLOCATION:".$record['location']."\n"); fwrite($fp, "STATUS:CONFIRMED\nSUMMARY:".$record['subject']."\nEND:VEVENT\nEND:VCALENDAR"); fclose($fp); return 'test/upload/'.$fileName.'.ics'; }
The function generateIcsAttachment is responsible for creating the ics file. It uses the subject of an event as part of the filename (c.f. Line 225) and uses it without validation or sanitization as file name in Line 230. Parts of the created file are controllable, e.g., via the description field in Line 236.
In combination, this allows the creation of a short-lived .ics file with dangerous contents.
A default installation of Vtiger contains KCFinder in version 2.21 at /kcfinder/browse.php. Among other functionality available to authenticated users, it allows renaming of files that are stored in its path. In the default docker installation, the controllable path is /app/test/upload/images.
POST /kcfinder/browse.php?type=images&lng=en&act=rename HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 63
Cookie: PHPSESSID=[... REDACTED ...]; dir=images&file=Event.ics&newName=Event.ics.phar&something=FUZZ
The HTTP request above renames Event.ics to Event.ics.phar. Most dangerous extensions are blocked, but .phar is not in the default deny list.
Note that PHP does only execute one request in parallel for each session ID. Hence, the cookie session ID used in the request above should be used only for the rename operation and not reused for the event creation. Two different sessions by the same user work.
In order to hit the race condition reliably, the rename operation can be performed in quick succession with ffuf --request Rename.req --request-proto http --input-cmd 'true' --input-num 100000 --fr 'Unknown error' -t 120.
With ffuf running in the background, an event is created as shown in the following screenshot.
The subject of the event specifies the path in which the temporary .ics file will be created. To create it inside the folder KCFinder is able to control, it suffices to place the file in the images directory. The description field contains the PHP code that will be executed.
If the race condition is hit, the file is renamed as shown in the following screenshot. Winning the race condition worked first try in most attempts.
When opening the file, the PHP code is executed.
Fix
Multiple separate adjustments are recommended for an in-depth fix:
- The .phar extension should be added to the deny list.
- Execution of .phar files via the PHP interpreter should be disabled.
- The name of an event should not be used inside the path to store the .ics file. Instead, creating a proper temporary file is recommended.
Users of Vtiger should upgrade to a patched version.
References
- https://www.vtiger.com/open-source-crm/download-open-source/
- https://hub.docker.com/r/vtigercrm/vtigercrm-8.2.0
Timeline
- 2024-09-24: Initial contact request to Vtiger
- 2024-10-14: Sent reminder via email
- 2024-11-25: Sent follow up email and opened security issue on Vtiger's GitLab instance
- 2025-01-29: Received contact information for disclosing vulnerabilties
- 2025-01-29: Disclosed findings via provided contact address
- 2025-03-03: usd AG reviewed implementation of suggested fixes and provided further guidance to strengthen Vtiger's security
- 2025-06-02: This advisory is published
Credits
This security vulnerability was identified by Florian Dewald, Tim Kranz, and Ole Wagner of usd AG.