goshs CVE-2026-50139
MEDIUMSeverity by source
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Network-accessible endpoint, token possession substitutes for no privilege requirement, race timing yields AC:H; impact is purely confidentiality with no integrity or availability consequence.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Lifecycle Timeline
1DescriptionGitHub Advisory
Share-link ?token=… redemption races past download limit
Ecosystem: Go Package: goshs.de/goshs/v2 (github.com/patrickhener/goshs) Affected: <= v2.0.9 (every release that shipped the share-link feature)
Summary
ShareHandler reads the share token's DownloadLimit under RLock, releases the lock, serves the file, then re-acquires the lock to increment the counter. Concurrent requests all read the same Downloaded/DownloadLimit snapshot, all pass the check, and all are served - exceeding the operator's intended cap.
Details
httpserver/handler.go:968-1018:
fs.sharedLinksMu.RLock()
entry, ok := fs.SharedLinks[token]
fs.sharedLinksMu.RUnlock() // <-- released here
if entry.DownloadLimit > 0 || entry.DownloadLimit == -1 {
// ...serve file... // <-- whole transfer happens unlocked
}
fs.sharedLinksMu.Lock() // <-- re-acquired only now
current.Downloaded++
if current.Downloaded >= current.DownloadLimit { delete(fs.SharedLinks, token) }
fs.sharedLinksMu.Unlock()Between line 978 (RUnlock) and line 1008 (Lock), any number of goroutines can interleave and each observes the same pre-increment limit.
Proof of concept
goshs -p 18000 -d /tmp/r -b admin:pw &
echo data > /tmp/r/f.txt
# operator issues a one-shot share
SHARE=$(curl -su admin:pw "http://localhost:18000/f.txt?share&limit=1")
TK=$(echo "$SHARE" | sed -n 's/.*token=\([^"]*\)".*/\1/p')
# attacker races two redemptions
curl -so /dev/null -w "%{http_code}\n" "http://localhost:18000/?token=$TK" & \
curl -so /dev/null -w "%{http_code}\n" "http://localhost:18000/?token=$TK" & \
wait
# observed: 200 / 200 (both succeed) -> limit=1 redeemed twiceReproduced 5/5 times in a row on a 2026-era M-series Mac during verification.
Impact
A "single-use" share intended to deliver a one-shot secret can be redeemed N times by N concurrent clients. Combined with any token-leak vector (mail forwarding, browser history, intercepted link, etc.) this multiplies the exfiltration window.
Suggested fix
Reserve under the write lock *before* serving - refund only if the serve fails:
fs.sharedLinksMu.Lock()
entry, ok := fs.SharedLinks[token]
if !ok || time.Now().After(entry.Expires) ||
(entry.DownloadLimit != -1 && entry.Downloaded >= entry.DownloadLimit) {
fs.sharedLinksMu.Unlock(); http.NotFound(w, r); return
}
entry.Downloaded++
if entry.DownloadLimit != -1 && entry.Downloaded >= entry.DownloadLimit {
delete(fs.SharedLinks, token)
} else {
fs.SharedLinks[token] = entry
}
fs.sharedLinksMu.Unlock()
// ...serve...Add a regression test that races two requests against a limit=1 token and asserts exactly one 200.
Reporter: Nishant Verma. Reproduced against goshs v2.0.9 (commit 8fc1e91) on 2026-05-27.
AnalysisAI
Download-limit enforcement in goshs (Go Simple HTTP Server) v2.0.9 and earlier can be bypassed by racing concurrent HTTP requests against a limited-use share token, allowing a single token to be redeemed more times than the operator configured. The TOCTOU flaw in ShareHandler means every concurrent goroutine observes the same pre-increment Downloaded counter, each passes the limit check, and each is served the file - completely defeating one-shot or low-count share-link controls. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | Exploitation requires three concrete conditions: (1) the goshs instance has the share-link feature in use and at least one token has been issued with a non-zero `DownloadLimit` - tokens with no limit (`DownloadLimit == -1`) are unaffected by this race since the bypass is only meaningful when a cap is intended; (2) the attacker possesses a valid, unexpired share token - this is the primary prerequisite and is not granted by this vulnerability itself; (3) the attacker can dispatch two or more concurrent HTTP requests within the file-transfer window, which widens naturally with larger files. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | The vendor-assigned CVSS 3.1 score of 5.9 Medium (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N) is consistent with the technical facts. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An attacker who obtains a valid goshs share token - through email interception, browser history exposure, or a forwarded link - issues two or more simultaneous HTTP GET requests to `/?token=<token>` using parallel HTTP clients (e.g., background curl processes). All requests enter the TOCTOU window simultaneously, each independently reads `Downloaded < DownloadLimit` as true before any increment is committed, and each receives a 200 response with the full file. … |
| Remediation | No specific patched release version is named in the available advisory data - operators should monitor the goshs GitHub repository (https://github.com/patrickhener/goshs) for a release superseding v2.0.9 that incorporates the fix described in GHSA-j48m-h7xq-2xpj. … Detailed patch versions, workarounds, and compensating controls in full report. |
Threat intelligence, references, and detailed analysis are available after sign-in.
Same weakness CWE-362 – Race Condition
View allSame technique Race Condition
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-j48m-h7xq-2xpj