GitHub Security Advisory: GHSA-8x77-f38v-4m5j
Package: wwbn/avideo (COMPOSER)
Severity: HIGH (CVSS 7.6)
CWE: CWE-863: Incorrect Authorization
Vulnerable versions: <= 26.0
Patched in: unpatched

Summary: AVideo: Video Moderator Privilege Escalation via Ownership Transfer Enables Arbitrary Video Deletion

Details:
## Summary

A user with the "Videos Moderator" permission can escalate privileges to perform full video management operations — including ownership transfer and deletion of any video — despite the permission being documented as only allowing video publicity changes (Active, Inactive, Unlisted). The root cause is that `Permissions::canModerateVideos()` is used as an authorization gate for full video editing in `videoAddNew.json.php`, while `videoDelete.json.php` only checks ownership, creating an asymmetric authorization boundary exploitable via a two-step ownership-transfer-then-delete chain.

## Details

The `PERMISSION_INACTIVATEVIDEOS` (ID 11) permission is described as a limited moderator role in `plugin/Permissions/Permissions.php:213`:

```php
$permissions[] = new PluginPermissionOption(
    Permissions::PERMISSION_INACTIVATEVIDEOS, 
    __('Videos Moderator'), 
    __('This is a level below the (Videos Admin), this type of user can change the video publicity (Active, Inactive, Unlisted)'), 
    'Permissions'
);
```

However, `Permissions::canModerateVideos()` (`Permissions.php:175`) is reused as an authorization gate in multiple locations in `videoAddNew.json.php` that go far beyond status changes:

**1. Upload gate bypass** (`videoAddNew.json.php:10`):
`User::canUpload()` (`user.php:2650`) returns `true` if `Permissions::canModerateVideos()` is true, granting moderators upload access.

**2. Edit gate bypass** (`videoAddNew.json.php:19`):
```php
if (!Video::canEdit($_POST['id']) && !Permissions::canModerateVideos()) {
    die('{"error":"2 ' . __("Permission denied") . '"}');
}
```
`Video::canEdit()` correctly checks only `canAdminVideos()` and ownership, but the `|| !Permissions::canModerateVideos()` fallback allows moderators to edit any video.

**3. Ownership transfer** (`videoAddNew.json.php:222`):
```php
if ($advancedCustomUser->userCanChangeVideoOwner || Permissions::canModerateVideos() || 
    Users_affiliations::isUserAffiliateOrCompanyToEachOther($obj->getUsers_id(), $_POST['users_id'])) {
    $obj->setUsers_id($_POST['users_id']);
}
```
`userCanChangeVideoOwner` defaults to `false` (`CustomizeUser.php:286`), but `canModerateVideos()` provides an unconditional bypass, allowing any moderator to reassign ownership of any video.

**4. Delete via ownership** (`videoDelete.json.php:22-28`):
```php
if(empty($video->getUsers_id()) || $video->getUsers_id() != User::getId()){
    if (!$video->userCanManageVideo()) {
        // denied
    }
}
$id = $video->delete();
```
`userCanManageVideo()` (`video.php:3614`) checks `canAdminVideos()` (not `canModerateVideos()`), then falls back to ownership. After the ownership transfer in step 3, the moderator is now the owner, so this check passes.

The authorization asymmetry: `videoAddNew.json.php` treats `canModerateVideos()` as equivalent to `canAdminVideos()`, but `videoDelete.json.php` and `userCanManageVideo()` do not — creating a gap exploitable by transferring ownership first.

Additional field