CVE-2021-42840 SuiteCRM RCE Log File Extension 2
CVE-2021-42840 This one will be a bit short, since severity/impact/video/etc is all identical to my post on the previous SuiteCRM RCE.
It’s been a bit since I spent some time looking for a web vuln… And this one was a great one to come back to.
This vulnerability allowed me to use a feature (which I later found was not needed any longer) that I found just by browsing the file system in the web root looking for interesting files. I identified a file named /main/upload/upload.php
and started tampering with it. Through source code review, I identified a parameter that did not check user input which allowed me to specify any location on the file system through directory traversal. I could then upload to any location on the file system where the user running the web server process could write to.
Since I had complete control over the contents of the file, I next located a few places in the web root where I could write files (most directories were owned by root and restricted to root:root
for writing, so I had limited options). One of them was not protected by the included .htaccess
file, so that was a candidate for code execution.
The last hurdle was bypassing the php2phps()
function that would rename php
files and many of the variations to phps
. However, phar
was not renamed which enabled me to upload arbitrary php code and execute it as shell.phar
.
I then had all of the pieces to achieve remote code execution from the perspective of an authenticated administrative user and was able to get a reverse shell as the www-data
user. Shortly after, I wrote a full Proof of Concept in python and submitted a detailed report to security@chamilo.org, which I located through their security issues wiki page.
Chamilo was, by far, the most responsive organization I have ever dealt with for disclosing a vulnerability. Other organizations should view them as a model of how to handle a vulnerability being responsibly disclosed. Within two hours of me sending the report(at ~06:30 AM), they already had two patches pushed to GitHub and requested that I test them.
Ubuntu Server 20.04.2 LTS (Focal Fossa)
Server version: Apache/2.4.41 (Ubuntu)
Server built: 2020-08-12T19:46:17
https://github.com/chamilo/chamilo-lms/tree/b17b552e76e1c3b781a6a42c471a647d4e9b9f90
I discovered this through source code analysis while hunting for interesting files. I came across /main/upload/index.php
and identified a parameter curdirpath
that was not sufficiently sanitized.
if (isset($_POST['curdirpath'])) {
$path = Security::remove_XSS($_POST['curdirpath']);
} else {
$path = '/';
}
/main/upload/upload.document.php#L22
From there, the input is passed through remove_XSS()
located here:
/main/inc/lib/security.lib.php#L303
This originally tipped me off, but if you look further down on upload.document.php
, you’ll see that curdirpath
is passed directly to handle_uploaded_document()
, which resides in fileUpload.lib.php
$new_path = handle_uploaded_document(
$_course,
$_FILES['user_upload'],
$base_work_dir,
$_POST['curdirpath'],
api_get_user_id(),
api_get_group_id(),
$to_user_id,
$_POST['unzip'],
$_POST['if_exists']
);
/main/upload/upload.document.php#L53
So, that other stuff didn’t even matter, but it was enough to get me to see what happened if I posted ../../../../../../../tmp/
for curdirpath. XD
To continue, handle_uploaded_document()
does a lot of stuff… curdirpath
is passed in as the argument $uploadPath
. The important thing is that there is no validation of the input prior to utilization here:
$whereToSave = $documentDir.$uploadPath;
/main/inc/lib/fileUpload.lib.php#L318
It is verified that the file exists and is a directory, or if it doesn’t, then create the directory.
Then, after the filename is sanitized, $whereToSave
is used in combination with $fileSystemName
to create the variable $fullPath
. Still, with no checking of the original value input through curdirpath
. $filepath
is also created using the original unchecked curdirpath
value.
$fullPath = $whereToSave.$fileSystemName;
// Example: /folder/picture.jpg
$filePath = $uploadPath.$fileSystemName;
Then a switch
statement determines whether to overwrite or rename based on whether or not the file already exists. For the video demo I do overwrite, but for the POC, I just leave the selection blank because it defaults to only save if the file doesn’t exist. That’s fine, because I generate a random name that probably doesn’t exist. The logic is pretty simple on how the file is put in place.
if (moveUploadedFile($uploadedFile, $fullPath)) {
chmod($fullPath, $filePermissions);
/main/inc/lib/fileUpload.lib.php#L386
Long story short here
Chamilo sanitizes the filename using the php2phps()
function, which essentially uses regex to detect the usual suspects (php[234567], phtml) and changes the filename to .phps
.
function php2phps($file_name)
{
return preg_replace('/\.(php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
}
/main/inc/lib/fileUpload.lib.php#L25
This is called through another function disable_dangerous_file()
here: /main/inc/lib/fileUpload.lib.php#L300
The important catch, they forgot about .phar
file types, which execute php code if you visit them in browser. So, that was my filetype of choice to upload.
### Finding a Place to Upload that will Execute
Chamilo uses an .htaccess file to return a 403
if the user tries to access a specific file type in specific directories. Ideally, these conditions cover areas where the web server user www-data
has write access. This is a good idea in the event that a user finds a flaw and is able to upload php files.
I found that most of the directories are owned by root. So, I did a find /var/www/html/chamilo-lms -type d -owner www-data 2>/dev/null
to find all of the directories the web server user owned. One that stood out to me was the /web/
directory, as it was owned by www-data
, but only the /web/css/
directory was protected by the htaccess file.
# Prevent execution of PHP from directories used for different types of uploads
RedirectMatch 403 ^/app/(?!courses/proxy)(cache|courses|home|logs|upload|Resources/public/css)/.*\.ph(p[3457]?|t|tml|ar)$
RedirectMatch 403 ^/main/default_course_document/images/.*\.ph(p[3457]?|t|tml|ar)$
RedirectMatch 403 ^/main/lang/.*\.ph(p[3457]?|t|tml|ar)$
RedirectMatch 403 ^/web/css/.*\.ph(p[3457]?|t|tml|ar)$
So, I generally just test this via a shell on the system:
echo '<?php echo `id`;?>' > test.php
And visit it in browser to see if you get the output of the id
command. I found that this worked, so I knew I had a good place to upload a file.
Next, I gave it a shot. This was my manual exploitation methodology.
Video here: chamilo-poc.mp4
[19 APR 2021 06:49] : Initial report sent to security@chamilo.org
[19 APR 2021 09:08] : Response received from Chamilo with two patches to test
[19 APR 2021 20:45] : I confirmed the patches addressed the issues
[28 APR 2021 ] Issue disclosed via Chamilo Security Issues page. They requested that I wait until after 12 MAY 2021 to disclose in order to give users a chance to patch. I was happy to do so.
[30 APR 2021] : CVE-2021-31933 assigned
[13 MAY 2021] : This writeup is released.
CVE-2021-42840 This one will be a bit short, since severity/impact/video/etc is all identical to my post on the previous SuiteCRM RCE.
Path traversal in File Upload leads to Remote Code Execution in Chamilo LMS Overview It’s been a bit since I spent some time looking for a web vuln… And this...
tldr/oneliner ruby -e '"".class.ancestors[3].system("cat /etc/passwd")' Why? So I was doing a bit of reading on SSTI, specifically that of Jinja/python which...
Remediation testing I found another vulnerability during remediation testing, and that writeup can be found here.
TL;DR Just go to the Demo Or, just go to the Demo Round 2 for reverse tunneling Accessing Resources Behind Multiple Resources At some point, you may run into...
How to get a Shell on your Router (hopefully) Vulnerability hunting is hard, and it’s even harder if you don’t have access to the source. Hardware devices ma...