Relative Path Overwrite: Turning URL Confusion Into CSS Injection
Relative Path Overwrite feels odd the first time you see it. No file is overwritten. Instead, a browser is convinced that an HTML response is a stylesheet because the page uses a relative CSS URL and the server tolerates extra path material.
The bug needs several pieces to line up, so test it as a chain rather than filing each weak signal.
The three ingredients
First, find a page with a relative stylesheet:
<link rel="stylesheet" href="assets/site.css">
On /profile, the browser requests /assets/site.css. If you load /profile/anything/, it may resolve that same link as /profile/anything/assets/site.css.
Second, the router must return the profile HTML for that bogus path instead of a 404. Framework catch-all routes, path-info handling, and ignored semicolon segments are good candidates.
Third, attacker-controlled text must appear in the HTML response in a form the CSS parser can use. A reflected path, username, search term, or error message can supply the payload.
Walk the browser, not just Repeater
Take a page such as /users/me and append path segments: /users/me/x/, /users/me;x/, or encoded separators. View the network panel. If the supposed stylesheet request returns text/html containing the page again, you have the routing half.
Modern browsers often refuse a stylesheet with the wrong MIME type, especially under X-Content-Type-Options: nosniff. That header can kill the chain. Test real browser behavior in the browser modes the program supports; changing a response to text/css in your proxy proves nothing about production.
For reflection, a CSS canary is enough:
{}body{background:#c0ffee}
Encode it into the controllable path or parameter, then make the browser request the confused page. A visible background change proves that the HTML was parsed as CSS. Avoid external url() callbacks until the program permits them; a local visual change is cleaner.
Quirks worth trying
Document modes made RPO famous in older Internet Explorer, but don't assume the class is dead. Embedded webviews, legacy enterprise browsers, and unusual MIME handling still surface it. Test a <base> tag too: a missing or attacker-controlled base changes every relative resource.
Authentication can raise impact. If a logged-in-only page reflects a CSRF token or personal value into parseable CSS, selector tricks may allow data exfiltration one character at a time. Do not extract real secrets. Use a token or profile field belonging to your own account and show one controlled match.
Reporting the whole chain
Your proof should show the crafted page URL, the derived stylesheet URL, its HTML response, and the resulting CSS effect. Mention browser and version. Without browser execution, you only have permissive routing plus a relative path—not RPO.
Good fixes are straightforward: use root-relative or absolute asset URLs, return 404 for unexpected path suffixes, set X-Content-Type-Options: nosniff, emit the correct content type, and encode reflected path data. Any one may break your payload; applying all of them removes the fragile assumptions that created it.