XSS Part 4 – How XSS could be used to gain RCE (Remote Code Execution)

In my previous post we showed how XSS can be used to hit endpoints in the context of the logged in user and the damage that could be done with this.

The er great thing about security issues is that whilst they can be bad enough individually they can get really bad when combined together.

In this post I will show a theoretical example using my deliberately vulnerable application hackthecat where we’ll pair XSS with another security issue to end up running arbitrary code on the web server (Remote Code Execution or RCE).

RCE is pretty much as bad as a security issue can get for an application – especially if it’s possible without login details!

Obligatory Warning/Nag

Do not under any circumstances try any of what I’ll discuss here on applications you don’t have permission to. It’s almost certainly illegal and you could/will end up in a lot of trouble..

A much better idea with less jail/police is to use something like my deliberately vulnerable node application that you can download from https://github.com/alexmackey/hackthecat or a machine provided by a service such as https://www.hackthebox.com/.

Previous Posts

This is my 4th post on a series of posts about XSS but if you’ve missed the previous posts here’s a helpful list so you can go back and read them and hopefully they’ll assist you with preventing XSS issues within your applications:

Part 1 – https://blog.simpleisbest.co.uk/2022/02/19/xss-cross-site-scripting-part-1-what-is-xss/

Part 2 – https://blog.simpleisbest.co.uk/2022/04/02/xss-part-2-attack-and-defence/

Part 3 – https://blog.simpleisbest.co.uk/2022/04/25/xss-part-3-session-riding-csp-policies-sameparty-and-samesite/

De-serialization Vulnerability

OK but how could an XSS issue lead to running any old code we like?

We’ll get to that but first I need to introduce you to a well-known deserialization issue in a very old library that is no longer maintained called node-serialize, version 0.4 (scarily at the time of writing npm page says it had nearly 1400 downloads last week which er let’s hope is not in production systems).

Node-Serialize’s purpose in life is surprisingly enough to serialize/de-serialize objects to and from JSON.

It has an interesting feature where it can serialize functions on an object.

Unfortunately it also offers the ability to execute the serialized function during the deserialization process by making an eval call.

This only happens if the functions property name begins with the string “_$$ND_FUNC$$_”. The below screenshot shows how this works (FUNCFLAG is defined as “_$$ND_FUNC$$_” earlier in the code):


This logic means that if we can feed some malicious JSON that ends up in a node-serialize deserialize call then we should be able to get node-serialize to execute it!

Note you can checkout a simple example of this particular issue and write up at https://snyk.io/test/npm/node-serialize and https://www.exploit-db.com/exploits/50036.

Writing our exploit for HackTheCat

It just so happens that HackTheCat has an endpoint that accepts a blob of JSON (I know as I put it there!) and you guessed it uses node-serialize to deserialize it.

In our example application imagine an endpoint intended to create products from a JSON request. Providing valid JSON will then create a new product in Products table.

This is what the end point looks like:


Using XSS we can execute a call to this endpoint and hopefully run some code on the server!

But why do we need XSS to do this?
Well we might not if the endpoint is unprotected and can be called by anyone but in the real world it’s more likely an endpoint like this would require an authenticated user with higher level privileges.

An attacker might use XSS in the hope a user with higher level privileges will stumble upon and trigger this endpoint.

Keep things simple

I quickly found that when playing with stuff like this it’s a good idea to do the simplest thing possible first and then build upon this.

If we were to try something more complex first like making a connection back to the attacker machine and it fails we don’t know whether this is because our approach was wrong or maybe there are other things such as firewalls preventing the connection.

We’ll first try to create a file using the touch command (Linux command that creates/modifies/sets properties on a file – this won’t work on Windows unless you have WSL running).

Some quick notes

I’m going to use a Linux based machine for this example as both the attacking and victim machine (yes we’ll connect back to ourselves but the approach is the same on distinct machines).

Ok we want to do the following:

  • Run the code when the page has loaded so we’ll add an event listener (not strictly necessary for this but other XSS attacks may require DOM elements to exist/be loaded)
  • We need to pass a valid product object that the endpoint expects just in case there is logic that expects various properties to exist (that’s the productName, smallImageUrl bit in the code below)
  • Pass any credentials the user may have (in the real world this is the sort of endpoint which is hopefully authenticated). We’ll use the credentials “include” option in our fetch request
  • We want to know when our call has completed so we’ll also make a call to http://localhost:8000/callback when the request has been made. This would also be useful if we were scripting the whole attack

Below is some dodgy code I wrote to do this:

Now once you’ve written something similar you’ll need to do the following to trigger it:

Log into hackthecat application

Use Netcat on your attacker machine to listen to port 8000 for the callback that will occur once the script has run (nc -lvnp 8000)

Next go to the products page, add a comment section and enclosing the XSS payload within a script block so it executes (e.g. <script>code here</script>) and then submit the comment.

All being well you should then see that your callback endpoint has been hit:

You should then find a file created on the server:

Oooo its looking good but what else might an attacker do?

Reverse Shells

Whilst the goals of attackers will vary having interactive/command line access to a machine is going to open a lot of possibilities from exploring files/services on the victim machine to using the machine as a stepping stone to attack other servers on the same network so it’s likely to be many attackers end game.

Most web servers will likely have a firewall or network rules between them and the Internet (or should do) that restrict access to specific ports or traffic such as 80 and 443.

