Overview
While researching a critical authentication bypass vulnerability, CVE-2026-20127, which was exploited in-the-wild, Rapid7 Labs discovered a new authentication bypass vulnerability affecting Cisco Catalyst SD-WAN Controller (formerly known as vSmart), CVE-2026-20182.
This new authentication bypass vulnerability affects the “vdaemon” service over DTLS (UDP port 12346), which is the same service that was vulnerable to CVE-2026-20127. The new vulnerability is not a patch bypass of CVE-2026-20127. It is a different issue located in a similar part of the “vdaemon” networking stack.
This impact however is the same, a remote unauthenticated attacker can leverage CVE-2026-20182 to become an authenticated peer of the target appliance, and perform privileged operations, such as injecting an attacker controlled public key into the vmanage-admin user account’s authorized SSH keys file. Once this has been performed, a remote unauthenticated attacker can login to the NETCONF service (SSH over TCP port 830) as the vmanage-admin user, and begin to issue arbitrary NETCONF commands.
CVE-2026-20182 has a CVSSv3.1 score of 10.0 (Critical), and a Common Weakness Enumeration (CWE) of CWE-287: Improper Authentication.
Technical analysis
The Cisco Catalyst SD-WAN Controller serves as the central control plane. Unlike Cisco Catalyst SD-WAN Manager, it has no web UI. Its network-reachable attack surface is narrow and depending on the configuration may expose the following ports:
Port | Protocol | Service |
22 | TCP | SSH (OpenSSH) |
830 | TCP | NETCONF over SSH |
12346 | UDP | vdaemon DTLS control plane |
⠀
UDP port 12346 is the DTLS-over-UDP control-plane peering port used by vdaemon for inter-controller and controller-to-edge communication. It carries Overlay Management Protocol (OMP) messages including route advertisements, Transport Locations (TLOC) tables, and peer state - the entirety of the SD-WAN overlay routing fabric. Compromising this service means compromising the network.
To understand the vulnerability, we first need to understand how vdaemon authenticates control-plane peers. The protocol is a multi-phase handshake over DTLS:
Attacker vSmart
| |
|──── DTLS Handshake (any cert) ───────────>| ← cert verify logs error but returns OK
| |
|<──── CHALLENGE (msg_type=8) ──────────────│ ← 256 random bytes + TLVs
| |
|──── CHALLENGE_ACK (msg_type=9) ──────────>| ← device_type=2 (vHub) → NO VERIFICATION
| |
|<──── CHALLENGE_ACK_ACK (msg_type=10) ─────│ ← peer->authenticated = 1
| |
|──── Hello (msg_type=5) ──────────────────>| ← passes auth check, peer goes UP
| |
|<──── Hello (msg_type=5) ──────────────────│ ← peer-type:vhub, new-state:up⠀
After a DTLS handshake completes (which accepts any client certificate), the server sends a CHALLENGE containing 256 random bytes and a set of TLVs including Certificate Authority (CA) RSA public key components. The client must respond with a CHALLENGE_ACK, and it is during the processing of this response, in vbond_proc_challenge_ack(), that device-type-specific certificate verification occurs. Or, in the case of a “vHub” device, does not occur.
The 12-byte message header format for the vdaemon protocol is as follows:
Byte Offset | Byte Size | Field | Notes |
0 | 1 | msg_type | Low nibble = type, high nibble = version |
1 | 1 | device_info | High nibble = device_type, low nibble = flags |
2 | 1 | flags | Standard value of 0xA0 |
3 | 1 | padding | Always 0x00 |
4 - 7 | 4 | domain_id | Big-endian uint32 |
8 - 11 | 4 | site_id | Big-endian uint32 |
⠀
The vdaemon protocol defines the following device types, encoded in the upper nibble of header byte 1, aka device_info:
Value | Device Type | Role |
1 | vEdge | Data-plane router |
2 | vHub | Hub router |
3 | vSmart | Control-plane controller |
4 | vBond | Orchestrator (trust anchor) |
5 | vManage | Management plane |
6 | ZTP | Zero-touch provisioning |
⠀
This is the core of the vulnerability. Below is a walk through of the decompiled code from vbond_proc_challenge_ack(), which processes the CHALLENGE_ACK message sent by a connecting peer. After the DTLS handshake, the function extracts the peer's certificate serial number and then enters device-type-specific verification (Note: edited for brevity):
⠀
// vdaemon!vbond_proc_challenge_ack()
// After extracting serial number from peer certificate via
// X509_get_serialNumber() / ASN1_INTEGER_to_BN() / BN_bn2hex()
// ...snip...
if ( *(_DWORD *)(a3 + 8) == 3 || *(_DWORD *)(a3 + 8) == 5 ) // <--- [1]
{
// vSmart (type 3) or vManage (type 5): Certificate chain verification
v24 = is_serial_duplicate(v22, *(_DWORD *)(a3 + 8), ...);
if ( v24 )
{
if ( (unsigned __int8)vbond_peer_dup_check(a1, a2, v24, ...) ) // <--- [2]
{
v19 = 36; // ERR: Duplicate Serial
goto LABEL_179; // REJECT
}
}
}
// ...snip...
// Second verification block - additional cert & state checks
if ( *(_DWORD *)(a3 + 8) == 3 && *(_DWORD *)(a1 + 8) == 3 // <--- [3]
|| *(_DWORD *)(a3 + 8) == 5 && *(_DWORD *)(a1 + 8) == 3
|| *(_DWORD *)(a3 + 8) == 5 && *(_DWORD *)(a1 + 8) == 5
|| *(_DWORD *)(a3 + 8) == 5 && *(_DWORD *)(a1 + 8) == 4
|| *(_DWORD *)(a3 + 8) == 3 && *(_DWORD *)(a1 + 8) == 4 )
{
v19 = vdaemon_dtls_verify_peer_cert(a2); // Full certificate verification
if ( v19 )
v18 = 0;
vdaemon_send_challenge_ack_ack(a1, *(_QWORD *)(a2 + 1232), a2, v18);
if ( v18 != 1 )
goto LABEL_179; // REJECT on verification failure
vbond_send_ssh_keys_to_vmanage_peer(a1, a2);
}
if ( *(_DWORD *)(a3 + 8) == 1 // <--- [4]
&& (dword_2A1A28 == 4 || dword_2A1A28 == 3 || dword_2A1A28 == 5) )
{
// vEdge (type 1): Hardware/virtual edge certificate verification
// ... challenge signature, board ID, OTP verification ...
if ( vdaemon_verify_peer_bidcert(a2, ...) )
goto LABEL_179; // REJECT on failure
}
// *** NO CODE PATH FOR device_type == 2 (vHub) *** // <--- [5]
*(_BYTE *)(a2 + 70) = 1; // peer->authenticated = true // <--- [6]
return 0LL; // Success⠀
We can see from the above that the function implements device-type-specific verification through a series of conditional blocks:
At [1] above, the function checks whether the connecting peer claims to be a vSmart (type 3) or vManage (type 5). If so, it enters a certificate serial number lookup via is_serial_duplicate(), which searches the local certificate database for a matching serial. At [2], if the serial is found, a duplicate-serial check via vbond_peer_dup_check() rejects the peer if a peer with that serial is already connected - preventing impersonation of existing authorized controllers.
At [3], a second verification block performs full certificate chain verification via vdaemon_dtls_verify_peer_cert(). This block executes only for specific (peer_type, local_type) pairs: vSmart-to-vSmart, vManage-to-vSmart, vManage-to-vManage, vManage-to-vBond, and vSmart-to-vBond. No pair in this block involves device type 2 (vHub). If the verification function returns a non-zero error, v18 is set to 0, and the function jumps to LABEL_179, which rejects the peer.
At [4], vEdge peers (type 1) enter hardware certificate verification via vdaemon_verify_peer_bidcert(). This path validates either a hardware TPM-based certificate (for physical vEdge routers) or a virtual edge certificate, including challenge-response signature verification and board ID validation. Failure sends the function to LABEL_179, which rejects the peer.
At [5], this is the bug, there is no “if” block matching a device type of 2 (vHub); the vHub device type simply has no verification code. The function falls through every conditional without entering any of them.
At [6], the function unconditionally sets “*(_BYTE *)(a2 + 70) = 1”, which is equivalent to ”peer->authenticated = true”, and returns success. The authenticated flag at peer struct offset 70 is the single bit that gates all subsequent message processing.
The following table summarizes the verification applied to each device type:
Device Type | Value | Verification | Result |
vEdge | 1 | HW cert, challenge signature, board ID, OTP | Verified |
vHub | 2 | None | Falls through to “peer->authenticated = 1” |
vSmart | 3 | Cert chain, serial lookup, duplicate check | Verified |
vBond | 4 | N/A (trust anchor - handled elsewhere) | - |
vManage | 5 | Cert chain, serial lookup, duplicate check | Verified |
⠀
Therefore, a remote unauthenticated attacker can bypass authentication by connecting to the vSmart DTLS port with any self-signed client certificate and claiming to be a vHub (type 2) in the CHALLENGE_ACK message. No valid credentials, no CA-signed certificate, and no knowledge of the SD-WAN deployment are required.
Looking further at the message dispatcher, we need to confirm that the CHALLENGE_ACK message can actually reach vbond_proc_challenge_ack() without prior authentication. The answer is in the pre-dispatch authentication gate in vbond_proc_msg():
// vdaemon!vbond_proc_msg()
// Pre-dispatch authentication gate:
if ( *(_BYTE *)(v100 + 70) != 1 // <--- [1]
&& *(_DWORD *)(a3 + 4) != 5 // msg != Hello
&& *(_DWORD *)(a3 + 4) != 8 // msg != CHALLENGE
&& *(_DWORD *)(a3 + 4) != 9 // msg != CHALLENGE_ACK
&& *(_DWORD *)(a3 + 4) // msg != NEW_CHALLENGE_ACK
&& *(_DWORD *)(a3 + 4) != 10 // msg != CHALLENGE_ACK_ACK
&& *(_DWORD *)(a3 + 4) != 7 // msg != Data
&& *(_DWORD *)(a3 + 4) != 11 // msg != TEAR_DOWN
// ...snip...
)
{
// ...snip...
// "Received an unexpected message from an un-authenticated device"
return 20;
}⠀
We can see at [1] above, that the condition is a conjunction of negations: the incoming message is rejected only if the peer is NOT authenticated AND the message type is not one of the pre-authentication allowed types (CHALLENGE, CHALLENGE_ACK, NEW_CHALLENGE_ACK, CHALLENGE_ACK_ACK, Data, and TEAR_DOWN).
CHALLENGE_ACK (Message type 9) is explicitly in the allow list, meaning it passes this gate without authentication and reaches the vulnerable vbond_proc_challenge_ack(). This is by design; the authentication handshake must be able to proceed before the peer is authenticated.
Once the vulnerable vbond_proc_challenge_ack() sets “peer->authenticated = true” via the vHub bypass, the attacker must send a Hello message (Message type 5) to transition the peer to the UP state. The Hello handler has its own secondary authentication check:
// Case 5 (Hello) in vbond_proc_msg - line 20362
case 5:
// ...snip...
if ( *(_BYTE *)(v100 + 70) != 1 ) // <--- [2]
{
// "Received an unexpected HELLO from un-authenticated device"
// ... cleanup and reject ...
return 0LL;
}
// Process Hello normally - peer transitions to UP⠀
At [2] above, the Hello handler verifies ”peer->authenticated == true” before processing. After our exploit sets this flag via the vHub bypass, Hello passes this secondary check and the peer transitions to the UP state, a fully trusted control-plane peer.
Putting all the pieces together: the attack chain is DTLS handshake (any cert) → receive CHALLENGE → send CHALLENGE_ACK with device type 2 (vHub) → authentication flag set unconditionally → send Hello → peer transitions to UP.
After establishing as an authenticated peer, the attacker has access to the full range of control-plane message types. We identified a particularly impactful post-authentication primitive: persistent SSH key injection via MSG_VMANAGE_TO_PEER (Message type 14).
The handler for message type 14 is vbond_proc_vmanage_to_peer(). Examining the decompiled code:
// vdaemon!vbond_proc_vmanage_to_peer()
// ...snip...
stream = fopen("/home/vmanage-admin/.ssh/authorized_keys", "a+"); // <--- [1]
if ( stream )
{
if ( (unsigned __int8)read_key_data((const char *)(a3 + 32), stream) != 1 && *(_BYTE *)(a3 + 32) )
{
if ( dword_241120 > 6 )
syslog(
191,
"%s[%d]: %%%s-%d: sshkey not present, writing to file",
"vbond_proc_vmanage_to_peer",
2368LL,
aVdaemonDbgMisc,
7LL);
fputs((const char *)(a3 + 32), stream); // <--- [2]
}
fclose(stream);
}
// ...snip...⠀
At [1] above, the file is opened in append mode - the attacker's key is added alongside any existing authorized keys, avoiding disruption of legitimate access. At [2], the attacker-controlled key buffer from the message body is written directly via fputs() with no sanitization.
The key injection message body is a fixed 769-byte structure:
Offset | Size | Field |
0-767 | 768 | Key buffer ("\n" + ssh_pubkey + "\n" + "\x00" + zero-padding) |
768 | 1 | TLV count = 0 |
⠀⠀
The leading “\n” ensures correct appending regardless of whether the existing authorized_keys file ends with a newline. The null byte terminates the string for fputs(), and the remainder is zero-padded to fill the 768-byte buffer.
Any authenticated peer, regardless of device type, can inject SSH keys into the vmanage-admin user's authorized_keys file on vSmart. The vmanage-admin user is a specific internal, high-privileged service account used for automated communication between the management plane (vManage) and the control plane (vSmart/vBond). This converts a transient control-plane peering session into persistent, credential-independent high-privileged access.
Exploitation
In this example we will use the exploit developed by Rapid7 Labs and target a Cisco Catalyst SD-WAN Controller which has an IP address of 192.168.80.11. In our example, both the vdaemon service and the NETCONF service are bound to the same interface. The attacker will have an IP address of 192.168.80.130. In our example, the target Cisco Catalyst SD-WAN Controller appliance is running version 20.12.6.1, which was the latest available version of the 20.12.* branch at the time of writing.
To begin, the attacker loads the module in Metasploit and configures the required options.

