In November, Intigriti challenged security researchers to solve an XSS Challenge, which solution needed to have the following rules:
- Should work on the latest version of Firefox or Chrome
- Should execute the following JS:
- Should be executed on this domain (challenge-1120.intigriti.io)
- Shouldn’t be self-XSS or related to MiTM attacks
This is how the challenge appeared when browsing on the relative webpage:
From an initial glance, it appeared like a static webpage having nothing more than a suspicious QR Code. As the first step, I inspected part of the HTML source code.
From the above image, I concluded that probably I was working on the wrong endpoint since the suspicious QRCode was inside a <iframe> pointing to qr.html. Therefore, I continued to work on https://challenge-1120.intigriti.io/qr.html?url=https://go.intigriti.com/submit-solution.
But hey, life is not simple at all. The RegEx was elegantly doing its work. However, then things started to become clearer by inspecting the subsequent JS code.
A payload like 1;test allowed bypassing the check performed from the qrcode-svg library to set the width and height of the QR Code, but at the same time, this value constituted part of the style attribute of the QRCode SVG tag created from the library.
Therefore, tampering the size GET parameter, it was possible to modify any CSS property of the QRCode <svg> tag. But how could it be helpful to trigger an XSS? Well, it took me a lot of time to get closer to a conclusion regarding it. First, I searched for known vulnerabilities in the other used JS libraries (https://github.com/niklasvh/html2canvas and https://github.com/cozmo/jsQR), but nothing interesting was found. Then, I read the subsequent JS code more carefully (which is always the right solution when you get stuck).
A click on the QRCode tag triggered the scanCode() function, which performed the following actions:
- Convert the <svg> element containing the QRCode into a canvas using the html2canvas library.
- Convert the canvas into an image.
- Read the QRCode data using the jsQR library.
Since there was not a method to directly modify the DOM elements, but it was possible to modify the style of the <svg> QR Code tag, what about directly tampering the QRCode SVG with another QRCode to confuse the reading library?
This required me a lot of trial and error, but I finally managed to bring out a solution, using the padding and the background-image CSS properties, respectively to move the existing QRCode to the bottom of the screen and setting another one as replacement. Following the final payload:
Some things to highlight about the payload:
- The width and height CSS properties needed to stay around 400, in order to properly throw aside the other QR Code and read the new one. In fact, the value of the size GET parameter was the one actually used to create the canvas from the DOM elements displayed on the screen.
- The padding-top property was used to move the existing QRCode to the bottom side of the screen. A value of 200px was okay for most screen sizes.
- The background-repeat:no-repeat property was necessary to show only one QR Code and prevent reading issues using the jsQR library.
This is the result using the above payload.