GHSA-w5ff-2mjc-4phc
MEDIUMAVideo has an OS Command Injection via Unescaped URL in LinkedIn Video Upload Shell Command
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.
Blast Radius
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 uploadVideoToLinkedIn() method in the SocialMediaPublisher plugin constructs a shell command by directly interpolating an upload URL received from LinkedIn's API response, without sanitization via escapeshellarg(). If an attacker can influence the LinkedIn API response (via MITM, compromised OAuth token, or API compromise), they can inject arbitrary OS commands that execute as the web server user.
Details
The vulnerability exists in plugin/SocialMediaPublisher/Objects/SocialUploader.php.
The initializeLinkedInUploadSession() method (line 649) sends a POST request to https://api.linkedin.com/rest/videos?action=initializeUpload and parses the JSON response at line 693:
// SocialUploader.php:693
$responseArray = json_decode($response, true);
The parsed uploadInstructions array is iterated at line 532, and each uploadUrl is passed to uploadVideoToLinkedIn() at line 542:
// SocialUploader.php:542
$uploadResponse = self::uploadVideoToLinkedIn($instruction['uploadUrl'], $tmpFile);
The uploadVideoToLinkedIn() method (line 711) constructs a shell command by directly concatenating both $uploadUrl and $filePath into a string passed to exec():
// SocialUploader.php:713-720
$shellCmd = 'curl -v -H "Content-Type:application/octet-stream" --upload-file "' .
$filePath . '" "' .
$uploadUrl . '" 2>&1';
_error_log("Upload Video Shell Command:\n" . $shellCmd);
exec($shellCmd, $o);
Neither $uploadUrl nor $filePath is sanitized with escapeshellarg(). A malicious URL such as https://uploads.linkedin.local" ; id ; echo " would break out of the quoted string and execute arbitrary commands.
The $uploadUrl originates from LinkedIn's API response — a trusted third-party source over HTTPS — so exploitation requires compromising that response (MITM at CA level, compromised OAuth token leading to attacker-controlled API responses, or LinkedIn API compromise). This makes the attack complexity high, but the missing sanitization is a defense-in-depth failure that could become critical if the trust boundary is ever violated.
PoC
This vulnerability requires manipulating the LinkedIn API response. A simulated proof-of-concept using a local proxy:
Step 1: Set up a proxy that intercepts the LinkedIn API response and replaces the uploadUrl field:
{
"value": {
"uploadInstructions": [
{
"uploadUrl": "https://example.com\" ; id > /tmp/pwned ; echo \"",
"firstByte": 0,
"lastByte": 1024
}
],
"uploadToken": "token123",
"video": "urn:li:video:123"
}
}
Step 2: The resulting shell command becomes:
curl -v -H "Content-Type:application/octet-stream" --upload-file "/tmp/tmpfile" "https://uploads.linkedin.local" ; id > /tmp/pwned ; echo "" 2>&1
Step 3: The id command executes as the web server user, writing output to /tmp/pwned.
Step 4: Verify:
cat /tmp/pwned
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
Impact
- Remote Code Execution: If the LinkedIn API response is compromised, an attacker gains arbitrary command execution as the web server user (
www-data). - Confidentiality: Full read access to application source code, configuration files (including database credentials), and any data accessible to the web server process.
- Integrity: Ability to modify application files, inject backdoors, or alter database records.
- Practical risk is low due to the high attack complexity — exploitation requires compromising a trusted HTTPS API response from LinkedIn. This is primarily a defense-in-depth issue.
Recommended Fix
Sanitize both $uploadUrl and $filePath with escapeshellarg() before interpolation into the shell command. Alternatively, replace the exec() call with PHP's native cURL functions (which are already used elsewhere in the same class):
Option 1 — Minimal fix with escapeshellarg():
// plugin/SocialMediaPublisher/Objects/SocialUploader.php:711-715
static function uploadVideoToLinkedIn($uploadUrl, $filePath)
{
$shellCmd = 'curl -v -H "Content-Type:application/octet-stream" --upload-file ' .
escapeshellarg($filePath) . ' ' .
escapeshellarg($uploadUrl) . ' 2>&1';
Option 2 — Replace shell exec with native PHP cURL (preferred):
static function uploadVideoToLinkedIn($uploadUrl, $filePath)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uploadUrl);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/octet-stream']);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, fopen($filePath, 'r'));
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
curl_close($ch);
// Extract ETag from response headers
$matches = [];
preg_match('/(etag:)(\s?)(.*)(\n)/i', $headers, $matches);
$etag = isset($matches[3]) ? trim($matches[3]) : null;
// ... rest of function
}
Option 2 is strongly preferred as it eliminates the shell execution entirely, removing the injection surface and aligning with the PHP cURL usage already present in initializeLinkedInUploadSession() on line 664.
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. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Remediation status
No patched version of wwbn/avideo has shipped for GHSA-w5ff-2mjc-4phc 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 pinpoints whether GHSA-w5ff-2mjc-4phc is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-w5ff-2mjc-4phc. 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-w5ff-2mjc-4phc in your dependencies?
O3 detects GHSA-w5ff-2mjc-4phc across Packagist dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.