⠀
The module will perform the authentication bypass and then inject an attacker controlled SSH public key into the authorized keys file for the vmanage-admin user. The module will generate a new RSA key-pair prior to exploitation, so that the attacker will inject a public key for which they have the corresponding private key.
The attacker then sets the target and runs the module.
msf6 auxiliary(admin/networking/cisco_sdwan_vhub_auth_bypass) > set RHOSTS 192.168.80.11
msf6 auxiliary(admin/networking/cisco_sdwan_vhub_auth_bypass) > run⠀

⠀
The attacker can now SSH into the NETCONF service over TCP port 830 by running the following command (as instructed by the exploit above).
ssh -i /home/cryptocat/.msf4/loot/20260501115947_default_192.168.80.11_cisco.sdwan.sshk_491665.pem [email protected] -p 830⠀
SSH public key authentication will succeed, and the attacker will have successfully established a connection to the NETCONF service.

⠀
At this point the attacker can begin to execute arbitrary NETCONF commands, for example the following “get-config” command can be run by the attacker in the NETCONF session.
<?xml version="1.0" encoding="UTF-8"?><hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability></capabilities></hello>]]>]]><rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><get-config><source><running/></source></get-config></rpc>]]>]]>⠀
The output of the get-config command is shown below.

⠀
The full Metasploit module will be made available on May 27, 2026.
Remediation
Cisco has released software updates that address this vulnerability. There are no workarounds that address this vulnerability.
Customers are advised to upgrade to an appropriate fixed software release as indicated in the Fixed Software section of the Cisco Security Advisory. The following tables indicate the appropriate fixed software releases.
Cisco Catalyst SD-WAN Release | First Fixed Release |
|---|---|
Earlier than 20.9* | Migrate to a fixed release |
20.9 | 20.9.9.1 |
20.10 | 20.12.7.1 |
20.11* | 20.12.7.1 |
20.12 | 20.12.5.4, 20.12.6.2, 20.12.7.1 |
20.13* | 20.15.5.2 |
20.14* | 20.15.5.2 |
20.15 | 20.15.4.4, 20.15.5.2 |
20.16* | 20.18.2.2 |
20.18 | 20.18.2.2 |
26.1.1 | 26.1.1.1 |
*These releases have reached the end of software maintenance. Cisco strongly encourages customers to upgrade to a supported release.
For additional details, please see the vendor advisory.
Vendor statement
"Cisco values the role of the security research community in helping maintain a secure ecosystem and we appreciate the collaboration with Rapid7. We have released a software update to remediate the identified vulnerability. We remain committed to transparent communication and to providing our customers with the robust security and resilience they expect."
Rapid7 customers
Exposure Command, InsightVM and Nexpose customers will be able to assess their exposure to CVE-2026-20182 with an authenticated vulnerability check expected to be available in the May 14th, 2026 content release.
Credit
This vulnerability was discovered by Stephen Fewer, Senior Principal Security Researcher, and Jonah Burgess, Senior Security Researcher, both at Rapid7 and is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.
Disclosure timeline
March 9, 2026: Rapid7 makes initial outreach to Cisco who confirms contact the same day. Rapid7 discloses the technical writeup and exploit code to Cisco.
March 11, 2026: Cisco confirms receipt of the technical writeup and exploit code and suggests a disclosure date of May 7, 2026.
March 20, 2026: Cisco confirms the vulnerability findings, and that a CVE will be reserved.
April 21, 2026: Cisco provides reserved CVE identifier and remediation guidance.
April 24, 2026: Cisco provides remediation version numbers, alignment on CWE and CVSS scoring, and requests moving disclosure date to May 14.
May 14, 2026: This disclosure.
Related blog posts

Vulnerabilities and Exploits
CVE-2026-31381, CVE-2026-31382: Gainsight Assist Information Disclosure and Cross-Site Scripting (FIXED)
Christopher O’Boyle

Vulnerabilities and Exploits
The Phone is Listening: A Cold War–Style Vulnerability in Modern VoIP
Douglas McKee, Director, Vulnerability Intelligence

Vulnerabilities and Exploits
CVE-2026-2329: Critical Unauthenticated Stack Buffer Overflow in Grandstream GXP1600 VoIP Phones (FIXED)
Stephen Fewer

Vulnerabilities and Exploits
Vulnerability Found in InsightVM & Nexpose: CVE-2026-1814 (FIXED)
Rapid7