These rules however are generally a lot less restrictive the reverse direction e.g. from the web server to the internet and also certain ports will likely need to be open for the server to do anything useful.

A reverse shell connects a pipe from the server/victim to the attacker allowing the attacker to send commands of their choosing.

There are lots of different ways of creating a reverse shell depending on the operating system, framework used, CPU architecture etc but in our example we know we’re working with a Node application and can execute node code so we’ll use a node reverse shell (I found this particular one by googling “node reverse shell” but there’s a great list here: https://book.hacktricks.xyz/shells/shells).

You can read more about reverse shell’s here: https://www.acunetix.com/blog/web-security-zone/what-is-reverse-shell/

Getting a reverse shell via hackthecat

Before we tell the victim machine to connect to us we’ll need something for it to connect to so let’s use Netcat to listen in on port 9000 for a connection (if you are trying this on two separate machines remember to check any firewall settings and allow access to the listening port from the victim machine):

Next we’ll use the same code we used in our example above but instead of using the touch command we’ll run a node reverse shell to port 9000:

If you then follow the same process as above example you should find you get a connection back! (yes I’m aware I’ve connected to myself but didn’t want to setup another machine – change these details as per your own setup..)

This is interactive so you can run various commands – lets run pwd & id (note you’ll want to upgrade this to a full shell but that’s another topic entirely):


We’ve shown how an attacker could use an XSS issue, combine it with a deserialization issue to end up running code on the host of the application and even get an interactive session on it!

This is also a realistic issue – here’s the first real world example I found from a quick Google: https://blog.sonarsource.com/magento-rce-via-xss

Well hopefully by now I’ve convinced you that XSS can be really bad but what should we do to prevent this?

  • Always encode output then the XSS attack would not have worked in the first place
  • Ok this is not XSS but we couldnt have done the RCE without this – monitor the third-party components you use – npm flags that node-serialize has a known security issue and third-party solutions such as Synk and Whitesource will too
  • Validate and filter input from untrusted sources – do you need to be able to accept HTML/JS from a comments textbox – probably not but note its pretty hard to do this as we’ll see shortly
  • Define CSP policies that allow just the minimum functionality necessary for a page – if an attacker found a way round our encoding/filtering this could prevent or limit what they can do
  • Use HTTPOnly and secure option on session cookies to make the attackers life harder and prevent session hijacking/interception
  • Transmit scripts and cookies over secure connection
  • Have a firewall/network restrictions between your server and internet (duh)
  • Restrict outbound connections from the server to just what is needed – more advanced firewalls that can inspect traffic may have prevented suspicious connections like our reverse shell
  • Don’t run web applications as root/high privileges – if in our example the hackthecat application was running as root when we’d performed this attack then attacker has free reign on the machine. If they were able to perform this attack as a lower privileged user they are going to have to find a way to elevate their privileges if they want to do much
  • The script we used was actually quite long and having field length restrictions/validation would have made the attackers job harder

Next Post

Some folks believe that they can filter out XSS attacks with simple string replacement approaches taking out script tags and event handlers.

In my next post we’ll look at some of the literally millions of ways of creating XSS attacks and explore some weird corners of HTML and JavaScript.

XSS Part 3 – Session Riding, CSP policies, SameParty and SameSite

In my previous post XSS Part 2 Attack and Defence we looked at how an attacker can use XSS to steal a users session token from a cookie and then hijack it.

We then enabled the HTTPOnly option on our session cookie preventing malicious JavaScript from accessing the cookie value and sending it to the attacker.

Whilst HTTPOnly option will prevent this scenario unfortunately there is a another we need to address.

Obligatory Warning

Before we go any further obligatory disclaimer/warning/nag – do not perform any of the attacks we’ll discuss on sites you do not have permission to otherwise you could get into a lot of trouble or even jail.

A much better option to explore this topic is to use something like my deliberately vulnerable application https://github.com/alexmackey/hackthecat or HackTheBox.

Back to the content..

Ok where we?

Ah yes so the big issue we need to address occurs because if an attacker can execute code on a web page then it will operate as if it was legitimate code written by the site author with the same access to cookies, local and session storage.

Now browsers under most circumstances will automatically send cookies set by the site the user is browsing with any requests made via XHR & fetch requests (note that fetch requests require this to be explicitly enabled).

This can be a big issue when an XSS problem exists as because if a cookie value is used to identify a user (like in our example) and an attacker can get code to run on a page then they can call any API endpoint in the context of the logged in user!

Uh oh..

An attacker could then use this to:

  • Hit an API to change the user’s password to a known value (we’ll do this in a minute!)
  • Perform any valid action the user can – maybe this is transferring money in a banking application or purchasing products and sending them to the attacker or maybe exploiting some dumb Web3 NFT thing..
  • Perform additional XSS attacks but as the user themselves e.g. messaging other users XSS nastyness
  • ..and a thousand other horrendous things!

So how does this work?

Creating this attack is trivial and any web developer will have written similar (but probably better) code for completely legitimate purposes.

Below is an example of code that in the hackthecat application will change the password of the logged in user.

If we can get this to run in a page the user viewing it will have their password changed!

var formData = new FormData();
formData.append("username", "user");
formData.append("newPassword", "changed!"); 
formData.append("confirmNewPassword", "changed!"); 
var request = new XMLHttpRequest();
request.open("POST", "/user/change-password");

You can of course write this using fetch as well just remember you’ll need to use the credentials option set to “include” or “same-origin” otherwise fetch wont send credentials/cookies.

To see this in action yourself simply log into the hack the cat application (user and pass will do), go to a product and add a review with the above code.

When this runs you’ll then find this has changed the users password – here’s an exciting before and after mysql query screenshot – note how the password is “changed!” in the second query:

Local/Session Storage and Session Tokens

Some sites use local or session storage to save a users session token and then append it to requests rather than storing tokens in a cookie.

I think generally as the XSS code is executing in the context of the user this approach is unlikely to provide any additional protection. In fact it could make things worse.

I’m of two minds about storing session tokens in local storage. Whilst you are arguably in trouble if an XSS issue exists (due to the attack type we are currently discussing) you lose the benefit of how cookies work and expire and additional protection such as HttpOnly or SameSite options that are baked into modern browsers.

CSRF (Cross Site Request Forgery) Tokens

Some of you might be thinking oh this is where CRSF tokens come in – they’ll save me!

CSRF tokens are intended for preventing a different issue (you can read more about them here: https://portswigger.net/web-security/csrf/tokens).

CSRF tokens are likely implemented as a special value stored in a hidden form field that is sent with any requests and compared server side.

To circumvent this protection will require the attacker to read a value of a hidden form field which er really isn’t too tricky so in respect to this attack doesn’t really provide any protection (except against some very lazy attackers).

You should still however use CSRF tokens as they will of course offer protection against CSRF attacks – exactly what they were intended for.

What can we do to prevent this attack?

Apart from preventing XSS issues in the first place probably the most effective protection against this particular attack is to ensure that critical actions such as changing a password enforce the user to provide their original password first as hopefully the attacker and script will not have access to this.

I guess you could also implement a CAPTCHA but no-one wants to count how many Volvo’s are in a crappy picture displayed in a 16×16 grid – no they really don’t – do better.

We are however going to make the attackers life a lot harder by using Content Security Policy (CSP to its friends).

CSP headers (Content Security Policy)

CSP policies allow the client application fine grained control of what should and should not be running on a page and from where.

CSP policies are supported in every major browser and in a limited form in IE10+ (which hopefully you don’t have to support).

CSP policies can be set either in HTTP headers or meta tags and look something like this:

Content-Security-Policy: script-src https://simpleisbest.co.uk/

The above policy says scripts can only run from simpleisbest.co.uk and some other behaviours I’ll discuss shortly.

Below is the meta tag equivalent:

<meta http-equiv=”Content-Security-Policy” content=”script-src https://simpleisbest.co.uk‘ ‘self’;”>

What can you do with CSP policies?

Let’s say we have set the following CSP header on my site simpleisbest.co.uk:

Content-Security-Policy: script-src https://simpleisbest.co.uk/

Adding this header to HTTP requests will do the following:

  • Any inline scripts will be blocked (unless explicitly enabled with ‘unsafe-inline’ option) – this would prevent the XSS attack we have been discussing!
  • Inline event handlers e.g. onclick=”doSomething()” will be blocked preventing another attack vector
  • If the attacker tried to load a script from anywhere but simpleisbest.co.uk it’s going to be blocked
  • Several script execution methods such as eval(), Function, setTimeout, setInterval, setImmediate are blocked unless specifically enabled with unsafe-eval option
  • It prevents an attacker sending data to another server with html forms

Now the chances are when you first implement CSP policies especially on a site that has been around for a while some of these settings will break the site – you’ll find all sorts of dubious approaches in your code and perhaps third-party libraries doing some weird stuff!

Whilst you are first implementing CSP policies I suggest instead of using “Content-Security-Policy” I suggest you use “Content-Security-Policy-Report-Only” – you’ll see stuff that CSP would have stopped in the browser console instead of it being blocked making it easier to fix it up.

Enabling specific inline scripts

If for some reason you wanted to enable specific inline scripts (really?) but benefit from the protections CSP offers you can do this by adding a nonce to your inline script and then specifying the nonce to be expected like so:

Content-Security-Policy: script-src 'nonce-test'
<script nonce="test">

Ensure only specific scripts can be loaded

CSP policies allow us to ensure that only specific versions of scripts can be loaded by specifying an expected hash of the script like below (remember to include script tags, whitespace and that this is all case sensitive):

Content-Security-Policy: script-src 'sha256-yourSha256hashhere'

This is probably a good idea if you are referencing any scripts especially those on third party servers. This way you can be sure what your users will get is exactly as expected!

The downside of course is that its going to require some maintenance when scripts and assets are updated but it’s a small price to pay.

Which policies should I use?

CSP policies offer a huge number of settings to tweak.

Ideally, you want to only allow the minimum necessary for your site to function – this will reduce the surface attack area of your site and make the attackers life really hard if you can limit what options they have to utilise.

OWASP list the following CSP policy on their cheat sheet page (https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)

Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'self'; form-action 'self';

This will do the following:

  • All resources must be in same domain as document
  • No inline script, evals for script and style
  • No need for other sites to frame
  • No form submissions to external sites
  • Prevents loading of non ajax and CSS resources

This seems a pretty good base to start from and adapt to your purposes.

Adding a CSP Policy to HackTheCat

Ok let’s add a simple CSP policy to hackthecat site so we can see the impact.

We’ll do this directly in homeRoutes.js.

Add the following line before the res.render call in homeRoutes.js:

res.set("Content-Security-Policy-Report-Only", "default-src 'self'");

Note really you want this header to apply to every response so this isn’t the best way to do this as it will only apply to this endpoint. Also note that there are some third party express libraries to do this.

Once you’ve added this, restart the app, navigate to the page and you will then see something like the following in the browser console showing the inline script has been blocked:

Changing this to “Content-Security-Policy” mode will also display information about the referenced stylesheet hosted on fonts.googleapis.com being blocked as we haven’t said this is OK in our policy:

Deprecated approaches

You may come across a reference to various other XSS protection headers/options such as

  • X-XSS-Protection
  • X-Content-Security-Policy
  • X-Webkit-CSP
  • X-FrameOptions

These are all now deprecated and superseded by CSP Policies (for X-FrameOptions you can use frame-ancestors to provide protection against frame based evilness).

Using these legacy approaches can even cause security issues so don’t use them.

SameSite and SameParty

Before we wrap up this post I just wanted to tie up a loose end from the last post on SameSite and SameParty cookie options that you may have seen in the browser dev tools:

SameParty is a proposal designed to deal with the issue that modern sites are often served over multiple domains owned by the same entity. The intention is to allow developers more control over the privacy boundaries of where cookies are shared.

As this is still a proposal I’m not going to worry too much about it at this stage but its an interesting idea. You can read more about this proposal here: https://chromestatus.com/feature/5280634094223360.

SameSite however is ready to use now and supported in all major browsers apart from our old friend IE11.

Same-site is designed to protect against CSRF and potential information leakage by allowing developers to control that it should only be sent from requests initiated by the domain it was created in.

This is different to the default cookie behaviour where cookies are automatically sent to the same domains that they were set in.

Same site has 4 main options:

  • Strict
  • Lax
  • None
  • Unspecified (behaves the same as lax)

What do these SameSite options do?

Let’s assume a scenario where:

The user has a cookie set by my domain simpleisbest.co.uk

The user is on an external blog site hosted at wordpress.com

If the cookie had SameSite set to strict then the following will occur:

  • If the blog site references an image on simpleisbest.co.uk then no cookies would be sent to simpleisbest.co.uk from this request
  • If the user were to follow a link from wordpress to simpleisbest.co.uk then on the first request they make no cookies will not be sent offering protection against CSRF attacks that rely on the user being logged in and clicking a malicious link
  • Once the user is on the site e.g. they click a link on simpleisbest.co.uk then cookies will be sent as they are now in a first-party context (e.g. on the site that set the cookie).

When SameSite is set to lax then:

  • If the blog site was referencing an image and requested an image from simpleIsBest.co.uk then no cookies would be sent as per strict mode above
  • Unlike strict mode however if the user follows a link to simpleIsBest.co.uk then cookies will be sent.


When SameSite is set to None:

  • If the blog site was referencing an image on simpleIsBest.co.uk then cookies would be sent with the request
  • If the user follows a link from another site to the site that set the cookie (simpleIsBest.co.uk) then cookies will be sent
  • Note you must set the secure attribute on cookies when using None


Unspecified now behaves the same as Lax (recent change).

Ok let’s wrap things up – in this post we looked at how XSS can without further protections in place allow us to execute actions in the context of the user such as change their password or call arbitrary endpoints via XHR or fetch.

We then looked at how CSP policies provide fine grained control of what functionality can run in a page and can prevent or at least make an attackers life a lot harder then finally we looked at SameSite and SameParty cookie options.

In my next post I’ll show how an XSS issue can be exploited to run arbitrary code on a webserver which is pretty much as bad as things can get!

XSS Part 2 – Attack! (and defence)

In my first post we looked at what XSS is, the different types and summarised some of the potential impacts it could have.

In my next few post’s we’re going to explore common XSS attacks and how we can prevent or mitigate them.

As always don’t try this stuff on sites you don’t have permission to as its almost certainly illegal and you could get into a lot of trouble..

A much better idea is to use something like my deliberately vulnerable node application that you can download from https://github.com/alexmackey/hackthecat and that’s exactly what we’ll be using in this post.

We’ll start off with some basic stuff and build upon it over the coming posts.

Finding XSS Issues

First of all, an attacker will need to locate an XSS issue in your application.

XSS issue can occur under the following conditions:

  • There is input controllable by an attacker
  • The input appears somewhere on a page that will be processed by a browser (or something browser like that will execute HTML/JS)
  • The input is not adequately validated/sanitised allowing tags such as <script> (note there’s many options here so do not think XSS is confined to just the script element)
  • When the input is eventually output to the page it is not encoded
  • Other protections such as CSP are not in place. We’ll talk more about CSP in future but for now know that CSP can prevent or mitigate some XSS attacks

Finding all the places XSS can occur in a large application can be time consuming and difficult so there are many different commercial and free engines that automate this process. These range from the freely available tools such as OWASP ZAP to affordable options such as Burp Professional up to tools costing thousands of dollars (and more).

A tool likely wont find everything and some XSS issues will require multiple steps to occur for it to manifest.

An example would be an XSS issue existing in a user registration form where everything is done right however elsewhere the application outputs the username in an unencoded form leaving an opportunity for XSS to occur if an attacker were to enter a malicious username.

However I digress and in our example we don’t need any of these automated scanners and we’ll find the issues manually by trying to add some HTML and see if it is output encoded.

Browsing around the HackTheCat site we are running locally we come across a page that allows users to add a review to a product.

This page is probably a good candidate to test as we can control the review text and its likely to be output to this page:

We wont know however whether this page is vulnerable to XSS until we try entering some values. It may be that the text we’ll enter has been encoded properly or sanitised on the back-end which will put a stop to our evil plans 😦

To see if the review text is encoded we could add some simple styled HTML or a script block to write to the console or that old classic the native JavaScript alert box aw yeah.

I guess a real attacker would probably try something subtle first to avoid alerting the team looking after the site.

In our HackTheCat cat bicycle shop example we’ll add some simple HTML in our review text:

<h3>Cats suck! Dogs Rule!</h3>

We’ll add this text and sure enough we see some larger formatted text so it’s likely this is not be encoded properly – we’ve found our XSS!

At this point we’ve added some HTML which whilst has possibilities around defacing the site, mis-information to promote dogs to our feline readers is not as useful or convenient as being able to run some JavaScript directly so let’s see if we can add a script block too.

We’ll do this by adding a message to write to the console like below:

Now if we press F12 to bring up the developer tools we can see we have a message that has been written to the console:

Some sites may allow some HTML tags to format input but filter out more risky things like <script> and JavaScript so you’ll probably want to play with both.

Back to our example – as we can see via output in the console we’re in business to do much more devious stuff now which we’ll get onto shortly.

Before we get onto this let’s see how this issue occurs in the first place as its key to understanding XSS.

XSS issues can occur when we accept input from the user, do not sanitise it, don’t encode it and output it somewhere where it will be executed

If we do a View Source on the page we’ll see something like the below:

This looks just like we’d written this script block ourselves in the applications code – and it will run in the same way as if we had!

In our HackTheCat application the page responsible for displaying user reviews is detail.ejs:

HackTheCat uses a templating engine called EJS (https://ejs.co/) which stands for Embedded JavaScript templating.

In this instance there’s no problem with EJS engine – the problem is in the choice of methods used to output the product review.

Here I’ve deliberately used the wrong output method to demonstrate this issue.

EJS offers two main ways of outputting content <%- (outputs raw, un-encoded output) and <%= (encoded output).

Why would you need raw output?

We’ll for the most part you probably don’t but it could be needed for certain scenarios for example the EJS documentation mentions using it to avoid double encoding output or where you can be sure the content you are outputting is not malicious.

For most circumstances however when using EJS you’ll want <%= to avoid XSS issues.

So does using a framework encoding method like this mean you can avoid XSS?

Most of the time.

However there’s certainly been issues in templating engines, frameworks and browsers and likely will again. Just last week Chromium had a parser issue and there’s been several XSS/client side template issues in frameworks such as Angular.

I dont think we’ve seen the last of these either and as per defence in depth you probably want to consider sanitising input too.

In a later post we’ll look at what frameworks and templating engines are actually doing underneath the cover and the native DOM methods performing this work.

Anyway let’s change the code to use <%= and see the difference in behaviour and output:

If you are using the containerised version of HackTheCat remember to rebuild with the –build flag to get this change:

When you have made this change navigating to this page should now show something like the following and no message in the console:

If we view the source we’ll see it looks a bit different too as the special characters have now been encoded properly:

Compare this with the unencoded version:

Ok this seems pretty straight forward.

There’s certainly been (and will continue to be) issues like this but XSS is can be more subtle than this.

XSS can often occur in less obvious places with values being passed into dialogue boxes or perhaps from other locations such as a user registration form.

Before we move on lets change back the output method so our comment is unencoded as I want to show you some more issues and how we can mitigate them:

Cookie Stealing/Session Hijack

Another XSS attack you should be aware of is that if an XSS issue exists then it can be used to try and steal cookies and ultimately a users session (assuming user sessions are session cookie based).

There’s a lot of different ways & libraries to manage sessions but in our HackTheCat example I’m using express-sessions to manage a users session within the application and sessions are maintained using a cookie.

We can see this in action by logging into the application (by default credentials are username “user” and password “pass” or “admin” and “pass”).

If we bring up the dev tools (F12), select the application tab and look at cookies we’ll see something like the following:

Here we can see that express-sessions has set a cookie called connect.sid containing a random ID. This will be used to identify a user.

Now if we were to copy the value of this cookie and set a cookie with the same name on another machine (and no additional checks/protections were in place and the session had not expired) then as far as the web application is concerned we will be the same logged in user – try it using incognito mode if you want.

This means if an attacker can steal a cookie then they can act as the logged in user.

What’s even worse is if the user had higher privileges e.g. an admin user was browsing the site then the attacker could have access to administrative functions that could be very damaging.

This scenario can occur with XSS using a simple script (or we could just use an img element directly) like the following:

If you look in the browser network tools you’ll then see this session cookie float off to the attackers server where they can gleefully pick it up and pretend to be various users:

Now we can prevent this by making use of some settings on the cookie.

If you open up app.js you’ll see the following:

I’ve deliberately used some bad, unsecure options to demonstrate these concepts.

Luckily many frameworks have some good default options out the box to help keep your applications safe but dont assume, check!

The key option here to prevent this particular attack is that HttpOnly flag.

HttpOnly flag (first implemented in 2002 by Internet Explorer team in IE 6 SP1!) tells the browser that the cookie can only be access from HTTP and not by JavaScript on the page.

We can see this flag wasn’t set in the browser tools when we look at the cookie:

Lets set this option now by updating the session configuration in app.js:

In the real world you’d probably want to set that secure flag too to ensure the cookie is only sent over a secure HTTPS connection so it cant be easily intercepted but lets ignore this for now.

Once you’ve made this change if you now look at this cookie we’ll see a nice HTTP Only tick:

And if we then look at the page source we’ll see there is no cookie present:

Trying to access the cookie properties in the browser console will return nothing:

OK so we’ve prevented that attack and you may think you are out the woods by adding these additional protections but as I’ll demonstrate in my next post you certainly are not.

In my next post we’ll talk about SameSite and SameParty flags, the other bad stuff an attacker could do despite HttpOnly flag and then show how this can be chained with another issue to get RCE or remote code execution but for now that’s all folks!

Advanced Web Attacks and Exploitation (AWAE/OWSE) Course Review

I recently took Offensive Security’s Advanced Web Attacks and Exploitation (AWAE) course and attempted the OWSE exam.

I successfully completed Offsec’s better known OSCP course last year which introduces you to penetration testing and offensive security techniques however with the AWAE course the focus is very much on web applications and the code behind them (which is er kind of explained in the title).

Having a development background, I wanted to know a bit more about what to look for when making and reviewing changes in code bases from a security perspective.

I’m not aware of much training being available in this space (pls add in the comments if you have come across anything good!) so decided to give it a go.

AWAE Course

AWAE structure is very different to OSCP.

AWAE teaches you key concepts by walking through several cases studies of issues in real world applications.

One of the key takeaways I took from the course was how various smaller issues could be chained together to ultimately result in remote code execution (RCE).

You’ll be taught to script these exploits yourself with simple Python examples provided – sometimes in an incomplete state for you to finish off as a learning exercise.

A variety of applications and issues are covered in several languages and platforms including .NET, Java, PHP, Node and Python.

At the end of the course there are two (White-box) and 1 (Black-box) machines with no instructions/solutions to give you a taste of what’s to come in the exam (given the code focus of the course I’m not sure why the Blackbox machine was thrown in there but hey its there if you want to play with it).

I liked how the course taught key concepts with real world examples although some of the examples felt a bit dated. Having said that the concepts are still very relevant so guess it’s ok just might have been nice to see something newer and maybe a bit less PHP (yes you can certainly have two much of that IMHO).

Its worth noting that I understand there’s been a recent update to the course with additional content around server-side request forgery and some other items added (I had the version prior to this).

The Exam

The exam is 48 hrs to review and bypass authentication mechanisms and then obtain RCE on applications you haven’t seen before – oh and you also have to script your attack end to end and write a report about it (you get another day for the report bit).

For writing the exploits you can use any language but you’ll probably end up using Python. I have a .NET/C# background but Python is so easy to pickup, use and has a heap of great libraries it’s a no brainer in my opinion.

The exam is tough and stressful.

To give you an idea of how tough this exam is (or possibly where I am skill-wise currently) I began the exam at 09:00 and finally found one of the authentication bypasses 13 hrs later at around 22:00. Annoyingly I’d actually spotted this earlier too..

I had RCE on the box midnight and the whole attack scripted about 00:30 before going to bed around 01:30 after I’d written the process up.

The second day I didn’t find anything useful at all and unfortunately, I couldn’t complete the other items I needed which meant I didn’t pass the exam this time – I’m still wondering what I missed.. 😦

My learnings:

  • The most challenging part by far is finding the damn authentication bypass/initial foothold!
  • It took me too long to find the issues and i’d have a much better chance at passing if could improve my methodology/spot these issues quicker – I suspect you need to train yourself to spot these
  • Once you find the issues things can turn around very quickly so don’t get disheartened if you haven’t found anything for a long time
  • Reading code in unfamiliar languages and frameworks whilst tired really ramps up the difficulty!
  • For those with a dev background coding the exploits was straight forward (probably the easiest bit for me) and I practised various approaches with my own https://github.com/alexmackey/hackthecat node app which gave me some good templates to use
  • If you don’t pass it’s hard to know what to do to improve – I think probably the best thing is to go over the course notes again, complete exercises you missed and look at other real world issues and code
  • I’d benefit from more real world security code review experience whether in day job or reviewing open source projects before tackling this again

I don’t like leaving things incomplete and not that I take a heap of certifications/exams but I think this is probably the first exam I haven’t passed (although my sociology grade wasn’t great going back to high school!).

For AWAE i’m of two minds whether I’ll retake it as:

  • It’s 3 days long! For those with work and family commitments 3 days uninterrupted by kids etc is a fair amount of time and investment for something with no guarantee you’ll pass
  • Seriously 3 days for an exam?! Reviewing code when really tired seems unnecessary, stressful and an unrealistic situation and you’ll feel smashed for a couple of days after (yes book at least 1 day off as you’ll need it)
  • There’s a fair amount of investment needed in stuff that may not be that relevant to you. E.g. I think I would have been a lot quicker to find the issues with being more familiar with weaker languages or frameworks I don’t use
  • I’m not sure how well known this certification is compared to say OSCP

I don’t have a heap of free time so I suspect I may now get more bang for my buck by working through some of Portswiggers great (and free!) workshops which whilst they dont focus on the code aspects are relevant and then see how I feel in a few months – at least when I’ve forgotten what a horrible experience that 48hrs was and want the challenge again!


This course is really good at:

  • Demonstrating real world issues
  • Showing how issues are chained end to end
  • Improving your python and exploit writing skills

You’ll end up developing some kind of methodology/approach from the examples and challenges but I think this is an area the course could do better in and provide more guidance.

I guess locating the issues really is the hard bit in this field and it must be a tricky thing to teach but there is a massive difference from being taken through an example to finding a problem yourself – the course only provides two machines to practice this end to end on (I enjoyed and completed them both).

This will be a challenging course for those without a development background to understand the code and some offensive security knowledge/techniques are assumed (I was fine after learning these aspects in OSCP).

Those with a development background will find the code aspects for the most part trivial but will also likely pickup some interesting items e.g. I learnt some things you could do with debugging symbols in .NET I wasn’t aware of and some weirdness about PHP variable handling – that second bit isnt that relevant for me but it was interesting!

I completed the course and many of the exercises within 2 months with a full time job and family commitments so this should be do-able for most with other commitments unlike OSCP which will take over your life for a bit.

In summary I really enjoyed AWAE and learnt a lot from it. I would recommend it to other developers who want to know more about how attackers will exploit issues in code and what to look for however be aware this course has a bit of a gap when it comes to teaching methodologies to locate these issues.

XSS (Cross Site Scripting) Part 1 – What is XSS?

I’ve been interviewing web developers recently and was surprised that the majority did not know what XSS (Cross Site Scripting) is.

Perhaps part of this is that many web developers will begin their development career using a framework such as React or Angular that abstracts what’s actually going on.

Many frameworks will now also provide sensible defaults that can help prevent many XSS issues and you have to go out of your way to get round these for example React warns you that you are probably about to do something potentially silly with its wonderfully named dangerouslySetInnerHTML method.

However its still very possible despite some of these protections and defaults to have XSS issues so if you are developing for the web it’s important you understand what XSS is and how to prevent it.

XSS is a surprisingly big topic (and I certainly learnt stuff putting this series of blog posts together) and we’ll be going deep into this topic so I’m going to split this up into what’s hopefully a series of more digestible articles rather than a 30,000 word mega post.

In this series of posts, I will:

  • Introduce XSS and talk about its different types
  • Talk about what an attacker could do with XSS
  • Show an example using my demo app of how XSS could lead to compromised accounts and even RCE (Remote Code Execution) when combined with other issues
  • Look at ways to mitigate or prevent XSS
  • Review some of the thousands of ways XSS can occur and how attackers circumvent various protective measures and what to do about it


As with any offensive security topics its important you do not use these methods and techniques on sites you do not own or have permission to test on.

Trying some of the techniques we’ll discuss on sites you don’t have permission to is almost certainly illegal and you could end up in a lot of trouble or even jail.

There’s plenty of great labs to explore security topics such as hackthebox, tryhackme or bug bounty programs where you can learn and help make stuff safer which makes the world a better place.

These are all much better options than a criminal record/jail time..

Hack The Cat (vulnerable Node/Express application for learning/teaching appsec)

I wanted an application to play, learn and teach some of these issues so I’ve been developing a deliberately vulnerable Node/Express application called Hack The Cat that contains many different security issues (below):

Whilst early in its development I’ve added various common security issues including:

  • Various XSS vulnerabilities
  • SQL Injection
  • Serialization issue
  • File upload with naïve filter

My plan is to expand upon this and also add the following:

  • SSTI (Server Side Template Injection)
  • More advanced XSS filters
  • LFI & RFI (Local/remote File Inclusion)
  • OS command injection
  • JWT issues

Anyway i’ll release this shortly with a MIT license so its free for all but let’s talk about XSS.

What exactly is XSS?

XSS or Cross Site Scripting is one of the most common web security issues and occurs when a website accepts malicious client-side code or HTML and then executes it in the context of the user who is visiting the site.

A simple example of XSS would be a shopping site that allows users to post reviews of products and then renders the reviews submitted.

If an attacker can submit a comment containing a script block like say below..

<script>alert('hello you have XSS issue you'll need to fix up')</script>

..and the browser renders it without encoding this then you’ll be greeted with an annoying smug alert box.

In fact if right click/view page source on this blog post and search for “hello” in the source code you’ll see something like below:

Here, WordPress has thankfully properly encoded this code block – if they didn’t you’d all be seeing an alert box as it would run as if the original site developer had added it.

If you want to see just how common XSS issues are and how they can be used go to https://www.exploit-db.com/ (a site that contains a massive index of security issues and code for utilizing them) and search for “xss” – wow quite a few issues..

XSS was apparently “discovered” by the Microsoft Security Response Centre and the IE Security team who heard about attacks where script and image tags were injected into web pages. In 2000 they published a report in conjunction with CERT where the term Cross Site Scripting was first used.

Whilst popping up an alert box and taking your users back to early 2000’s web/common current debugging techniques is certainly annoying an attacker could do much worse and this can be used to devastating affect.

For this we need to get into cookies and sessions.

Cookies and Sessions

XSS can become especially destructive due to how web site sessions & cookies work.

Cookies are pretty important and one way of holding state about a visitor. Cookies are one of the main mechanisms websites use to identify individual user on a website and understand if a user is logged in.

When I log into many sites the site will issue me a cookie to identify me and that I should be able to access the site.

Now the thing about cookies is that they are sent with any request made from a site or “origin” (there are a few exceptions such as using fetch which doesnt send cookies by default and certain configurations we’ll talk about later).

Now this can be a big issue as if an attacker can get code to run on a site as we know that cookies are being used to identify users then the site may think the request is from a legitimate user!

Same-origin policy

Before we move on I just wanted to mention same-origin policy as this relates to cookies and will also prevent or make harder some types of attack.

This can get quite complex so here’s an overly simplified version.

Same-origin policy places restrictions on what a document or script can do when a page is loaded from one origin and interacting with another.

A site is considered as having the same origin if the following match:

  • Its protocol (http/https)
  • Port (if specified – note weirdly IE ignores the port bit because er IE)
  • host (the address bit – it’s more complex than this but anyway)

The following for example would be considered to have the same origin:



Now this is important as if an attacker is running a script on another server (origin) then they wont be able to do certain things (at least directly) like steal cookies. This is a good thing.

Ok enough theory lets talk about different types of XSS.

Different Types of XSS

XSS comes in several different and overlapping ugly flavours you should be aware of:

  • Stored XSS
  • Blind XSS
  • Reflected XSS
  • DOM Based XSS

Persistent/Stored XSS

In Stored XSS the attacker manages to save their horrible XSS payload somewhere (most likely a database as they are good for this kind of thing unlike er blockchain which really isnt very good at anything).

The bad news with Persistent/Stored XSS is that once malicious code is stored it could potentially be executed for thousands of users who don’t have to do anything but visit your site where the code is rendered which can give it a massive reach.

I guess you’d also have to clean up the stored malicious content at some point in your database which could be really annoying if an attacker automated the creation of thousands of comments.

Blind XSS

Blind XSS is a form of Persistent/Stored XSS but has the difference that the attacker is not sure when the code will actually execute and if at all.

Er what?

Imagine an application that allows the sending of a contact form message to admin users.

When an admin user views this message then the malicious code will then execute in the context of the admin user. This malicious code could then be used potentially to perform administrative functions, steal the admin’s session cookie etc as its executing in the context of the admin user.

In Blind XSS the attacker may not have access to the code base/application so they cant be sure their code will ever actually execute.

An attacker might send code that will “ping” a remote server or endpoint by perhaps requested an image and then monitor to see if a blind xss attack was successful. They could then perhaps send more information about the page its executing on.

Reflected XSS

Reflected XSS occurs when the XSS payload is not actually stored but is “reflected” back to the user.

A site may have a search function that allows users to search for products and then sends the search values unencoded in the query string e.g. simpleisbest.co.uk?search=<script>something bad</script>

Attackers could potentially send users links like the above that are pointing at a legitimate site that then executes their malicious code.

An attacker would likely take care to obscure their payload more carefully than the above example and this could be obscured with various methods e.g. JavaScript’s String.fromCharCode method and a hellish number of different encoding schemes that most browsers support.

It’s important to note that reflected XSS requires a user to perform an action (e.g. click the link) and may be avoided by more knowledgeable users or automated systems that scan emails for malicious content.

Reflected XSS attacks are likely to have limited reach compared to persistent/stored XSS.

DOM (Document Object Model) based XSS

In DOM based XSS the attacker finds a way to inject a payload into the pages DOM without modifying the HTML directly.

A site may after login put the users name in a query string to display a welcome message that is then output on a page e.g. simpleisbest.co.uk?username=Alex

If the receiving code doesn’t encode the output properly then an attacker could supply a malicious bit of code in the username parameter that would then be output and executed on a page.

DOM based XSS can be harder to find/scan for as the attacks never reach the server itself.

So what’s the big deal about XSS?

Whilst using XSS to pop up an alert box or maliciously redirecting users to Michael Buble’s site could be very annoying XSS can be devastating when chained with other attacks.

Depending on other protections & configuration in place we’ll talk about in the future XSS can be used to do pretty much anything a user on that page could do as code will execute in the context of that users browser.

This means of course that the code can:

  • Transverse and manipulate the pages DOM
  • Read users cookies, local and session storage (depending on other protections in place)
  • Send requests to backend APIs in the context of the current user even if not used on the current page
  • Redirect the user to other sites/applications
  • Modern APIs have access to the camera, geolocation, microphone and certain files (these will require user to grant permission if they haven’t already)

And any other endless possibilities – basically anything you could do with client side code in the context of the current user

This means an attacker could:

  • Harvest and steal session cookies (depending on other protections in place) allowing them to act as if they are the user logged in until the cookie/token expires or is revoked
  • Perform actions in the context of the current user as the browser will send cookies with any requests made e.g. change their password to something known
  • Transverse & manipulate the DOM to pull out sensitive information
  • Manipulate the DOM and trick users into performing certain actions or reading wrong information
  • Redirect the user to other sites e.g. a site hosting malware or Michael Buble’s music
  • Defacing a site and replacing it with other content (hopefully not Mr Buble’s music)

Ok so hopefully I’ve convinced you XSS can be a pretty big deal.

In the next post i’ll show an example with my demo application of how it could be used to gain control of an admin account and even an entire server and then we’ll look at how we can fix this up.

Further reading