Solving Intigriti’s November XSS Challenge 2020 With the JavaScript Console.

Nov 9, 2020 · 5 min read

Like you should do with every challenge, I started with reading the rules. Those were clear. The goal was to execute alert(document.domain) on the domain, without using self-xss or MiTM attacks. The attack should work in the latest version of Chrome and Firefox.

While looking at the html of the page, I quickly noticed that the QR code was an iframe with a page that accepts a parameter ‘URL’ in the URL and displays the URL in the form of a QR code. When the code is clicked, the url will be opened in a new tab.

On the challenge page itself, there was no way to inject something, so I focused on the page from the iframe (

In the JavaScript file of this page, the URL parameter is being validated (it must start with http:// or https://). It looks like this can’t be bypassed. The domain itself is not being verified. This means we can use this page for open redirects. But for this challenge we must find xss, not open redirects, so I continue the search.

The next thing I noticed was that the JavaScript also checks for size parameter in the URL. The value of this parameter gets injected in the style attribute of the QR code’s container. Via this parameter it is possible to inject CSS for this html element.

I started researching on xss via css, but all I found were outdated payloads that worked in older browsers. For a moment I was stuck on this, but a little later the 4th tip was given on Twitter. It was the cover of the movie Paddington, so I probably must use some padding in the CSS. I looked again in the JavaScript and noticed that the function scanCode() executes is the content of the scanned QR. If this content is “javascript:alert(document.domain)”, a new page gets opened, with an alert. So, all I must do is find a way to make the scanner read my QR instead of the one generated from the URL parameter.

One of my first thoughts was to add a background image of a QR code with our payload. In order to be visible, I used padding-top: <size of QR in px>. And to be completely clean, set the height to 0 and overflow hidden.


Now only our own QR is visible. Unfortunately, when clicked. The JavaScript returns that the code could not be read. I started debugging and put my breakpoint on the line where the canvas gets deleted.

The canvas is a copy of the html containing the QR and is used by the scanner to read it. I removed the display: hidden line from the canvas’ style and found out it was empty. For some reason the function html2canvas couldn’t read our image. After some googling, I found out this is a problem with cross origin resource sharing (CORS), the way to bypass this, is to encode the image as base64. But putting the encoded image in the URL, made it too long so I got a 414 response. I quickly gave up this solution and searched for hours for another one. But all I found were solutions in JavaScipt, and we have no way to inject it. I took a break and went for a run, and when I came back, I started Googling on the base64 again. I found out about the js function toDataURL, which can be used on a canvas element to convert it to an image in base64 format. It can be given a second parameter to determine the quality of the image. By copying some of the JavaScript and pasting it in the console, I created the malicious QR and put it in a canvas.

Create QR code with our xss
code1 = new QRCode({
content: "javascript:alert(document.domain)",
color: "#ef3257",
width : parseInt(size),
height : parseInt(size)

Then, I converted the canvas to a jpeg in base64 format and reduced the quality to 0.5.

window.location.href = "/qr.html?url=;padding-top:100px;overflow:hidden;background:url('" + document.querySelector("canvas").toDataURL("image/jpeg", 0.5) + "');height:0";

After executing the above JavaScript, I got back an empty page, with an error in the console, that the URL of the image was invalid. After comparing the invalid URL with a valid one, I found out this was because the ‘+’ signs in the url got translated into a space. To resolve this issue, surround the toDataURL function with “encodeURIComponent()”:

window.location.href = "/qr.html?url=;padding-top:100px;overflow:hidden;background:url('" + encodeURIComponent(document.querySelector("canvas").toDataURL("image/jpeg", 0.5)) + "');height:0";

When the above js code is executed, you can click the QR code and the xss gets executed.

Challenge solved!

The Startup

Get smarter at building your thing. Join The Startup’s +750K followers.