Persistent XSS at AH.nl
Are you aware of any (private) bug bounty programs or platforms? I would love to get an invite. Please get in touch with me: Jonathan@Protozoan.nl
These types of attacks need a user to click a specific link in order to run our malicious code. A problem with that is that most browsers block this sort of attacks.
But what if we want to inject our code into the website so every visiting user will be targeted? We call this type of attack Persistent- or Stored XSS. A big advantage is that browser can’t easily detect this type of attack, so we don’t have problems with bypassing the XSS auditors.
Today we will try to find this type of bug at Albert Heijn, the biggest supermarket chain in The Netherlands.
Searching for vulnerable pages
Persistent means that we have to save our code in the database of the website. This narrows our focus to places where we can register, sign up and create content. A quick look at AH.nl shows us a community website that offers the exchange of recipes, Kookschrift.
Everyone can register and after your first submission of a recipe someone at AH.nl will activate and publish your profile. Part of your profile is an avatar, you are allowed to pick one from a predefined list. After clicking the avatar a number will be saved into a hidden form field and it’s all fine.
Burp Suite to the rescue
An easy way to test different payloads is to use burpsuite. You start burpsuite and add it to your browser. From that moment it will intercept all your website requests. This allows you to easily modify parameters, submit new values and evaluate the responses.
Use the action button and pick ‘Send to repeater’. After that press the repeater tab and let the magic happen.
You are now able to set all sorts of payloads to the different fields, with the result of the submission on the right.
Now it’s time to submit your payload, refresh the browser and see if our code results in unexpected behavior. Think of not properly escaped strings, number parameters that are able to hold strings, url strings that are not checked against a whitelist, etc. You are able to automatically brute force this by using tools like burpsuite (attacker tab), but I advice you to do it manually. Otherwise you may trigger firewalls and cause unnecessary load on the website.
I save you some time here; almost all the parameters are properly sanitized, except one; the avatarUrl field on the profile edit page.
For this proof of concept it will be enough to arrange an account that is featured on the frontpage as a new user. We won’t use that user for our injection, but you can imagine the impact if we did.
Creating the XSS Payload
Before we start we need to take a look at how the HTML is structured.
As you can see the HTML is quite simple, just an image displaying the avatar.
What if we are able to inject a
" in order to close the
src= attribute and after that an
Firewalls, regular expressions, string sanitizers
After a few hours of trying I discovered that quite a lot of combinations are blocked and I came up with the following working payload:
We start with the “ to close the img src attribute, since it will give an error (the image that it tries to load does not exist) we can use the onerror handler to execute code.
Onerror/onload/onclick= strings are removed by their firewall, but we can bypass that by using a string like
onerroronerror== it will remove the onerror= in the middle and we end up with… onerror=.
Get on the frontpage, hutspot
Now we want to target all the visitors that visit this part of the website. We need AH.nl to publish one of our accounts, so that means; write recipes!
I signed up with an account, submitted one of my best hutspot recipes, took a cup of tea and waited for few hours. Welcome Hermelien Acker!
We have a dangerous combination now if we load our code into Hermelien her avatar. We won’t do that since we want to avoid impacting any regular visitors.
However the code still loads if we are logged in with a manipulated account (the avatar is displayed in the menu) so we can use that for our screen capture and bug submission.
Proof of concept in action
- Always validate user input
- Define a field as an integer in the database if it is an integer, don’t allow type mixing.
- Escape all fields in the output HTML
Impact of the attack
- Extra filtration of user information from other pages at AH.nl, submit data as the visitor on the AH.nl domain
- Attack the browser of visitors through injection of a framework like beefproject.com
- Setting up a phishing login
14–06–18 Found the bug, informed AH.nl
15–06–18 Bug confirmed by AH.nl
19–06–18 Requested update
05–07–18 Requested update
06–07–18 Bug fixed by AH.nl
09-07–18 Blog published, €200 reward