Skip to main content

Caddy CVE-2026-52846

| EUVDEUVD-2026-38556 MEDIUM
Improper Encoding or Escaping of Output (CWE-116)
2026-06-16 https://github.com/caddyserver/caddy GHSA-vcc4-2c75-vc9v
4.2
CVSS 3.1 · Vendor: https://github.com/caddyserver/caddy
Share

Severity by source

Vendor (https://github.com/caddyserver/caddy) PRIMARY
4.2 MEDIUM
AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
vuln.today AI
4.2 MEDIUM

AC:H reflects non-default configuration prerequisites (templates enabled, stripHTML on untrusted input); UI:R required as victim must render the output; no availability impact applies.

3.1 AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
4.0 AV:N/AC:H/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/caddyserver/caddy).

CVSS VectorVendor: https://github.com/caddyserver/caddy

CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
Required
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
None

Lifecycle Timeline

2
Source Code Evidence Fetched
Jun 17, 2026 - 00:30 vuln.today
Analysis Generated
Jun 17, 2026 - 00:30 vuln.today

DescriptionCVE.org

Summary

Caddy’s stripHTML template function cannot reliably remove all HTML tags from input strings. Certain malformed HTML, such as <<>img src=x onerror=alert()>, can bypass the tag-stripping logic, potentially leaving dangerous content in the output if it is later rendered as HTML. This may allow client-side XSS in cases where untrusted strings are rendered unsafely.

---

Details

The vulnerability originates from funcStripHTML in:

caddy/caddy/caddyhttp/templates/tplcontext.go

go
func (TemplateContext) funcStripHTML(s string) string {
    var buf bytes.Buffer
    var inTag, inQuotes bool
    var tagStart int
    for i, ch := range s {
        if inTag {
            if ch == '>' && !inQuotes {
                inTag = false
            } else if ch == '<' && !inQuotes {
                // false start
                buf.WriteString(s[tagStart:i])
                tagStart = i
            } else if ch == '"' {
                inQuotes = !inQuotes
            }
            continue
        }
        if ch == '<' {
            inTag = true
            tagStart = i
            continue
        }
        buf.WriteRune(ch)
    }
    if inTag {
        // false start
        buf.WriteString(s[tagStart:])
    }
    return buf.String()
}

POC

Caddyfile setup

:8080 {
    root * ./site
    file_server
    templates
}

Template file (index.html)

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>StripHTML Bypass Test</title>
</head>
<body>
    <p>{{ stripHTML "<<>img src=x onerror=alert('XSS')>" }}</p>
</body>
</html>

The payload exploits the false start branch to smuggle a literal < back into the output, then uses the following > to terminate the parser’s tag state, leaving a valid <img ...> tag behind.

Tested in v2.11.3

Impact

Malformed HTML can bypass stripHTML, potentially allowing arbitrary HTML or JavaScript to be rendered if the output is used unsafely, leading to client-side XSS.

AI Disclosure

AI assisted in writing the report description; however, the discovery of the issue has been done manually.

AnalysisAI

The stripHTML template function in Caddy web server can be bypassed with malformed HTML input such as <<>img src=x onerror=alert()>, allowing injected HTML tags to survive sanitization and reach the browser as executable markup, resulting in client-side XSS. Affected versions are Caddy v2 <= 2.11.3 and Caddy v1 <= 1.0.5; exploitation requires the templates middleware to be active and untrusted input to be processed via stripHTML and rendered without further escaping. A publicly available proof-of-concept exists per the GitHub security advisory GHSA-vcc4-2c75-vc9v; no active exploitation is confirmed in CISA KEV at time of analysis.

Technical ContextAI

The vulnerability is rooted in CWE-116 (Improper Encoding or Escaping of Output). The affected code is funcStripHTML in modules/caddyhttp/templates/tplcontext.go, which implements a hand-rolled character-scanning HTML stripper in Go. The parser tracks state using inTag and tagStart variables: when a < is encountered inside an existing tag context, it is treated as a 'false start' and the substring from tagStart to the current position is flushed to the output buffer - including the original < character. The crafted payload <<>img src=x onerror=alert()> exploits this: the first < sets inTag=true, the second < triggers the false start branch and writes a literal < to the buffer while resetting tagStart, the following > then closes what is effectively the empty pseudo-tag <>, and the remaining img src=x onerror=alert()> is written as non-tag content. The browser reassembles this as a fully valid <img> element. The affected packages are pkg:go/github.com_caddyserver_caddy_v2 and pkg:go/github.com_caddyserver_caddy.

RemediationAI

For Caddy v2 deployments, upgrade to version 2.11.4 or later, which contains the upstream fix per the GHSA-vcc4-2c75-vc9v advisory at https://github.com/caddyserver/caddy/security/advisories/GHSA-vcc4-2c75-vc9v. No vendor-released patch is available for Caddy v1 (1.0.5 and earlier); v1 users should migrate to the v2 branch where feasible. As immediate compensating controls: disable the Caddy templates middleware entirely if not required (remove the templates directive from the Caddyfile), avoiding the attack surface completely with no functional trade-off for static file serving. If templates are required, avoid passing untrusted user input through stripHTML for security-sensitive sanitization; instead use Go's html/template auto-escaping or a dedicated HTML sanitization library such as bluemonday. Do not rely on stripHTML as a security boundary - it is a display-formatting helper, not a sanitizer.

Share

CVE-2026-52846 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy