Skip to content

Commit bf0bebf

Browse files
1 parent 92659e8 commit bf0bebf

4 files changed

Lines changed: 253 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-2rm7-j397-3fqg",
4+
"modified": "2026-03-29T15:41:33Z",
5+
"published": "2026-03-29T15:41:33Z",
6+
"aliases": [
7+
"CVE-2026-34245"
8+
],
9+
"summary": "AVideo: Missing Authorization in Playlist Schedule Creation Allows Cross-User Broadcast Hijacking",
10+
"details": "## Summary\n\nThe `plugin/PlayLists/View/Playlists_schedules/add.json.php` endpoint allows any authenticated user with streaming permission to create or modify broadcast schedules targeting any playlist on the platform, regardless of ownership. When the schedule executes, the rebroadcast runs under the victim playlist owner's identity, allowing content hijacking and stream disruption.\n\n## Details\n\nThe endpoint at `plugin/PlayLists/View/Playlists_schedules/add.json.php` performs only a capability check, not an ownership check:\n\n```php\n// add.json.php:14 — only checks if user CAN stream, not if they OWN the playlist\nif (!User::canStream()) {\n forbiddenPage(__(\"You cannot livestream\"));\n}\n\n// Line 18-19: attacker-controlled playlists_id is used directly\n$o = new Playlists_schedules(@$_POST['id']);\n$o->setPlaylists_id($_POST['playlists_id']);\n```\n\nThe `Playlists_schedules::save()` method (line 182) only validates that `playlists_id` is non-empty — no ownership check:\n\n```php\npublic function save()\n{\n if(empty($this->playlists_id)){\n _error_log(\"Playlists_schedules::save playlists_id is empty\");\n return false;\n }\n // ...\n return parent::save();\n}\n```\n\nWhen the schedule triggers via `plugin/PlayLists/run.php`, the rebroadcast executes under the **playlist owner's** user ID, not the schedule creator's:\n\n```php\n// run.php:55 — uses playlist owner's ID\n$pl = new PlayList($ps->playlists_id);\n$response = Rebroadcaster::rebroadcastVideo(\n $ps->current_videos_id,\n $pl->getUsers_id(), // <-- victim's user ID\n Playlists_schedules::getPlayListScheduledIndex($value['id']),\n $title\n);\n```\n\nBy contrast, all other playlist modification endpoints properly verify ownership via `PlayLists::canManagePlaylist()`:\n\n```php\n// PlayLists.php:55-68\nstatic function canManagePlaylist($playlists_id)\n{\n if (!User::isLogged()) return false;\n if (self::canManageAllPlaylists()) return true;\n $pl = new PlayList($playlists_id);\n if ($pl->getUsers_id() == User::getId()) return true;\n return false;\n}\n```\n\nThis check is used in `saveShowOnTV.json.php:43`, `playListToSerie.php:24`, and `getPlaylistButtons.php:34`, but is entirely absent from `add.json.php`.\n\nAdditionally, the `delete.json.php` endpoint requires `User::isAdmin()` (line 11), making the disparity with `add.json.php` even clearer.\n\nWhen providing an existing schedule `id` via POST (line 18), no ownership check is performed on the existing schedule record either, allowing modification of any schedule on the platform.\n\n## PoC\n\n```bash\n# Step 1: Authenticate as a normal user with streaming permission\n# Obtain PHPSESSID via login\n\n# Step 2: Create a broadcast schedule targeting another user's playlist\n# Replace VICTIM_PLAYLIST_ID with the target playlist ID (enumerable via API)\ncurl -X POST 'https://target.com/plugin/PlayLists/View/Playlists_schedules/add.json.php' \\\n -H 'Cookie: PHPSESSID=<attacker_session>' \\\n -d 'playlists_id=<VICTIM_PLAYLIST_ID>&name=hijacked&start_datetime=2026-03-26+12:00:00&finish_datetime=2026-03-27+12:00:00&loop=1&repeat=d&parameters={}'\n\n# Expected: {\"error\":false} — schedule created for victim's playlist\n# The schedule will execute via run.php under the victim's user identity\n\n# Step 3: Modify an existing schedule by providing its id\ncurl -X POST 'https://target.com/plugin/PlayLists/View/Playlists_schedules/add.json.php' \\\n -H 'Cookie: PHPSESSID=<attacker_session>' \\\n -d 'id=<EXISTING_SCHEDULE_ID>&playlists_id=<VICTIM_PLAYLIST_ID>&name=modified&start_datetime=2026-03-26+00:00:00&finish_datetime=2026-03-28+00:00:00&loop=1&repeat=d&parameters={}'\n```\n\n## Impact\n\n- **Content hijacking:** Attacker can force-broadcast content from any user's playlist, including private or paid content\n- **Stream disruption:** Scheduled rebroadcast can interfere with a victim's ongoing live streams\n- **Identity abuse:** Rebroadcast executes under the victim's user identity, making it appear the victim initiated the broadcast\n- **Resource consumption:** Scheduled broadcasts consume the victim's server bandwidth allocation\n- **Schedule tampering:** Existing schedules can be modified or redirected by any streaming user\n\n## Recommended Fix\n\nAdd ownership validation in `add.json.php` before saving:\n\n```php\n// After line 16 (canStream check), add:\n$playlists_id = intval($_POST['playlists_id']);\nif (!PlayLists::canManagePlaylist($playlists_id)) {\n forbiddenPage(__(\"You cannot manage this playlist\"));\n}\n\n// When editing existing schedules, also verify ownership of the existing record:\nif (!empty($_POST['id'])) {\n $existing = new Playlists_schedules(intval($_POST['id']));\n if (!PlayLists::canManagePlaylist($existing->getPlaylists_id())) {\n forbiddenPage(__(\"You cannot modify this schedule\"));\n }\n}\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L"
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+
"last_affected": "26.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-2rm7-j397-3fqg"
42+
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34245"
46+
},
47+
{
48+
"type": "WEB",
49+
"url": "https://github.com/WWBN/AVideo/commit/1e6dc20172de986f60641eb4fdb4090f079ffdce"
50+
},
51+
{
52+
"type": "PACKAGE",
53+
"url": "https://github.com/WWBN/AVideo"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-862"
59+
],
60+
"severity": "MODERATE",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-03-29T15:41:33Z",
63+
"nvd_published_at": "2026-03-27T17:16:30Z"
64+
}
65+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-g3hj-mf85-679g",
4+
"modified": "2026-03-29T15:41:44Z",
5+
"published": "2026-03-29T15:41:44Z",
6+
"aliases": [
7+
"CVE-2026-34247"
8+
],
9+
"summary": "AVideo: IDOR in uploadPoster.php Allows Any Authenticated User to Overwrite Scheduled Live Stream Posters and Trigger False Socket Notifications",
10+
"details": "## Summary\n\nThe `plugin/Live/uploadPoster.php` endpoint allows any authenticated user to overwrite the poster image for any scheduled live stream by supplying an arbitrary `live_schedule_id`. The endpoint only checks `User::isLogged()` but never verifies that the authenticated user owns the targeted schedule. After overwriting the poster, the endpoint broadcasts a `socketLiveOFFCallback` notification containing the victim's broadcast key and user ID to all connected WebSocket clients.\n\n## Details\n\nThe vulnerable endpoint at `plugin/Live/uploadPoster.php` accepts a `live_schedule_id` from `$_REQUEST` and uses it to determine poster file paths and trigger socket notifications without ownership validation.\n\n**Entry point — attacker-controlled input (line 11-12):**\n```php\n$live_servers_id = intval($_REQUEST['live_servers_id']);\n$live_schedule_id = intval($_REQUEST['live_schedule_id']);\n```\n\n**Insufficient auth check (line 14-17):**\n```php\nif (!User::isLogged()) {\n $obj->msg = 'You cant edit this file';\n die(json_encode($obj));\n}\n```\n\nThis only verifies the user is logged in. There is no check that `User::getId()` matches the schedule owner's `users_id`.\n\n**Poster path resolved by ID alone (line 40-42):**\n```php\n$paths = Live_schedule::getPosterPaths($live_schedule_id, 0);\n$obj->file = str_replace($global['systemRootPath'], '', $paths['path']);\n$obj->fileThumbs = str_replace($global['systemRootPath'], '', $paths['path_thumbs']);\n```\n\n`getPosterPaths()` is a static method that constructs file paths purely from the numeric ID with no authorization.\n\n**Attacker's file overwrites victim's poster (line 48):**\n```php\nif (!move_uploaded_file($_FILES['file_data']['tmp_name'], $tmpDestination)) {\n```\n\n**Broadcast to all WebSocket clients (line 67-73):**\n```php\nif (!empty($live_schedule_id)) {\n $ls = new Live_schedule($live_schedule_id);\n $array = setLiveKey($ls->getKey(), $ls->getLive_servers_id());\n $array['users_id'] = $ls->getUsers_id();\n $array['stats'] = getStatsNotifications(true);\n Live::notifySocketStats(\"socketLiveOFFCallback\", $array);\n}\n```\n\nThe `Live_schedule` constructor (inherited from `ObjectYPT`) loads data by ID with no auth checks. `Live::notifySocketStats()` calls `sendSocketMessageToAll()` which broadcasts to every connected WebSocket client.\n\n**Notably, the parallel endpoints DO have ownership checks:**\n- `plugin/Live/view/Live_schedule/uploadPoster.php` (line 18-21) checks `$row->getUsers_id() != User::getId()`\n- `plugin/Live/uploadPoster.json.php` (line 24-27) checks `User::isAdmin() || $row->getUsers_id() == User::getId()`\n\nThis proves the missing check in `uploadPoster.php` is an oversight, not by-design.\n\n## PoC\n\n```bash\n# Step 1: Log in as a low-privilege user to get a session cookie\ncurl -c cookies.txt -X POST 'https://target.com/objects/login.json.php' \\\n -d 'user=attacker@example.com&pass=attackerpassword'\n\n# Step 2: Overwrite the poster for live_schedule_id=1 (owned by a different user)\ncurl -b cookies.txt \\\n -F 'file_data=@malicious.jpg' \\\n -F 'live_schedule_id=1' \\\n -F 'live_servers_id=0' \\\n 'https://target.com/plugin/Live/uploadPoster.php'\n\n# Expected: 403 or ownership error\n# Actual: {} (success) — poster overwritten, socketLiveOFFCallback broadcast sent\n\n# Step 3: Verify the poster was replaced\ncurl -o - 'https://target.com/videos/live_schedule_posters/schedule_1.jpg' | file -\n# Output confirms attacker's image now serves as the victim's poster\n\n# The socketLiveOFFCallback broadcast (received by all WebSocket clients) contains:\n# { \"key\": \"<victim_broadcast_key>\", \"users_id\": <victim_user_id>, \"stats\": {...} }\n```\n\nSchedule IDs are sequential integers and can be enumerated trivially.\n\n## Impact\n\n1. **Content tampering:** Any authenticated user can overwrite poster images on any scheduled live stream. This enables defacement or phishing (e.g., replacing a poster with a malicious redirect image).\n2. **False offline notifications:** The `socketLiveOFFCallback` broadcast misleads all connected viewers into thinking the victim's stream went offline, disrupting the victim's audience.\n3. **Information disclosure:** The broadcast leaks the victim's `users_id` and broadcast key to all connected WebSocket clients.\n4. **Enumerable targets:** Schedule IDs are sequential integers, so an attacker can trivially enumerate and target all scheduled streams.\n\n## Recommended Fix\n\nAdd an ownership check after the login verification at line 17 in `plugin/Live/uploadPoster.php`:\n\n```php\nif (!User::isLogged()) {\n $obj->msg = 'You cant edit this file';\n die(json_encode($obj));\n}\n\n// Add ownership check for scheduled live streams\nif (!empty($live_schedule_id)) {\n $ls = new Live_schedule($live_schedule_id);\n if ($ls->getUsers_id() != User::getId() && !User::isAdmin()) {\n $obj->msg = 'Not authorized';\n die(json_encode($obj));\n }\n}\n```\n\nThis mirrors the existing authorization pattern already used in `uploadPoster.json.php` (line 24) and `view/Live_schedule/uploadPoster.php` (line 18).",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/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+
"last_affected": "26.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-g3hj-mf85-679g"
42+
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34247"
46+
},
47+
{
48+
"type": "WEB",
49+
"url": "https://github.com/WWBN/AVideo/commit/5fcb3bdf59f26d65e203cfbc8a685356ba300b60"
50+
},
51+
{
52+
"type": "PACKAGE",
53+
"url": "https://github.com/WWBN/AVideo"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-862"
59+
],
60+
"severity": "MODERATE",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-03-29T15:41:44Z",
63+
"nvd_published_at": "2026-03-27T17:16:30Z"
64+
}
65+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-r4fj-r33x-8v88",
4+
"modified": "2026-03-29T15:39:56Z",
5+
"published": "2026-03-29T15:39:56Z",
6+
"aliases": [
7+
"CVE-2026-34243"
8+
],
9+
"summary": "wenxian: Command Injection in GitHub Actions Workflow via `issue_comment.body` ",
10+
"details": "#### Summary\n\nA GitHub Actions workflow uses untrusted user input from `issue_comment.body` directly inside a shell command, allowing potential command injection and arbitrary code execution on the runner.\n\n#### Details\n\nThe workflow is triggered by `issue_comment`, which can be controlled by external users.\nIn the following step:\n\n```bash\necho identifiers=$(echo \"${{ github.event.comment.body }}\" | grep -oE '@njzjz-bot .*' | head -n1 | cut -c12- | xargs) >> $GITHUB_OUTPUT\n```\n\nthe value of `github.event.comment.body` is directly interpolated into a shell command inside `run:`.\n\nSince GitHub Actions evaluates `${{ }}` before execution, attacker-controlled input is injected into the shell context without sanitization. This creates a command injection risk.\n\nAdditionally, the extracted value is later reused in another step that constructs output using backticks:\n\n```bash\necho '@${{ github.event.comment.user.login }} Here is the BibTeX entry for `${{ steps.extract-identifiers.outputs.identifiers }}`:'\n```\n\nwhich may further propagate unsafe content.\n\n#### PoC\n\n1. Go to an issue in the repository\n2. Post a comment such as:\n\n`@njzjz-bot paper123\" ) ; whoami ; #\n`\n\n3. Observe whether the command is executed or reflected in logs/output\n<img width=\"658\" height=\"203\" alt=\"poc\" src=\"https://github.com/user-attachments/assets/084ac264-8cb9-4721-8279-26a1da9b891f\" />\n\nThe injected payload successfully breaks out of the quoted context and executes arbitrary shell commands.\n\nAs shown in the workflow logs, the injected `whoami` command is executed, and the output (`runner`) is printed. This confirms that attacker-controlled input from `github.event.comment.body` is interpreted as shell commands.\n\nThis demonstrates a clear command injection vulnerability in the workflow.\n\n#### Impact\n\n* Remote attackers can inject arbitrary shell commands via issue comments\n* Potential impacts:\n\n * Execution of arbitrary commands in GitHub Actions runner\n * Access to `GITHUB_TOKEN`\n * Exfiltration of repository data\n * CI/CD pipeline compromise\n\n\nThis issue affects all current versions of the repository as the vulnerable workflow is present in the main branch.\n\n### Suggested Fix\n\nAvoid directly interpolating untrusted user input into shell commands.\n\nInstead, pass `github.event.comment.body` through an environment variable and reference it safely within the script:\n\n```yaml\n- name: Extract identifiers\n id: extract-identifiers\n env:\n COMMENT_BODY: ${{ github.event.comment.body }}\n run: |\n identifiers=$(echo \"$COMMENT_BODY\" | grep -oE '@njzjz-bot .*' | head -n1 | cut -c12- | xargs)\n echo \"identifiers=$identifiers\" >> $GITHUB_OUTPUT",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "GitHub Actions",
21+
"name": "njzjz/wenxian"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "0.3.1"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/njzjz/wenxian/security/advisories/GHSA-r4fj-r33x-8v88"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/njzjz/wenxian"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-20",
51+
"CWE-77",
52+
"CWE-78"
53+
],
54+
"severity": "CRITICAL",
55+
"github_reviewed": true,
56+
"github_reviewed_at": "2026-03-29T15:39:56Z",
57+
"nvd_published_at": null
58+
}
59+
}

0 commit comments

Comments
 (0)