Lately we had to analyze QR-Codes in a pentest. Those held some random data which was used as a token for login and we wanted to know if that data was really random.
If you ever worked with the Burp Suite you may know the Burp Sequencer, which offers some statistical analysis regarding the randomness of tokens which appear in requests (you just have to tell Burp what or where the token is). In our case the QR-Code was delivered as an inline-image in HTML to the browser, like this:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg
AAAG8AAABvAQMAAADYCwwjAAAABlBMVEUAAAD///+l2Z/dAAAAAnRSTlP
//8i138cAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAEmSURBVDiN1dQxjoQw
DAXQjyjSMReIlGuk85XgAjBzAfZK6bhGJC6Q6VJEeA0zu5otBqdYrbSue
IXBTmzAPwL/gwkYeL0tFnAq71yGbIdQJq5gsD25GaU3dTRo2FZyNO0tlC
qyPJQp2O8iT7j3u9jhtf333CMU+JeDfc9EmCQ38/x41Sk5SE5pcgGxzgW
diZfF3RencvO8kduoTUfuOROOkrzbvFO5ob0aAFJYBX28ZNtR7MA6af0I
vJnYkU4+7n32qzSiMnmeKTZ5TV4nZ9mFsg+h5wrKaNkeeFZ1yuQxhdgT8
PzuGSUS7RN4Z50ysaOcOsnQssp9f8M6f+Uq3FeyldpAVRxNbHidTSX5Cj
vlCsr+Sr8UR69T+u19HI1tMqv8xX/sH/ETgkoCo4LtuEYAAAAASUVORK5
CYII=">
So we didn’t have a chance to call an URL to directly download the QR-Code as a png-image. We needed to process that inline-image. For that purpose I wrote two Python scripts, one for processing the Burp-export and one for actually reading the QR-Code.
I’ll go a bit into detail what the scripts are doing:
base64_img_extractor.py (Download)
Input: The Burp-export from the Sequencer, which holds responses with QR-Codes.
Output: Base64-encoded images, one per line, as a textfile.
Burp encodes the HTML-Response in Base64 in its export, so we need to decode them first. When you open up a Burp-export, you will notice it’s written in XML. You could use some XML-Library to travel through that file or, just as I did, use a regular expression to find the encoded response. Afterwards we will just decode it and then use another regular expression to find the inline-QR-Code.
Note: if you want to use that script, you should adjust the regular expressions to your needs…
base64_qr_decoder.py (Download)
Input: Base64-encoded images, one per line, as a textfile. (= output from first script)
Output: Text encoded in the QR-Code, one per line, as a texfile.
This script reads each Base64-encoded image, decodes it to binary and then decodes the containing QR-Code using qrtools.
With those we were able to process about 20k requests in 1-2 minutes and get the contents of the QR-Codes. Those were imported into Burp’s Sequencer to analyze their randomness.
Oh, and if you ever happen to quickly need to encode an image (or any other binary) in Base64, here is a Python 3-liner:
>>> import base64
>>> file = open("path/to/image/troopers.png", "r")
>>> print base64.b64encode(file.read())
Happy Holidays to everyone!
Benjamin