VYPR
High severity7.5NVD Advisory· Published Jun 9, 2026· Updated Jun 9, 2026

CVE-2026-49842

CVE-2026-49842

Description

FreeSWITCH mod_verto allows unauthenticated bandwidth amplification via a speed-test protocol, potentially causing DoS.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

FreeSWITCH mod_verto allows unauthenticated bandwidth amplification via a speed-test protocol, potentially causing DoS.

Vulnerability

FreeSWITCH versions prior to 1.11.1 contain a vulnerability in the mod_verto module. The WebSocket frame loop intercepts a speed-test protocol (#SPU, #SPB, #SPE) before any authentication checks. The declared payload size in the #SPU frame was parsed using atoi(), which only rejected non-positive values. This allowed an unauthenticated peer to request up to INT_MAX bytes, leading to a significant outbound bandwidth amplification when the server responded [1].

Exploitation

An attacker can exploit this vulnerability by sending a specially crafted WebSocket frame to the mod_verto listener. This frame initiates the speed-test protocol. No authentication, prior login, or user interaction is required. The attacker needs network reach to the mod_verto WebSocket listener, which typically runs on ports :8081 (plaintext) or :8082 (TLS). The vulnerability is present before the authentication gate, and TLS does not mitigate it as the vulnerable code path executes after transport termination [1].

Impact

Successful exploitation allows an unauthenticated attacker to cause the FreeSWITCH server to write tens of gigabytes of data back to the requester. This can saturate outbound link capacity and exhaust socket and thread resources, leading to a denial of service. Multiple concurrent requests can easily deny service to legitimate users on the same listener [1].

Mitigation

This vulnerability has been patched in FreeSWITCH version 1.11.1, released on 2023-03-15 [2]. The fix requires an authenticated session to reach the speed-test handler and caps the declared size to 10 MiB, preventing unbounded amplification [1]. Workarounds include restricting the mod_verto WebSocket listener to trusted networks via firewall or bind address, or disabling mod_verto entirely if not in use [1].

AI Insight generated on Jun 9, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

3

Patches

2
67b62fb969a6

Merge commit from fork

https://github.com/signalwire/freeswitchDmitry VerenitsinMay 26, 2026Fixed in 1.11.1via llm-release-walk
1 file changed · +14 3
  • src/mod/endpoints/mod_verto/mod_verto.c+14 3 modified
    @@ -43,6 +43,7 @@ SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_vert
     #define HTTP_CHUNK_SIZE 1024 * 32
     #define HTTP_POST_MAX_BODY (10 * 1024 * 1024)   /* max accepted Content-Length for form-urlencoded POST */
     #define EP_NAME "verto.rtc"
    +#define VERTO_SPEED_TEST_MAX_SIZE (10 * 1024 * 1024)
     //#define WSS_STANDALONE 1
     #include "libks/ks.h"
     
    @@ -2112,24 +2113,34 @@ static void client_run(jsock_t *jsock)
     					char repl[2048] = "";
     					switch_time_t a, b;
     
    +					if (!switch_test_flag(jsock, JPFLAG_AUTHED)) {
    +						die("%s Speed-test request before authentication\n", jsock->name);
    +					}
    +
    +					if (bytes < 4) {
    +						continue;
    +					}
    +
     					if (s[1] == 'S' && s[2] == 'P') {
     
     						if (s[3] == 'U') {
    -							int i, size = 0;
    +							int i;
    +							long size;
     							char *p = s+4;
     							int loops = 0;
     							int rem = 0;
     							int dur = 0, j = 0;
     
    -							if ((size = atoi(p)) <= 0) {
    +							size = strtol(p, NULL, 10);
    +							if (size <= 0 || size > VERTO_SPEED_TEST_MAX_SIZE) {
     								continue;
     							}
     
     							a = switch_time_now();
     							do {
     								bytes = kws_read_frame(jsock->ws, &oc, &data);
     								s = (char *) data;
    -							} while (bytes && data && s[0] == '#' && s[3] == 'B');
    +							} while (bytes >= 4 && data && s[0] == '#' && s[3] == 'B');
     							b = switch_time_now();
     
     							if (!bytes || !data) continue;
    
33ee3663bbe6

Merge commit from fork

https://github.com/signalwire/freeswitchDmitry VerenitsinMay 26, 2026Fixed in 1.11.1via llm-release-walk
5 files changed · +405 4
  • src/mod/endpoints/mod_verto/mod_verto.c+5 4 modified
    @@ -41,6 +41,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime);
     SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_verto_runtime);
     
     #define HTTP_CHUNK_SIZE 1024 * 32
    +#define HTTP_POST_MAX_BODY (10 * 1024 * 1024)   /* max accepted Content-Length for form-urlencoded POST */
     #define EP_NAME "verto.rtc"
     //#define WSS_STANDALONE 1
     #include "libks/ks.h"
    @@ -1824,24 +1825,24 @@ static void http_run(jsock_t *jsock)
     		char *buffer = NULL;
     		switch_ssize_t len = 0, bytes = 0;
     
    -		if (request->content_length && request->content_length > 10 * 1024 * 1024 - 1) {
    +		if (request->content_length && request->content_length >= HTTP_POST_MAX_BODY) {
     			char *data = "HTTP/1.1 413 Request Entity Too Large\r\n"
     				"Content-Length: 0\r\n\r\n";
     			kws_raw_write(jsock->ws, data, strlen(data));
     			request->keepalive = 0;
     			goto done;
     		}
     
    -		if (!(buffer = malloc(2 * 1024 * 1024))) {
    +		if (!(buffer = malloc(request->content_length + 1))) {
     			goto request_err;
     		}
     
     		while(bytes < (switch_ssize_t)request->content_length) {
     			len = request->content_length - bytes;
     
    -#define WS_BLOCK 1
    +#define WS_BLOCK 10000   /* ms; matches libks's internal default */
     
    -			if ((len = kws_raw_read(jsock->ws, buffer + bytes, len, WS_BLOCK)) < 0) {
    +			if ((len = kws_raw_read(jsock->ws, buffer + bytes, len, WS_BLOCK)) <= 0) {
     				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Read error %" SWITCH_SSIZE_T_FMT"\n", len);
     				goto done;
     			}
    
  • tests/unit/conf_verto/freeswitch.xml+47 0 added
    @@ -0,0 +1,47 @@
    +<?xml version="1.0"?>
    +<document type="freeswitch/xml">
    +  <X-PRE-PROCESS cmd="set" data="local_ip_v4=127.0.0.1"/>
    +  <X-PRE-PROCESS cmd="set" data="domain=127.0.0.1"/>
    +
    +  <section name="configuration" description="Configuration">
    +
    +    <configuration name="modules.conf" description="Modules">
    +      <modules>
    +        <load module="mod_console"/>
    +        <load module="mod_loopback"/>
    +        <load module="mod_dptools"/>
    +        <load module="mod_dialplan_xml"/>
    +        <load module="mod_sndfile"/>
    +        <load module="mod_verto"/>
    +      </modules>
    +    </configuration>
    +
    +    <configuration name="switch.conf" description="Core Configuration">
    +      <settings>
    +        <param name="colorize-console" value="false"/>
    +        <param name="loglevel" value="debug"/>
    +        <param name="rtp-start-port" value="16384"/>
    +        <param name="rtp-end-port" value="16484"/>
    +      </settings>
    +    </configuration>
    +
    +    <configuration name="console.conf" description="Console Logger">
    +      <mappings>
    +        <map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
    +      </mappings>
    +      <settings>
    +        <param name="colorize" value="false"/>
    +        <param name="loglevel" value="debug"/>
    +      </settings>
    +    </configuration>
    +
    +    <configuration name="timezones.conf" description="Timezones">
    +      <timezones>
    +        <zone name="GMT" value="GMT0"/>
    +      </timezones>
    +    </configuration>
    +
    +    <X-PRE-PROCESS cmd="include" data="verto.conf.xml"/>
    +
    +  </section>
    +</document>
    
  • tests/unit/conf_verto/verto.conf.xml+36 0 added
    @@ -0,0 +1,36 @@
    +<configuration name="verto.conf" description="HTML5 Verto Endpoint (test)">
    +
    +  <settings>
    +    <param name="debug" value="0"/>
    +  </settings>
    +
    +  <profiles>
    +    <profile name="test-v4">
    +      <!--
    +        bind-local without "secure" → plain TCP. Port 33081 chosen to avoid
    +        clashing with a default vanilla install on 8081.
    +      -->
    +      <param name="bind-local" value="127.0.0.1:33081"/>
    +      <param name="force-register-domain" value="127.0.0.1"/>
    +      <param name="userauth" value="true"/>
    +      <param name="blind-reg" value="false"/>
    +      <param name="rtp-ip" value="127.0.0.1"/>
    +      <param name="timer-name" value="soft"/>
    +
    +      <!--
    +        vhosts block is REQUIRED for http_run() to be invoked
    +        (see mod_verto.c:2041,2061 — KWS_HTTP flag depends on it).
    +        No auth-realm here, so requests bypass the 401 challenge and
    +        reach the body-read path (which is itself pre-auth anyway).
    +      -->
    +      <vhosts>
    +        <vhost domain="127.0.0.1">
    +          <param name="alias" value="localhost"/>
    +          <param name="root" value="."/>
    +          <param name="index" value="index.html"/>
    +        </vhost>
    +      </vhosts>
    +    </profile>
    +  </profiles>
    +
    +</configuration>
    
  • tests/unit/Makefile.am+1 0 modified
    @@ -6,6 +6,7 @@ noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer
     noinst_PROGRAMS += switch_stun
     noinst_PROGRAMS += test_tts_format
     noinst_PROGRAMS+= switch_hold switch_sip
    +noinst_PROGRAMS += test_mod_verto
     
     if HAVE_PCAP
     noinst_PROGRAMS += switch_rtp_pcap
    
  • tests/unit/test_mod_verto.c+316 0 added
    @@ -0,0 +1,316 @@
    +/*
    + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
    + * Copyright (C) 2026, Anthony Minessale II <anthm@freeswitch.org>
    + *
    + * Version: MPL 1.1
    + *
    + * The contents of this file are subject to the Mozilla Public License Version
    + * 1.1 (the "License"); you may not use this file except in compliance with
    + * the License. You may obtain a copy of the License at
    + * http://www.mozilla.org/MPL/
    + *
    + * Software distributed under the License is distributed on an "AS IS" basis,
    + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    + * for the specific language governing rights and limitations under the
    + * License.
    + *
    + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
    + *
    + * The Initial Developer of the Original Code is
    + * Anthony Minessale II <anthm@freeswitch.org>
    + * Portions created by the Initial Developer are Copyright (C)
    + * the Initial Developer. All Rights Reserved.
    + *
    + * Contributor(s):
    + * Dmitry Verenitsin <dmitry.verenitsin@signalwire.com>
    + *
    + *
    + * test_mod_verto.c -- Tests for mod_verto
    + *
    + */
    +
    +#include <switch.h>
    +#include <test/switch_test.h>
    +
    +#define VERTO_TEST_HOST "127.0.0.1"
    +#define VERTO_TEST_PORT 33081
    +
    +/* Must match HTTP_POST_MAX_BODY in src/mod/endpoints/mod_verto/mod_verto.c */
    +#define VERTO_POST_MAX_BODY (10 * 1024 * 1024)
    +
    +static switch_status_t verto_connect(switch_socket_t **sock_out, switch_memory_pool_t *pool)
    +{
    +	switch_sockaddr_t *addr = NULL;
    +	switch_socket_t *sock = NULL;
    +	int attempts;
    +
    +	if (switch_sockaddr_info_get(&addr, VERTO_TEST_HOST, SWITCH_UNSPEC,
    +								 VERTO_TEST_PORT, 0, pool) != SWITCH_STATUS_SUCCESS) {
    +		return SWITCH_STATUS_FALSE;
    +	}
    +
    +	for (attempts = 0; attempts < 50; attempts++) {
    +		if (switch_socket_create(&sock, switch_sockaddr_get_family(addr),
    +								 SOCK_STREAM, SWITCH_PROTO_TCP, pool) != SWITCH_STATUS_SUCCESS) {
    +			return SWITCH_STATUS_FALSE;
    +		}
    +		switch_socket_opt_set(sock, SWITCH_SO_TCP_NODELAY, 1);
    +
    +		if (switch_socket_connect(sock, addr) == SWITCH_STATUS_SUCCESS) {
    +			*sock_out = sock;
    +			return SWITCH_STATUS_SUCCESS;
    +		}
    +
    +		switch_socket_close(sock);
    +		sock = NULL;
    +		switch_yield(100000);
    +	}
    +
    +	return SWITCH_STATUS_FALSE;
    +}
    +
    +static switch_status_t send_all(switch_socket_t *sock, const char *buf, switch_size_t len)
    +{
    +	switch_size_t remaining = len;
    +	const char *p = buf;
    +
    +	while (remaining > 0) {
    +		switch_size_t n = remaining;
    +		if (switch_socket_send(sock, p, &n) != SWITCH_STATUS_SUCCESS) {
    +			return SWITCH_STATUS_FALSE;
    +		}
    +		if (n == 0) {
    +			return SWITCH_STATUS_FALSE;
    +		}
    +		p += n;
    +		remaining -= n;
    +	}
    +	return SWITCH_STATUS_SUCCESS;
    +}
    +
    +static switch_size_t read_status_line(switch_socket_t *sock, char *out, switch_size_t cap)
    +{
    +	switch_size_t got = 0;
    +
    +	while (got < cap - 1) {
    +		switch_size_t want = cap - 1 - got;
    +		if (switch_socket_recv(sock, out + got, &want) != SWITCH_STATUS_SUCCESS || want == 0) {
    +			break;
    +		}
    +		got += want;
    +		if (memchr(out, '\n', got)) break;
    +	}
    +	out[got] = '\0';
    +	return got;
    +}
    +
    +FST_CORE_DB_BEGIN("./conf_verto")
    +{
    +	FST_SUITE_BEGIN(test_mod_verto)
    +	{
    +		FST_SETUP_BEGIN()
    +		{
    +			fst_requires_module("mod_verto");
    +			switch_yield(500000);
    +		}
    +		FST_SETUP_END()
    +
    +		FST_TEARDOWN_BEGIN()
    +		{
    +		}
    +		FST_TEARDOWN_END()
    +
    +		FST_TEST_BEGIN(post_at_cap_returns_413)
    +		{
    +			switch_memory_pool_t *pool = NULL;
    +			switch_socket_t *sock = NULL;
    +			char req[256];
    +			char resp[64] = { 0 };
    +			switch_size_t req_len;
    +
    +			do {
    +				if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not allocate memory pool");
    +					break;
    +				}
    +				if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not connect to verto listener");
    +					break;
    +				}
    +
    +				req_len = switch_snprintf(req, sizeof(req),
    +					"POST / HTTP/1.1\r\n"
    +					"Host: " VERTO_TEST_HOST "\r\n"
    +					"Content-Type: application/x-www-form-urlencoded\r\n"
    +					"Content-Length: %d\r\n"
    +					"\r\n",
    +					VERTO_POST_MAX_BODY);
    +
    +				if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not send request");
    +					break;
    +				}
    +
    +				read_status_line(sock, resp, sizeof(resp));
    +				fst_check_string_starts_with(resp, "HTTP/1.1 413");
    +			} while (0);
    +
    +			if (sock) switch_socket_close(sock);
    +			if (pool) switch_core_destroy_memory_pool(&pool);
    +		}
    +		FST_TEST_END()
    +
    +		FST_TEST_BEGIN(post_small_body_parsed)
    +		{
    +			switch_memory_pool_t *pool = NULL;
    +			switch_socket_t *sock = NULL;
    +			const switch_size_t body_len = 32 * 1024;
    +			char *body = NULL;
    +			char req[256];
    +			char resp[64] = { 0 };
    +			switch_size_t req_len;
    +
    +			do {
    +				if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not allocate memory pool");
    +					break;
    +				}
    +				if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not connect to verto listener");
    +					break;
    +				}
    +
    +				body = malloc(body_len);
    +				if (!body) {
    +					fst_fail("could not allocate body buffer");
    +					break;
    +				}
    +				memset(body, 'x', body_len);
    +
    +				req_len = switch_snprintf(req, sizeof(req),
    +					"POST / HTTP/1.1\r\n"
    +					"Host: " VERTO_TEST_HOST "\r\n"
    +					"Content-Type: application/x-www-form-urlencoded\r\n"
    +					"Content-Length: %" SWITCH_SIZE_T_FMT "\r\n"
    +					"\r\n",
    +					body_len);
    +
    +				if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not send headers");
    +					break;
    +				}
    +				if (send_all(sock, body, body_len) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not send body");
    +					break;
    +				}
    +
    +				read_status_line(sock, resp, sizeof(resp));
    +				fst_check_string_starts_with(resp, "HTTP/1.1 ");
    +				fst_xcheck(strncmp(resp, "HTTP/1.1 413", 12) != 0,
    +					"server returned 413 below cap");
    +			} while (0);
    +
    +			free(body);
    +			if (sock) switch_socket_close(sock);
    +			if (pool) switch_core_destroy_memory_pool(&pool);
    +		}
    +		FST_TEST_END()
    +
    +		FST_TEST_BEGIN(post_large_body_no_overflow)
    +		{
    +			switch_memory_pool_t *pool = NULL;
    +			switch_socket_t *sock = NULL;
    +			const switch_size_t body_len = 8 * 1024 * 1024;
    +			char *body = NULL;
    +			char req[256];
    +			char resp[64] = { 0 };
    +			switch_size_t req_len;
    +
    +			do {
    +				if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not allocate memory pool");
    +					break;
    +				}
    +				if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not connect to verto listener");
    +					break;
    +				}
    +
    +				body = malloc(body_len);
    +				if (!body) {
    +					fst_fail("could not allocate body buffer");
    +					break;
    +				}
    +				memset(body, 'x', body_len);
    +
    +				req_len = switch_snprintf(req, sizeof(req),
    +					"POST / HTTP/1.1\r\n"
    +					"Host: " VERTO_TEST_HOST "\r\n"
    +					"Content-Type: application/x-www-form-urlencoded\r\n"
    +					"Content-Length: %" SWITCH_SIZE_T_FMT "\r\n"
    +					"\r\n",
    +					body_len);
    +
    +				if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not send headers");
    +					break;
    +				}
    +				if (send_all(sock, body, body_len) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not send body");
    +					break;
    +				}
    +
    +				read_status_line(sock, resp, sizeof(resp));
    +				fst_check_string_starts_with(resp, "HTTP/1.1 ");
    +				fst_xcheck(strncmp(resp, "HTTP/1.1 413", 12) != 0,
    +					"server returned 413 below cap");
    +			} while (0);
    +
    +			free(body);
    +			if (sock) switch_socket_close(sock);
    +			if (pool) switch_core_destroy_memory_pool(&pool);
    +		}
    +		FST_TEST_END()
    +
    +		FST_TEST_BEGIN(post_overflow_length_returns_413)
    +		{
    +			switch_memory_pool_t *pool = NULL;
    +			switch_socket_t *sock = NULL;
    +			char req[256];
    +			char resp[64] = { 0 };
    +			switch_size_t req_len;
    +
    +			do {
    +				if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not allocate memory pool");
    +					break;
    +				}
    +				if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not connect to verto listener");
    +					break;
    +				}
    +
    +				req_len = switch_snprintf(req, sizeof(req),
    +					"POST / HTTP/1.1\r\n"
    +					"Host: " VERTO_TEST_HOST "\r\n"
    +					"Content-Type: application/x-www-form-urlencoded\r\n"
    +					"Content-Length: 9999999999\r\n"
    +					"\r\n");
    +
    +				if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
    +					fst_fail("could not send request");
    +					break;
    +				}
    +
    +				read_status_line(sock, resp, sizeof(resp));
    +				fst_check_string_starts_with(resp, "HTTP/1.1 413");
    +			} while (0);
    +
    +			if (sock) switch_socket_close(sock);
    +			if (pool) switch_core_destroy_memory_pool(&pool);
    +		}
    +		FST_TEST_END()
    +	}
    +	FST_SUITE_END()
    +}
    +FST_CORE_END()
    

