Jul 29, 2026·5 min read·21 visits
A path traversal flaw in the openhole proxy server decodes percent-encoded dot-dot-slash segments and forwards them to the local agent, which executes raw, unauthorized file retrieval requests against local development services.
A critical path traversal vulnerability (CWE-22) in openhole-server version 0.1.1 and earlier allows remote, unauthenticated attackers to traverse directories and access restricted files or endpoints on local backend services exposed via the tunnel proxy. The issue stems from improper handling of decoded URL paths inside the proxy handler, which are then reconstructed and executed literally by the CLI client.
The openhole suite is an open-source utility designed to expose local development servers to the public internet. This exposure is accomplished by routing traffic through a public-facing gateway to an internal agent running on the developer's local system.
A critical vulnerability exists within openhole-server version 0.1.1 and earlier, classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). The core issue lies in the public proxy component, which fails to preserve raw, percent-encoded request paths when forwarding them to the local agent.
This behavior allows a remote attacker to perform a directory traversal attack against the underlying local services. By submitting specifically crafted requests with percent-encoded directory traversal sequences, the attacker can access restricted resources residing outside the intended document root of the local application.
To understand the root cause of CVE-2026-54650, one must analyze how Go handles URL parsing in the standard net/http library. When the standard library parses an incoming HTTP request, it populates the r.URL structure with fields that interpret percent-encoding differently.
The r.URL.Path property is designed to represent the decoded or unescaped path string. Consequently, any percent-encoded dot segments (%2e%2e representing ..) and path separators (%2f representing /) are automatically converted back into their literal characters before the handler processes the request.
In internal/server/public_proxy.go, the application assigned the incoming request path directly from r.URL.Path. This architectural choice resulted in the immediate expansion of all percent-encoded directory traversal segments inside the proxy server context. The proxy server subsequently wrapped this normalized path into a JSON-based protocol control message and dispatched it down the tunnel stream.
An examination of the vulnerable codebase in internal/client/local_proxy.go shows how the client reconstructed the destination URL. The client utilized string formatting with fmt.Sprintf to merge the local host, port, and the decoded path directly into a target string.
// Vulnerable implementation
url := fmt.Sprintf("http://%s:%d%s", host, port, req.Path)This string formatting step meant that if req.Path contained the literal string /../../etc/passwd, the client requested that exact URL from the local server. The Go HTTP client then transmitted a literal, canonicalized directory traversal request to the local backend service.
The remediation introduced in version 0.1.2 modifies both the server and client components. The server now calls r.URL.EscapedPath(), ensuring that percent-encoded structures remain encoded during transit. The client now properly instantiates a url.URL struct to parse the path safely.
// Patched client implementation
target := &url.URL{
Scheme: "http",
Host: fmt.Sprintf("%s:%d", host, port),
}
httpReq, err := http.NewRequest(req.Method, target.String(), bytes.NewReader(body))
// ...
path := req.Path
if path == "" {
path = "/"
}
httpReq.URL.RawPath = path
httpReq.URL.Path, err = url.PathUnescape(path)
if err != nil {
httpReq.URL.Path = path
}This structured assignment forces Go's HTTP client to output the exact original, percent-encoded string to the downstream local backend service, delegating path canonicalization to the local server instead of validating traversal at the proxy client boundary.
An attacker begins by identifying a publicly exposed openhole tunnel, represented by a domain such as https://temp-tunnel.openhole.dev. Since the endpoint is accessible over the public internet, no authentication is required to interact with the server interface.
The attacker crafts a custom HTTP GET request targeting the public endpoint. The request includes percent-encoded directory traversal indicators aimed at sensitive operating system files or local configuration assets.
GET /%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd HTTP/1.1
Host: temp-tunnel.openhole.dev
Connection: closeWhen the vulnerable public-facing proxy receives this request, it decodes the URI, generating a literal string /../../../../etc/passwd. The proxy encapsulates this string in a WebSocket-based request payload and forwards it down the active tunnel. The local CLI client translates this payload into an active localhost request, triggering file retrieval on the developer's workstation.
The primary impact of CVE-2026-54650 is unauthorized read-access to arbitrary files on the local workstation hosting the openhole client. This includes access to source code, environment variables, localized databases, and critical system files.
The CVSS v3.1 base score of 8.6 reflects high severity. Significantly, the Scope metric is evaluated as Changed (S:C). While the vulnerability resides in the internet-facing proxy server, the actual security impact occurs entirely within the security domain of the internal localhost development environment.
Because developers frequently run local databases and backend components with minimized security configurations, exposing these services via an openhole tunnel bypasses perimeter defenses. The vulnerability allows a remote attacker to gain direct access to assets that were otherwise assumed to be shielded behind firewalls or restricted to localized loopback interfaces.
Remediation requires upgrading both the openhole-server and the local openhole CLI agent to version 0.1.2 or higher. The updated software implements rigorous URL parameter formatting, ensuring that raw, percent-encoded values are preserved during routing.
For systems where immediate upgrades are impossible, developers should implement temporary workarounds. These include restricting access to the public proxy via firewall rules or IP address whitelisting, limiting exposure only to authorized external clients.
Additionally, developers should ensure that local development applications do not run with root or administrative privileges. Minimizing system-level access prevents traversal attacks from retrieving highly privileged operating system components if a local application is exposed to malicious input routing.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
openhole bablilayoub | <= 0.1.1 | 0.1.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS v3.1 | 8.6 |
| EPSS Score | N/A |
| Impact | Confidentiality (High) |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
Improper limitation of a pathname to a restricted directory, allowing path traversal.
A Server-Side Request Forgery (SSRF) vulnerability exists in the rmcp OAuth client, which is part of the Model Context Protocol (MCP) Rust SDK. The vulnerability arises from insecure processing of the resource_metadata parameter in WWW-Authenticate headers returned by a malicious or compromised MCP server. The client parses and fetches absolute URLs from this header without validation of scheme, origin, or network routing, allowing remote attackers to initiate HTTP GET requests to local network interfaces, RFC 1918 private subnets, or cloud metadata endpoints.
A module allowlist bypass vulnerability (CVE-2026-92945 / GHSA-7q3f-wx44-378m) was identified in the vm2 sandboxing library prior to version 3.11.7. This flaw permits unauthenticated or untrusted code running within the sandbox environment to bypass explicit module restrictions. When the transitive resolution option is disabled, the system fails to validate file system path boundaries, allowing prefix-sharing sibling directories to be resolved and loaded, thereby escaping intended sandbox restrictions.
CVE-2026-92941 is a critical sandbox-escape and trust-manipulation vulnerability in the vm2 library (versions 3.11.3 to 3.11.6). This security flaw allows untrusted code executing within a NodeVM sandbox environment to compromise the global TLS trust store of the host Node.js process. By leveraging a design flaw where the host's native 'tls.setDefaultCACertificates' can be executed via a proxy wrapper, combined with a bridge unwrapping bypass in the 'url' module, an attacker can modify the process-wide default root Certificate Authorities. Consequently, all subsequent outbound TLS/HTTPS clients running on the host thread are forced to trust attacker-signed certificates, facilitating transparent Man-in-the-Middle (MitM) attacks. The vulnerability was resolved in version 3.11.7 of vm2 by introducing built-in member-level sanitization before applying read-only proxy wrappers.
A critical engine-level reachability failure in Node.js 26 running V8 14.6 allows attackers to escape the vm2 sandbox environment. When consecutive prototype properties are modified using sequential assignments, a V8 optimization bug fails to invalidate the PromiseThenLookupChain protector. By calling Promise.prototype.finally, the attacker bypasses the vm2 wrappers, hijacks the promise reaction using a custom constructor, triggers a calibrated stack overflow to capture a host-realm RangeError, and executes arbitrary shell commands on the host.
A critical sandbox escape vulnerability in the vm2 library allows sandboxed JavaScript code to bypass containment and execute arbitrary native code on the host process. This occurs when the host's builtin crypto module is exposed to the NodeVM environment. Although vm2 implements a read-only proxy layer to restrict direct modifications to host properties, it does not prevent invocation of host-level functions. By calling the crypto.setEngine API with a path to a malicious native library on disk, an attacker can trigger OpenSSL's dynamic module loader. The host's operating system loader immediately runs the dynamic library's initializers/constructors before verifying engine compatibility, leading to remote code execution in the context of the host process.
CVE-2026-92938 is a critical sandbox escape vulnerability in the vm2 library (versions 3.11.3 through 3.11.6) that allows arbitrary native code execution on the host when the node:sqlite built-in module is loaded inside a sandboxed NodeVM environment.