GitHub Security Advisory: GHSA-mcj5-6qr4-95fj
Package: wwbn/avideo (COMPOSER)
Severity: CRITICAL (CVSS 9.8)
CWE: CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Vulnerable versions: <= 26.0
Patched in: unpatched

Summary: AVideo has an Unauthenticated SQL Injection via `doNotShowCats` Parameter (Backslash Escape Bypass)

Details:
### Summary

An unauthenticated SQL injection vulnerability exists in `objects/category.php` in the `getAllCategories()` method. The `doNotShowCats` request parameter is sanitized only by stripping single-quote characters (`str_replace("'", '', ...)`), but this is trivially bypassed using a backslash escape technique to shift SQL string boundaries. The parameter is not covered by any of the application's global input filters in `objects/security.php`.

### Affected Component

**File:** `objects/category.php`, lines 386-394, inside method `getAllCategories()`

```php
if (!empty($_REQUEST['doNotShowCats'])) {
    $doNotShowCats = $_REQUEST['doNotShowCats'];
    if (!is_array($_REQUEST['doNotShowCats'])) {
        $doNotShowCats = array($_REQUEST['doNotShowCats']);
    }
    foreach ($doNotShowCats as $key => $value) {
        $doNotShowCats[$key] = str_replace("'", '', $value);  // INSUFFICIENT
    }
    $sql .= " AND (c.clean_name NOT IN ('" . implode("', '", $doNotShowCats) . "') )";
}
```

### Root Cause

1. **Incomplete sanitization:** The only defense is `str_replace("'", '', $value)`, which strips single-quote characters. It does **not** strip backslashes (`\`).
2. **No global filter coverage:** The `doNotShowCats` parameter is absent from every filter list in `objects/security.php` (`$securityFilter`, `$securityFilterInt`, `$securityRemoveSingleQuotes`, `$securityRemoveNonChars`, `$securityRemoveNonCharsStrict`, `$filterURL`, and the `_id` suffix pattern).
3. **Direct string concatenation into SQL:** The filtered values are concatenated into the SQL query via `implode()` instead of using parameterized queries.

### Exploitation

MySQL, by default, treats the backslash (`\`) as an escape character inside string literals (unless `NO_BACKSLASH_ESCAPES` SQL mode is enabled, which is uncommon). This allows a backslash in one array element to escape the closing single-quote that `implode()` adds, shifting the string boundary and turning the next array element into executable SQL.

**Step-by-step:**

1. The attacker sends:
   ```
   GET /categories.json.php?doNotShowCats[0]=\&doNotShowCats[1]=)%20OR%201=1)--%20-
   ```

2. After `str_replace("'", '', ...)`, values are unchanged (no single quotes to strip):
   - Element 0: `\`
   - Element 1: `) OR 1=1)-- -`

3. After `implode("', '", ...)`, the concatenated string is:
   ```
   \', ') OR 1=1)-- -
   ```

4. The full SQL becomes:
   ```sql
   AND (c.clean_name NOT IN ('\', ') OR 1=1)-- -') )
   ```

5. MySQL parses this as:
   - `'\'` — the `\` escapes the next `'`, making it a literal quote character inside the string. The string continues.
   - `, '` — the comma and space are part of the string. The next `'` (which was the opening quote of element 1) **closes** the string.
   - String value = `', ` (three characters: quote, comma, space)
   - `) OR 1=1)` — executable SQL. The first `)` closes `NOT IN (`, the second `)` closes the outer `AND (`.
   - `-- -` — SQL comment, discards the remainder `')