Jul 28, 2026·6 min read·22 visits
A trailing slash on request paths allows remote attackers to bypass goshs ACLs and blocklists, exposing sensitive configuration files and password hashes.
CVE-2026-66064 is an access control list (ACL) and blocklist bypass vulnerability in the goshs file server prior to version 2.1.5. Due to an inconsistency between uncleaned raw URI path evaluation and normalized file access, remote unauthenticated attackers can retrieve protected files, including the configuration file containing password hashes, by appending a trailing slash to the requested path.
CVE-2026-66064 identifies an access control list (ACL) and blocklist bypass vulnerability in goshs, a feature-rich, single-binary file server designed for developers and red teamers. The issue is present in all versions prior to 2.1.5. This server utilizes ACLs and blocklists to restrict remote access to specific sensitive files, such as internal directories and the .goshs configuration file itself.
An attacker can bypass these protection mechanisms by appending a trailing slash to the requested URI. This mismatch occurs because the server evaluates security restrictions using an uncleaned, raw request path, while the underlying operating system resolves the file using a normalized path. The resulting discrepancy permits unauthorized remote actors to read restricted assets without authentication.
This behavior is classified under CWE-41 (Improper Resolution of Path Equivalence) and CWE-863 (Incorrect Authorization). The vulnerability is particularly severe because the .goshs file often contains security configurations, including bcrypt credential hashes used for basic authentication.
The vulnerability exists in the sendFile function located within httpserver/handler.go. When processing an HTTP request, goshs extracts the final path component of the target file to determine whether access should be blocked. It does this by splitting the string representation of the request URL path on the forward slash character (/) and referencing the last item in the resulting slice.
If an attacker appends a trailing slash to the URL path (such as /blocked-directory/secret.txt/), the string split operation yields an empty string as the final segment of the path array. As a result, the local variable representing the target filename resolves to "". Because "" does not match the blocklist definitions or the protected configuration name .goshs, the security validations succeed without raising any exceptions.
While the security module evaluates the empty string, the routing layer processes the file path using canonical path sanitization functions such as filepath.Clean. This normalizes the request by removing the trailing slash before opening the file descriptor on the disk. Consequently, the operating system opens the actual file (e.g., secret.txt), bypassing the security controls that were evaluated against the empty string.
The vulnerability is highlighted by the discrepancy between path evaluation and actual file access. Below is the vulnerable logic from httpserver/handler.go before version 2.1.5:
// Vulnerable code in httpserver/handler.go
pathSplit := strings.Split(req.URL.Path, "/")
filename := pathSplit[len(pathSplit)-1]
if filename == ".goshs" {
fs.handleError(w, req, fmt.Errorf("open %s: no such file or directory", file.Name()), 404)
return
}
if fs.isBlocked(filename) {
fs.handleError(w, req, fmt.Errorf("open %s: permission denied", file.Name()), 403)
return
}To resolve this flaw, the patch implemented in commit f3ef599e409151d1380866e47de8b1afb0bb54fa shifts the validation from the raw request URL to the verified file descriptor metadata. The code now extracts the real base name of the file by querying the filesystem directly via file.Stat():
// Patched code in httpserver/handler.go (v2.1.5)
stat, err := file.Stat()
if err != nil {
fs.handleError(w, req, err, http.StatusInternalServerError)
return
}
// Retrieve the real filename from the physical file descriptor
filename := stat.Name()
if filename == ".goshs" {
fs.handleError(w, req, fmt.Errorf("open %s: no such file or directory", file.Name()), 404)
return
}
if fs.isBlocked(filename) {
fs.handleError(w, req, fmt.Errorf("open %s: permission denied", file.Name()), 403)
return
}By utilizing stat.Name(), the application guarantees that the authorization checks are conducted against the actual file being retrieved from disk. Even if an attacker manipulates the URI with trailing slashes, the operating system's file system interface ensures that the resulting FileInfo struct contains the canonical file name, preventing any divergence.
Exploiting this vulnerability does not require authentication and has low technical complexity. An attacker must first identify an active goshs server instance and determine the location of restricted directories or files. The attacker then constructs an HTTP GET request containing the targeted resource path with an appended trailing slash.
For example, if the target configuration file is located at /secret/.goshs, the attacker sends the request GET /secret/.goshs/ HTTP/1.1. The server receives the path, parses the filename as an empty string, and skips the ACL check. Simultaneously, the normalized path resolution opens /secret/.goshs and returns the file payload to the client.
This behavior is illustrated in the sequence diagram below:
This technique allows complete retrieval of blocklisted documents and configuration files. In configurations where basic authentication is active, downloading the .goshs file provides direct access to bcrypt-hashed credentials, which can be extracted and targeted in offline brute-force attacks.
The security impact of CVE-2026-66064 is classified as moderate, receiving an official CVSS v3.1 score of 5.3. The primary impact is a partial loss of confidentiality. Because goshs is designed as a lightweight utility often deployed in temporary or local sharing contexts, the scope is generally limited to the host system's shared web root directory.
However, in environments where goshs is utilized to serve sensitive red team payloads or restrict administrative tools, the disclosure of protected configurations introduces severe secondary risks. Obtaining the .goshs control file exposes the access patterns, blocklists, and password hashes. If weak passwords are used, offline cracking attempts will succeed, allowing attackers to pivot to authorized user roles.
The vulnerability does not allow direct write operations, and thus integrity and availability are unaffected. The attack requires no special privileges, no user interaction, and can be executed over any network interface exposing the goshs port. Because automated tooling can easily scan and detect the trailing-slash anomaly, public-facing instances face high risk of immediate discovery.
The definitive solution for CVE-2026-66064 is upgrading goshs to version 2.1.5 or later. This update modifies the filename resolution sequence to use metadata derived from the open file descriptor instead of raw URI parsing. Administrators should download the patched binary from the official release channels or rebuild it using Go modules.
If immediate patching is not possible, temporary mitigations can be applied at the network or reverse-proxy level. A front-end reverse proxy, such as Nginx or Apache, can be configured to inspect incoming requests and reject URIs that attempt to access files with a trailing slash. For example, a configuration rule can deny access to any request ending in /.goshs/ or containing multiple trailing slashes.
Additionally, security teams should audit existing .goshs files for the presence of password hashes. In the event that an instance was exposed prior to patching, any active credentials must be rotated immediately. Implementing defensive hosting strategies, such as binding the server strictly to localhost or utilizing virtual private networks (VPNs) for remote access, reduces the threat surface.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
goshs goshs-labs | < 2.1.5 | 2.1.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-41 |
| Attack Vector | Network |
| CVSS v3.1 Score | 5.3 |
| Exploit Status | PoC / Regression Tests Available |
| Impact | Partial Confidentiality Loss |
| KEV Status | Not Listed |
The software receives input that contains equivalent paths but treats them as different, allowing attackers to bypass checks.
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.