Vulnerability mechanics

Root cause

"The WebSocket frame loop in mod_verto processed speed-test protocol frames before authentication, allowing unauthenticated requests with excessively large payload sizes."

Attack vector

An unauthenticated peer can send a WebSocket frame with a '#'-prefixed speed-test protocol identifier (e.g., #SPU). The declared payload size in this frame is parsed using `atoi()`, which only rejects non-positive values. This allows an attacker to request an extremely large payload size, up to `INT_MAX` [patch_id=5390375]. The server then responds with a significantly amplified outbound bandwidth during the download phase.

Affected code

The vulnerability resides in the `client_run` function within `src/mod/endpoints/mod_verto/mod_verto.c`. Specifically, the code handling '#'-prefixed frames, including the parsing of payload size with `atoi()`, is affected.

What the fix does

The patch addresses the vulnerability by first gating the '#' speed-test protocol handling behind an authentication flag (`JPFLAG_AUTHED`) [patch_id=5390375]. Additionally, it replaces the vulnerable `atoi()` function with a bounded `strtol()` and caps the declared payload size at 10 MiB (`VERTO_SPEED_TEST_MAX_SIZE`). This prevents excessively large requests and mitigates the bandwidth amplification.

Preconditions

  • authThe attacker does not need to be authenticated.
  • networkThe attacker must be able to establish a WebSocket connection to the FreeSWITCH server.

Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.