How this bug class keeps getting shipped
Two of this week's CISA KEV additions arrive by the same route. On 2026-09-22 the agency added CVE-2026-93616, a path traversal in Check Point Security Management, Multi-Domain Security Management, Log Server, Multi-Domain Log Server, and SmartEvent that lets an unauthenticated attacker upload and execute arbitrary scripts. Two days later CISA added CVE-2026-5430, a path traversal in the WSO2 API Control Plane, API Manager, Traffic Manager, and Universal Gateway that allows unrestricted file upload and remote code execution. Different vendors, different codebases, different customers. Same shape.
That shape refuses to die: a write-side sink (an upload handler, a temp-file writer, an import feature) that takes an attacker-influenced path component and joins it to a trusted base directory without proving the result still lives inside that base. If the final path escapes the base and lands somewhere the process can execute from, "upload" quietly becomes "install." Both flaws were exploited in the wild before CISA listed them. The anatomy is worth examining.
The write-side path-confusion pattern
The classic read-side traversal (GET /files?name=../../etc/passwd) is well understood. Frameworks ship canonicalizers, WAF rules ship signatures, and code reviews flag string concatenation into open(). The write-side variant is quieter because the offending endpoint never reads anything back to the attacker. The request looks like a legitimate upload; the response is often just a 200 or a redirect. The impact surfaces only when something else reads the file the attacker placed.
The minimum ingredients are boring:
- An endpoint that accepts a filename, an archive entry name, a document ID, or any string that becomes a path segment.
- Server code that builds the destination path by joining a trusted base with the attacker's string.
- A sink that will later execute, deserialize, template-evaluate, or otherwise "run" whatever ends up at that path. A web root, a scheduled-job spool, a plugin directory, or a language interpreter's autoload path all qualify.
The gap that produces a CVE is almost always step 2. Developers reach for path.join, os.path.join, Path.resolve, or a language equivalent and assume the join is safe because they controlled the left operand. It is not. Both path.join('/opt/app/uploads', '../../var/www/html/shell.jsp') and equivalent calls in other languages resolve outside /opt/app/uploads. The join operator does not know what "inside" means. The developer must check, after the join, whether the result is still a descendant of the base, and reject it if not.
A minimum viable guard looks like this:
from pathlib import Path
BASE = Path("/opt/app/uploads").resolve(strict=True)
def safe_join(user_component: str) -> Path:
# Resolve symlinks and "..", then compare against the trusted base.
candidate = (BASE / user_component).resolve(strict=False)
try:
candidate.relative_to(BASE)
except ValueError:
raise PermissionError(f"path escapes base: {user_component!r}")
return candidate
Three details matter. The base is resolved once, at startup, so a later symlink swap does not change the reference. The candidate is resolved without strict=True (the target may not exist yet), and the containment check uses relative_to, which throws on escape rather than returning a truthy value that reviewers may silently accept. The guard also runs before the file descriptor opens, not after; opening first and validating second races the filesystem.
KEV entry one: CVE-2026-5430 in WSO2
CISA's KEV entry for CVE-2026-5430 is short and clinical: WSO2 API Control Plane, API Manager, Traffic Manager, and Universal Gateway "contain a path traversal vulnerability that could allow for unrestricted file upload and lead to remote code execution." The key phrase is the compound impact. Path traversal alone is a confidentiality problem. Path traversal into an upload handler that lets the attacker choose both the directory and the extension is an RCE primitive; the JVM loads whatever class file lands in the right place under a web application context, and API gateway management planes typically run with wide filesystem privileges by design.
Blue teamers should note the reach. A control plane sits above the data plane it manages, so a foothold there grants the attacker policy authority over every gateway it fronts. Detecting exploitation after the fact is harder than detecting the initial request. Look for two signals together: HTTP requests to management-plane upload or import endpoints whose parameter values contain encoded traversal sequences (%2e%2e%2f, ..\, %c0%ae%c0%ae), and shortly afterward, first-seen executable or script files under directories that a server process reads on demand. Either signal alone is noisy. Together, they form one of the tighter high-fidelity detection chains for this bug class.
KEV entry two: CVE-2026-93616 in Check Point management
The Check Point advisory covers the same primitive against a different surface. CVE-2026-93616 affects the Security Management Server, Multi-Domain Security Management Server, Log Server, Multi-Domain Log Server, and SmartEvent. The KEV description is explicit: "path traversal vulnerability that allows an unauthenticated attacker to upload and execute arbitrary scripts." Unauthenticated, on the management plane of a firewall estate, matters. These systems hold the policy that defines what "inside" means for everything downstream. If an attacker reaches the management interface and lands an executable script, the segmentation the firewalls enforce becomes irrelevant.
The exposure question is answerable without vendor tools. Any deployment with the administrative interface reachable outside a hardened jump path is in scope; any system with logs showing unauthenticated POSTs to upload, import, or upgrade endpoints since the KEV publication date should be treated as a live incident until proven otherwise, not a patching ticket.
What "verifiable" looks like for this class
Patching closes the specific CVE. It does not close the class. Two controls restore the assurance patching alone cannot provide:
- Prove containment at the sink, not the source. For every endpoint that writes attacker-influenced bytes to disk, add a test that submits
../and encoded variants and asserts a 4xx before the write, not after. A unit test that mocks the filesystem misses the symlink case; run it against a real temporary tree. - Prove the sink cannot execute. Upload directories should be mounted
noexecwhere the OS supports it, served from a location the application server will not evaluate as code, and scanned for extensions the interpreter recognizes. If a.jsp,.php,.aspx, or.pyfile ever appears in a directory whose purpose is "user document," the alert should go straight to on-call.
CISA's KEV listing carries the obligation set by BOD 26-04 (Prioritizing Security Updates Based on Risk), in force for federal civilian agencies. For everyone else, both entries confirm that active attackers are targeting this bug class now. Both flaws were reachable pre-authentication; both hit management planes that oversee production traffic; both deliver code execution as a service account with room to move. The write-side variant of path confusion has been shipping in commercial software for more than two decades. It keeps shipping because the guard belongs at the sink, and the sink lives far from the parser that made the mistake.
If the audit story for your uploads is "we call path.join and trust the base," you already have the bug. If it is a containment assertion, a noexec mount, and a monitored first-seen executable detector on every writable path, you have a defence that survives the next CVE in this class. It will land whether or not it makes CISA's list.
Verifiable security.