Tags: sqli qrcode 

Rating:

This challenge was about performing SQL injection while avoiding a primitive blacklist filter, all through QR codes.

When signing up for QRBook, you receive a QR code used as a key to retrieve a message you specify. The QR code, when scanned, has a series of letters and numbers. Trying a few usernames, one will notice that the string is often preceded by one or two equals signs. This seems a lot like Base64, but reversed. Sure enough, reversing the string and Base64 decoding results in our username. The logo of the site tells us that Jarvis is the creator of the site, once we photomanipulate it into being scannable, so we construct a QR code for Jarvis using Python to get the right value...


>>> 'Jarvis'.encode('base64').strip()[::-1]
<span>'zlmdyFmS'
</span>
...and Google's API to turn it into a QR code: http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=zlmdyFmS

However, "logging in" with this QR code returns us some message obviously written by another player. Apparently, this isn't the solution. Since SQL injection is often the bug in CTF challenges, we try SQL injection through the uploaded QR codes. However, we get an 'Attack Detected' message when we try the usual SQL injection attacks, so let's use a small "or 1=1" equivalent: '='

This works because field=''='' evaluates to field=True, which will evaluate to True unless the field contains something like 'False' or 0. We end up getting the same message as Jarvis, which stands to reason since he's the creator and would therefore be the first user. Next, just to make sure, we try '!=', which should always evaluate to False, and sure enough we get no message.

After some probing, we find that spaces seem to be the only thing that's denied. Since many databases allow tabs as an alternative, we use tabs and find that the rest of the problem is just UNION based SQL injection with only a single row returned. We run into a small problem, which is that newlines terminate our data. Since some base64 encoders will put newlines every 70 chars, we need to remove those before we can continue.

We discover that the only table in the database created by a user is the "messages" table, and one of its columns is secret_field. We assume one of the users' secret_field values will contain our flag, so we tell the database to find it for us:

<span>>>> string.replace("asdfasdvv'  union   select  secret_field    from    messages        where   secret_field    like    '%h4ck1t%".encode('base64'),'\n','').strip()[::-1]
'</span><span>lQXMrNGNoVyJJU2apxWCkxWZpZ2X0VmcjV2cJUmclh2dJMXZnF2czVWbJ02byZWCkxWZpZ2X0VmcjV2cJQ3YlxWZzlgbvlmb1lwJ2ZHZzFmZkNXY'
</span>
And the following URL has the image to grab the flag: http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=lQXMrNGNoVyJJU2apxWCkxWZpZ2X0VmcjV2cJUmclh...