GitHub Advisoryadvisory · Packagist
GHSA-ffr8-fxhv-fv8h AVideo is Vulnerable to SQL Injection through Subscribe Endpoint via Unsanitized user_id Parameter
HIGH
Official page ↗
- Aliases
- CVE-2026-33723
- Ecosystem
- Packagist
- Packages
- wwbn/avideo
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N
- Weaknesses
- CWE-89
- Reviewed
- 2026-03-25
Affected packages
• Packagist wwbn/avideo: <= 26.0 (no fix yet)
Description
Summary
The `Subscribe::save()` method in `objects/subscribe.php` concatenates the `$this->users_id` property directly into an INSERT SQL query without sanitization or parameterized binding. This property originates from `$_POST['user_id']` in both `subscribe.json.php` and `subscribeNotify.json.php`. An authenticated attacker can inject arbitrary SQL to extract sensitive data from any database table, including password hashes, API keys, and encryption salts.
Details
The vulnerability exists because of a disconnect between where `intval()` is applied and where the value is used in SQL.
Entry points — `objects/subscribe.json.php:40` and `objects/subscribeNotify.json.php:23`:
```php
// subscribe.json.php line 40
$subscribe = new Subscribe(0, $_POST['email'], $_POST['user_id'], User::getId());
```
Constructor stores raw value — `objects/subscribe.php:34`:
```php
public function __construct($id, $email = "", $user_id = "", $subscriber_users_id = "")
{
// ...
$this->users_id = $user_id; // Raw $_POST['user_id'], no sanitization
$this->subscriber_users_id = $subscriber_users_id;
if (empty($this->id)) {
$this->loadFromId($this->subscriber_users_id, $user_id, "");
}
}
```
`getSubscribeFromID` sanitizes local copies only — `objects/subscribe.php:137-139`:
```php
public static function getSubscribeFromID($subscriber_users_id, $user_id, $status = "a"){
$subscriber_users_id = intval($subscriber_users_id); // Local variable only
$user_id = intval($user_id); // Local variable only — $this->users_id is NOT affected
```
When `getSubscribeFromID` finds no matching subscription (the attacker simply targets a user_id they haven't subscribed to), `loadFromId()` returns false. The object's `$this->id` remains null, and `$this->users_id` retains the unsanitized injection payload.
Vulnerable sink — `objects/subscribe.php:88`:
```php
public function save()
{
if (!empty($this->id)) {
// UPDATE path (not reached when $this->id is null)
} else {
$this->status = 'a';
$sql = "INSERT INTO subscribes (users_id, email, status, ip, created, modified, subscriber_users_id)
VALUES ('{$this->users_id}', ..."; // Direct concatenation of injected value
}
$saved = sqlDAL::writeSql($sql); // Called with NO $formats or $values
```
`sqlDAL::writeSql` provides no protection — `objects/mysql_dal.php:102`:
When called without `$formats`/`$values` parameters (as `save()` does), the `eval_mysql_bind()` function at line 636 returns `true` without binding any parameters. The already-concatenated SQL string is passed directly to `$global['mysqli']->prepare()` and `execute()`, executing the injection as the prepared statement itself.
PoC
Prerequisites: An authenticated session on the target AVideo instance.
Step 1: Confirm injection with time-based blind SQLi
```bash
Pick a user_id that the current user has NOT subscribed to (e.g., 99999)
The SLEEP(5) will cause a ~5 second delay confirming injection
curl -s -o /dev/null -w "%{time_total}" \
-b 'PHPSESSID=VALID_SESSION_ID' \
-d "user_id=99999'+AND+SLEEP(5)+AND+'1" \
https://target/objects/subscribe.json.php
Expected: ~5 second response time (vs users_id` before use in the constructor, or better yet, use parameterized queries in `save()`.
Option 1 — Sanitize in constructor (minimal fix):
```php
// objects/subscribe.php, constructor (line 34)
• $this->users_id = $user_id;
+ $this->users_id = intval($user_id);
```
Option 2 — Use parameterized query in save() (recommended):
```php
// objects/subscribe.php, save() method (lines 87-90)
public function save()
{
global $global;
if (!empty($this->id)) {
$sql = "UPDATE subscribes SET status = ?, notify = ?, ip = ?, modified = now() WHERE id = ?";
$saved = sqlDAL::writeSql($sql, "sssi", [$this->status, $this->notify, getRealIpAddr(), $this->id]);
} else {
$this->status = 'a';
$sql = "INSERT INTO subscribes (users_id, email, status, ip, created, modified, subscriber_users_id) VALUES (?, ?, ?, ?, now(), now(), ?)";
$saved = sqlDAL::writeSql($sql, "isssi", [intval($this->users_id), $this->email, $this->status, getRealIpAddr(), intval($this->subscriber_users_id)]);
}
```
Option 2 is strongly recommended as it also fixes the unsanitized `$this->email`, `$this->status`, and `getRealIpAddr()` values in both the INSERT and UPDATE paths, preventing any future injection through those fields.
Related
Alias · CVE (1)
- CVE-2026-33723 WWBN AVideo is an open source video platform. vulnerability
Weakness · CWE (1)
- CWE-89 Improper Neutralization of Special Elements used in an SQL Command weakness