Mar 1, 2026·7 min read·15 visits
Unsanitized input reflection in go-httpbin allows attackers to force a text/html Content-Type response containing malicious JavaScript. Patched in version 2.18.0.
A reflected Cross-Site Scripting (XSS) vulnerability exists in mccutchen/go-httpbin (also known as httpbingo) versions prior to 2.18.0. The vulnerability allows remote attackers to execute arbitrary JavaScript in the context of a victim's browser by manipulating the 'Content-Type' header and response body content simultaneously. This issue affects endpoints designed to echo request data, such as /response-headers and /base64, where proper output encoding was not enforced for dangerous content types.
The go-httpbin library, a Go port of the popular httpbin.org service, is widely used by developers to test HTTP requests and responses. The service includes endpoints specifically designed to echo back data provided by the client, allowing users to inspect how their HTTP clients handle various server responses. However, versions prior to 2.18.0 contain a critical logic flaw in how these echo responses are generated, leading to Reflected Cross-Site Scripting (XSS).
The vulnerability, identified as CVE-2025-45286, arises from the application's willingness to accept a user-defined Content-Type via query parameters or path segments while simultaneously reflecting user input in the response body. By default, the application did not differentiate between safe content types (like application/json) and executable content types (like text/html).
When an attacker crafts a request that sets the Content-Type to text/html and injects HTML tags into the response body parameters, the browser interprets the server's output as an HTML document rather than a JSON object or plain text. This causes the browser to parse and execute any JavaScript embedded within the response, bypassing standard same-origin protections and granting the attacker execution context within the victim's session.
The root cause of this vulnerability is the failure to neutralize output based on the response context (CWE-79). Specifically, the /response-headers and /base64 endpoints in go-httpbin function as sinks that reflect untrusted input without adequate encoding when dangerous Multipurpose Internet Mail Extensions (MIME) types are specified.
In the /response-headers endpoint, the application iterates through provided query parameters to construct the HTTP response headers. Crucially, if a user provides a Content-Type parameter, the application applies it to the response header. After setting the headers, the application serializes the entire map of query parameters into a JSON object and writes it to the response body. The code implicitly assumed that because the body structure was JSON, it would be treated as data. However, browsers prioritize the Content-Type header over the body structure. If the header declares text/html, the browser attempts to render the JSON text as HTML, executing valid tags found within the JSON strings.
Similarly, the /base64 endpoint decodes a URL-provided base64 string and writes the raw bytes to the response. It also accepts a Content-Type override. An attacker can encode a malicious HTML payload in base64, append a Content-Type override, and force the server to serve a valid HTML page containing the payload. In both cases, the lack of an allowlist for content types or conditional HTML escaping for dangerous types facilitated the execution of arbitrary code.
The following analysis compares the vulnerable implementation with the remediated code in version 2.18.0. The focus is on the responseHeaders handler, which processes user input to generate headers and body content.
In the vulnerable version, the handler iterates over query parameters, sets them as headers, and then encodes the map directly to the response writer. No check exists to determine if the Content-Type is dangerous.
// Pre-patch logic (simplified)
func (h *Handler) responseHeaders(w http.ResponseWriter, r *http.Request) {
// ... (logic to parse params)
// 1. User input sets Content-Type header directly
for k, v := range params {
w.Header().Set(k, v[0])
}
// 2. User input is written to body as JSON
// If Content-Type is text/html, browser executes scripts in JSON strings
encoder := json.NewEncoder(w)
encoder.Encode(params)
}The fix introduces a safety check using mustEscapeResponse. If the requested Content-Type is not in a safe allowlist (e.g., application/json, text/plain), the application HTML-escapes the body content before writing it. This neutralizes <script> tags by converting them to <script>.
// Post-patch logic (Commit 0decfd1)
func (h *Handler) responseHeaders(w http.ResponseWriter, r *http.Request) {
contentType := getContentType(params)
// 1. Check if the requested content type requires escaping
if h.mustEscapeResponse(contentType) {
// 2. Use a buffer to encode the JSON first
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(params)
// 3. HTML Escape the entire body string before writing
w.Write([]byte(html.EscapeString(buf.String())))
} else {
// Safe content types (json/plain) processed normally
json.NewEncoder(w).Encode(params)
}
}The patch also defines safeContentTypes map including application/json, text/plain, and application/octet-string. Any type not in this list triggers the escaping logic.
Exploiting this vulnerability requires crafting a URL that sets the Content-Type to text/html and includes a JavaScript payload in a reflected parameter. This attack vector relies on the victim clicking a malicious link (Reflected XSS).
go-httpbin instance.The following URL demonstrates the attack against the /response-headers endpoint:
http://vulnerable-httpbin.com/response-headers?Content-Type=text/html&xss=<script>alert(document.domain)</script>Execution Flow:
Content-Type=text/html and sets the response header Content-Type: text/html.xss to the JSON map: {"xss": "<script>alert(document.domain)</script>", ...}.text/html header. It ignores the JSON syntax and scans for HTML tags.<script>... and executes the JavaScript payload.A similar attack works on the /base64 endpoint by encoding the payload <html><script>alert(1)</script></html> into Base64 strings and appending it to the URL path, effectively turning go-httpbin into a malicious file host.
The impact of this vulnerability is classified as Medium (CVSS 6.1) because it requires user interaction and does not directly compromise the server's infrastructure. However, the consequences for the client/victim can be significant depending on the context in which go-httpbin is deployed.
Confidentiality Impact (Low/High): If the vulnerable instance runs on a domain that shares cookies or local storage with sensitive applications (e.g., an internal testing subdomain test.corp.com sharing context with admin.corp.com), an attacker could steal session tokens or authentication credentials. The S:C (Scope: Changed) metric in CVSS 3.1 reflects this potential to impact other resources in the browser's security context.
Integrity Impact (Low): An attacker can modify the visual presentation of the page or perform actions on behalf of the user, such as submitting unauthorized requests to other services accessible from the victim's browser (CSRF via XSS).
Availability Impact (None): This vulnerability does not allow for denial of service or server-side resource exhaustion. The server continues to function normally while serving the malicious payload.
The vulnerability is addressed in go-httpbin version 2.18.0. The primary remediation strategy is to upgrade the library to this version or later. The patch enforces HTML escaping for any response where the Content-Type is not explicitly allowlisted as safe.
The patch introduces a configuration flag, UNSAFE_ALLOW_DANGEROUS_RESPONSES (or the command-line flag -unsafe-allow-dangerous-responses). This flag defaults to false. Administrators must ensure this flag remains disabled in production environments. Enabling this flag reverts the application to the vulnerable behavior, negating the security fix.
For environments where immediate patching is not feasible, the following mitigations are recommended:
/response-headers and /base64 containing Content-Type=text/html or other dangerous MIME types (e.g., image/svg+xml, text/xml).go-httpbin instance. A policy such as default-src 'none'; script-src 'self'; would prevent the execution of inline scripts injected via this vulnerability.go-httpbin instances to trusted internal networks or authenticated users via VPN/Zero Trust proxies, reducing the likelihood of a successful phishing attack.CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
go-httpbin mccutchen | < 2.18.0 | 2.18.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| CWE Name | Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') |
| CVSS v3.1 | 6.1 (Medium) |
| Attack Vector | Network (Reflected) |
| EPSS Score | 0.00012 (1.47%) |
| Exploit Status | PoC Available |