CTF and other games

SHA2017 CTF – web 300 write-up

Another challenge written by me was the Web 300 – Eindbazen Election challenge running on https://vote.stillhackinganyway.nl/. This page contains a ranking of all Eindbazen members, a link to the Android voting software and a QR code.

I wrote this challenge, because we had all those cool images created by Thice and because Dutch election software is apparently broken. So I decided to create my own safe election software.

The goal of the challenge is to figure out how the Android application is talking to the website and to see if we can use that to get more information from the database or gain access to the website.

Android application

If you open the Android application you see a list of the Eindbazen members to choose from. If you choose one you get the option to vote. And if you push vote you have to scan the QR code on the website. Since the application got some protection against altering and SSL-Man-in-the-Middle-attacks, we open it with jadx to read the decompiled source code.

The application is using ProGuard to do some obfuscation and make the application less readable. First we read strings.xml to see if there is some interesting information:

There are some interesting strings used in the application. One of them is the api_url. Let’s see if we can find the source code which uses this string.

Only one source file is using the api_url string. Let’s open this file.

We notice a connection is made to an url provided by the b() function with two headers (X-Signature and X-Device-Id). The X-Device-Id is the ID of the Android device, and X-Signature is provided by the a() function.

The b() function returns the String d + c. The d variable is the api_url we found in strings and the c variabele is set by the a(String str) function. Let’s see where this is used.

So there are three actions to submit to the api:

  • https://vote.stillhackinganyway.nl/api.php?action=get_list
  • https://vote.stillhackinganyway.nl/api.php?action=vote&id=XXX
  • https://vote.stillhackinganyway.nl/api.php?action=get_score&id=XXX

Let’s see if we can get some information from the api with curl.

Apparently we have to use the supplied headers. We assume we can fill anything into the X-Device-Id, so let’s see how we can get the X-Signature header.

X-Signature header
The X-Signature is provided by the a() function which only returns string a. String a is set with the b(String str) function. We also see a c() function which calls b(“”), so the argument of b(String str) can be empty. Let’s have a closer look to this function.

So basically it’s a SHA-256 hash of c + ANDROID_ID + str + f(). We know c, cause that is the second part of the url, the android_id is the X-Device-Id header, the str part can be empty, so all we need to know is what the f() function returns. Let’s have a look at this function.

It looks like the f() function does a XOR on the app_key and the output of the c() function, which seems to be a hex2byte function. So let’s get the output ourselves.

With this output we can now calculate a X-Signature, let’s try to get the get_list again with curl.

And it seems to work. We now can talk to the api. Let’s see what we can do with the other two api calls.

So, we can get the list, we can get the amount of votes of an id, but still can’t vote. Since the voting works by scanning a QR-code, we probably have to do something with this. Let’s have a look at the VoteActivity.

We notice the onActivityResult function, which is probably called after the scanning. We also see that the f.a() function is called to set the path to /api.php?action=vote&id=X and we see that the f.b() is called with as argument a.a(), which we can assume is the scanned QR-code. So, to send a vote, we need to create the following X-Signature.

So our next job is to scan the QR-code. We can use zbar to do this.

So we are now also able to vote. But only once. Since we successfully can talk to the API, we can now focus to see if we can use these API functions to get the flag.

SQL injection
The next step can take some time to figure out. As the challenge creator I know where the flaw is. But let’s see if we can think logically and figure it out. We have 3 api calls:

  • /api.php?action=get_list
  • /api.php?action=get_score&id=X
  • /api.php?action=vote&id=X

The first call returns a list of all Eindbazen, we can’t inject anything there. The second and third call we can test if we can inject anything into the id parameter.

And the same happens for the vote function. So guess we have to look further. Besides the id parameter we also controls the X-Signature and the X-Device-Id header. The X-Signature is only used to check if the request is not tampered with, but we have full control on the X-Device-Id header. We also know that we can only vote once. Maybe this header is used to prevent multiple votes. Let’s alter our vote.sh script and check this out.

First of all we are able to vote multiple times, now let’s get famous, cause we have broken this election. Secondly we notice a blind sql injection. To get the necessary information from the database I created the following Python script.

It will take some time to run this script, but it will give us the following output.

We finally get a link to an image file. All other images are found in https://vote.stillhackinganyway.nl/images/, so let’s open this file and get a flag.