Overview
Rapid7 Labs conducted a zero-day research project against an HP Poly VVX 450 Voice over Internet Protocol (VoIP) phone. This research resulted in the discovery of a critical unauthenticated stack-based buffer overflow vulnerability, CVE-2026-0826. A remote attacker can leverage CVE-2026-0826 to achieve unauthenticated remote code execution (RCE) with root privileges on a target device.
The vulnerability is present in the device’s parsing of Session Description Protocol (SDP) attributes for Interactive Connectivity Establishment (ICE). The ICE feature, which is not enabled by default, must be enabled for the device to be exploitable by a remote attacker.
While we discovered and validated the vulnerability on a VVX 450 device, the vulnerability has been confirmed to affect all models in the VVX series (VVX 150, VVX 250, VVX 350, and VVX 450), as well as three models from the Trio IP Conference series (Trio 8800, Trio 8500, and Trio 8300).
CVE-2026-0826 has a CVSSv4 score of 9.2 (Critical), and a Common Weakness Enumeration (CWE) of CWE-121: Stack-based Buffer Overflow.
Impact
A Metasploit exploit module has been developed to demonstrate how an unauthenticated attacker could leverage this vulnerability to gain root privileges on a vulnerable device.
Shown below is the exploit being run against a target Poly VVX 450 device running a vulnerable firmware version 6.4.7.4477.
Figure 1: Metasploit exploit module targeting a Poly VVX 450 device.
⠀
As we can see above, the attacker achieves unauthenticated RCE with root privileges on the device. This is demonstrated by the attacker executing a reverse shell payload and running several arbitrary OS shell commands.
Technical analysis
Our analysis is based upon a VVX 450 device running firmware version 6.4.7.4477. During testing, the test device had an IPv4 address of 192.168.86.80. The non-default ICE feature was enabled by specifying the following in the device configuration:
device.feature.nat.ice.enabled="1"The main binary that provides the majority of functionality to the device is /user/local/root/polyapp (32 bit ARM, Little Endian). This binary parses SDP data provided in an Session Initiation Protocol (SIP) request over UDP on port 5060.
When SDP data is processed, if ICE is enabled, an SDP attribute named candidate can be parsed. The candidate attribute is intended to contain a transport address for a candidate that can be used for connectivity checks. An example of a valid candidate attribute can be seen in the RFC8839 5.1:
The following is an example SDP line for a UDP server-reflexive “candidate” attribute for the RTP component:a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr 203.0.113.141 rport 8998
Using the example from the RFC, a SIP request can contain SDP data that looks like this, with the candidate attribute appearing on the final line:
c=IN IP4 192.168.86.122
m=audio 50786 RTP/AVP 0
a=rtpmap:0 PCMU/8000/1
a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr 203.0.113.141 rport 8998⠀
The /user/local/root/polyapp binary has two functions that will parse incoming SDP data, named ParseRemoteSDP and IceSession::ParseRemoteSdpForAddresses. In both cases, when a string line starting with “a=candidate:” is found, a helper function ParseICECandidate (at address 0xB12780) is called to parse the expected candidate attribute held in the remainder of that string line. The intent is to parse out the individual components of a candidate attribute which are separated by white space characters.
This helper function ParseICECandidate contains a stack based buffer overflow. Shown below we can see that the start of the function contains a call to memcpy, which will copy the incoming string line being processed into a 256 byte stack buffer. No length check is performed to ensure the incoming string length is less than 256 bytes. Therefore by providing a candidate attribute whose length is greater than 256 bytes, a stack-based buffer overflow will occur.
int __fastcall ParseICECandidate( const void *string_line, size_t string_line_length, int a3, int *a4, _DWORD *a5, int *a6, std::string *a7, _DWORD *a8, _DWORD *a9, std::string *a10, _DWORD *a11)
{
size_t v11; // r0
char *v12; // r0
size_t v13; // r0
char *v14; // r0
size_t v15; // r0
char buffer256[256]; // [sp+25h] [bp-11Fh] BYREF
char v22[7]; // [sp+128h] [bp-1Ch] BYREF
char v23; // [sp+12Fh] [bp-15h] BYREF
char *nptr; // [sp+130h] [bp-14h]
char v25; // [sp+137h] [bp-Dh]
v25 = 0;
if ( !string_line )
return 0;
memcpy(buffer256, string_line, string_line_length); // <--- buffer256 can be overflowed due to no destination length check
buffer256[string_line_length] = 0;
nptr = strtok_r(buffer256, ":", (char **)&buffer256[255]);
nptr = strtok_r(0, " ", (char **)&buffer256[255]);
if ( !nptr )
return 0;
// ...snip...⠀
To demonstrate the vulnerability, we can construct an example SIP INVITE request that contains the required SDP data to trigger the buffer overflow. The malicious candidate attribute will be comprised of:
An attribute name of “a=candidate:”, which is 12 bytes long.
244 A characters, to fill out variable buffer256 (shown in the code snippet above), as 244 + 12 is 256.
19 B characters, to provide padding between the variable buffer256 and the saved registers on the current stack frame.
The characters 1111 (0x31313131 in hex) to overwrite the saved r4 register.
The characters 2222 (0x32323232 in hex) to overwrite the saved r5 register.
The characters 3333 (0x33333333 in hex) to overwrite the saved r11 register.
The characters 4444 (0x34343434 in hex) to overwrite the saved pc register.
A large number of C characters (0x43 in hex) to show the remaining attacker controlled data on the stack.
The entire example SIP INVITE request sent to the device is shown below:
INVITE sip:192.168.86.80:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.86.122:5060
Route: <192.168.86.122:5060><192.168.86.80:5060><192.168.86.80:5060><192.168.86.80/>192.168.86.80:5060>192.168.86.80:5060>192.168.86.122:5060>⠀

⠀
""""""""""""""""""""""""""""""""""""""""""⠀
⠀
’
“”
’

