GHSA-73gr-r64q-7jh4: AVideo has User Group-Based Category Access Control Bypass via Missing and Broken Group Filtering in categories.json.php
SummaryThe `categories.json.php` endpoint, which serves the category listing API, fails to enforce user group-based access controls on categories. In the default request path (no `?user=` parameter), user group filtering is entirely skipped, exposing all non-private categories including those restricted to specific user groups. When the `?user=` parameter is supplied, a type confusion bug causes the filter to use the admin user's (user_id=1) group memberships instead of the current user's, rendering the filter ineffective.DetailsThe vulnerability has two related failures in `objects/categories.json.php` and `objects/category.php`:**1. Default request — group filtering completely skipped**In `categories.json.php:17-24`, when `$_GET['user']` is not set, `$sameUserGroupAsMe` defaults to `false`:In `category.php:438-452`, the user group filter is gated on `$sameUserGroupAsMe` being truthy:Since `$sameUserGroupAsMe = false`, the entire block is skipped. All non-private categories are returned regardless of their user group restrictions set via the `categories_has_users_groups` table.**2. With `?user=` parameter — boolean-to-integer type confusion**When `$_GET['user']` is non-empty, `$sameUserGroupAsMe` is set to boolean `true` (line 21). This value is passed to `UserGroups::getUserGroups($sameUserGroupAsMe)` at `category.php:440`.In `userGroups.php:349-379`, the parameter is used as `$users_id`:PHP casts boolean `true` to integer `1` for the prepared statement bind, resulting in `WHERE users_id = 1` — fetching the **admin user's** group memberships. The filter then allows categories visible to admin groups, effectively granting any unauthenticated user the admin's category visibility.**3. getTotalCategories also unfiltered**`getTotalCategories()` at `category.php:978` does not accept a `$sameUserGroupAsMe` parameter at all, so the total count always reflects the unfiltered category set.The endpoint requires no authentication — it uses `allowOrigin()` (a CORS header helper) and is publicly routable via the `.htaccess` rewrite rule: `RewriteRule ^categories.json$ objects/categories.json.php`.PoCImpactAny unauthenticated user can:**Enumerate all non-private categories** regardless of user group restrictions, bypassing the intended access control model where categories are restricted to specific user groups via the CustomizeUser plugin's `categories_has_users_groups` table.**Discover the user group configuration** for each category via the `users_groups_ids_array` field in the response, revealing the internal access control structure.**Identify group-restricted content areas** that should be hidden, which could be used to target further access control bypasses on the videos within those categories.The severity is Medium because this is an information disclosure of category metadata (names, descriptions, icons, group assignments) rather than the actual video content within restricted categories. However, the exposure of the access control structure itself (which groups have access to which categories) is a meaningful information leak.Recommended FixIn `objects/categories.json.php`, pass the current user's ID (or 0 for unauthenticated users) instead of a boolean:Additionally, in `category.php:getAllCategories()`, ensure the group filter block always runs when categories have group restrictions, not only when `$sameUserGroupAsMe` is truthy. A more robust approach:This ensures that even when no user is logged in, categories with group restrictions are hidden (only categories with zero group restrictions are shown). The `getTotalCategories()` function should also be updated to accept and apply the same `$sameUserGroupAsMe` filter.
CVSS v3.1: 5.3