Blind GQL injection and optimised binary search - A7 ~ Gee cue elle (misc) Google CTF 2017

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
A7 - gee cue elle was a hard misc challenge. It combines a database query injection with optimizing the algorithm to perform the attack. So also partially a programming or computer science challenge. When I first read the title I mispronounced it as “gi que elle”, so a more german pronunciation and I totally didn’t get what it tried to hint at. But more about that later. Let’s get started. A7 - gi que elle, We installed a fancy automatic attack protection system to be more secure against automated attacks from robots and hackers, so it can be fully A7 compliant. And a hint with .yaml tilde. So the first thing I did was look up a7 again. because of the “fully a7 compliant” comment, I immediately thought it’s probably that OWASP thing. So what is it again? OWASP Top 10. A7 insufficient attack protecion…. Ohhhh that thing. What a bullshit item on that owasp list. If you want to read about some infosec drama, search for a7 controversy. And this challenge is certainly a reference to that. Anyway, I took this as a hint that I should use some automated attack tool. Which in retrospect I think was wrong. But whatever. The description has a few more hints, but we will get back to that in a minute. Let’s first check out the site. The challenge here links a .html file with the following content. So part of the subdomain can be random. It’s obviously to give every player a unique site. We will see that come up later. On the site itself we can find a simple login field and when we inspect the html, we see a validation pattern that tells us the username has to be admin and the password has to follow a more complex pattern. It’s basically a valid flag pattern. CTF curly braces then some characters that start with quotas, and if you paid attention you can see that the subdomain is basically that part here, and then followed by 64 characters. So it seems like if we find the correct password for the admin user, we have found the flag. Like I said I though the a7 hint meant to tell me to use some tool, so I used nikto which basically does something like dirbuster and it found an app.yaml file. It turns out I could have found that myself if I had looked into the robots.txt, oh well. That qa entry here threw me a bit off but ignored it mostly. And this is where the second hint comes into play. The yaml tilde. So if you didn’t know what that means, some editors such as emacs or maybe vim create files to track your current progress in case you don’t save and it crashes or so. Then it can be recovered. And some editors create a file with the same name but append a tilde. And that’s basically what happened here, the developer apparently opened the file in an editor and it created that tilde file and for some reasons it didn’t get deleted. The yaml file is really interesting, it’s basically a web application config file for google app engine and it tells us here where the app that handles the page lives. I might also google a little bit to learn more about app engine to understand the structure of this file. So basically I’m hunting now for the application sources. In the google app engine docs I find a hello world example using main.py, so I tried that. And with the tilde there as well, I can leak the content. So now we got the sources. The code doesn’t look too big, but there are some dense areas. A first thing you might notice is, that there is something about quotas and an abuse detection system. Mhmh… And when I looked at the code, there was a lot of time calculations and I hoped I didn’t have to understand that right now. So I continued. Here the login post request. That must be a very important part. And indeed, I immediately noticed a query language injection. You see this here the colon 1 together with the parameter here is safe, but this direct python string manipulation with percentage s is unsafe. We can inject another single quote and break out of the string and screw with the query. So is this an SQL injection? Well. kinda. not really. As you can see here in the function name it’s gql. Google query language. At this point I still didn’t get that the title of the challenge was supposed to hint for that. Gee cue elle. But. Oh well. So what can we do with that, ideally we want to be able to log in. So what kind of features could we use in the query language. If you look up the grammar of the query language it’s really short and there is not much you can do. So we are here in the WHERE condition and all we can do is append more conditions with AND. There is not even an OR. And we could sort or limit the result but that’s not really useful. So no SQL UNION SELECT to inject a password we can control to bypass the authentication or so. The only output we have is either wrong username or wrong password. So it’s going to be a blind injection. The idea is if we make the query return a password, then the password we supply would be wrong and if we make the query return no password, we would get the wrong username error. We can play with this. GQL doesn’t have advanced string stuff like SQL. For example there is no WHERE password Like A%. Where password like B%. to slowly bruteforce the first character. But we can basically simulate that with greater or lower than. So you can inject a compare if the password is bigger than A, and if that’s the case the query returns the password and we get wrong password error. But if the password was not bigger than A, then the password might start with A or another char that’s lower than that, then the query would not return a password and we get the wrong username error. So we can in fact slowly bruteforce the password. So I start writing some code to do that. The comparison works by ascii value, so the order of chars is how they appear in ascii. So lowercase a is bigger than capital A. So I was writing that code but I quickly ran into the “Abuse detection system”. I was banned for 90 seconds because I either triggered two errors in 30 seconds, or made more than 13 requests per 30 seconds or it took me longer than 2240 seconds. Oh damn. Because with every request we get an error, we can only perform one request every 15 seconds. To not trigger the 2 errors in 30s rule. And not only that, we only have 2240 seconds time for that. That’s only 150 requests. But the flag is already at least 64 bytes long. We know parts of the password just not the main part. This means we have only like 2 requests per character. We will never bruteforce the password with those restrictions. So I started to review more of the code and long story short, I couldn’t find a bypass for the abuse detection system. Also the password is dynamically generated, but it’s safe. It’s hmac with a secret key and the first part of the hostname. Which means if you know the secret key and I give you a valid flag, you can verify that it’s valid. The first part generates the second part. So also no tricks possible there. Basically I had following options in mind: Bypass detection system Find an issue in the dynamic flag/password generation Better gql injection Try to optimize the bruteforce Like I said first one lead nowhere, second one was also unlikely, third one too, the query syntax is just so short, which means the only viable way is optimization. So an obvious first improvement to the bruteforce with greater and lower is to do a binary search. Basically we have an oracle that tells us with a guess of the password if the real password is greater or lower. Which means we can lower the amount of requests necessary. My first implementation did this per character. Each character has 64 options and with binary search you can find the right guess in about log N steps. So about 4.1 steps necessary per character. Which means we need roughly 262 steps in total, which doesn’t work, because we only can do up to 150 requests in the time we have. So I was stuck there for a while. A lot of time went into fixing programming bugs and testing it and because it’s so slow. with 1 request every 15s it is just took ages. But then when I did another round of auditing the code I noticed something. So error requests, of which you can only have two every 30 seconds, are only counted on exceptions. And if you look closely in the login code you can see that only a wrong password triggers an exception. Wrong username is just a regular request of which you can have 13 per 30 seconds. That’s the key! We need to optimise the binary search to favor wrong username over wrong password. So how do we do that? Well in binary search you always select the center of your search area. This means there is a 50:50 chance that your item is either greater or lower. So how can we skew that chance. Well instead of picking the center, we pick something more towards one side. For example if we do a 75:25 split, we have a much higher probability that our item is going to be lower than that new index. In our case we can have 13 requests in 30 seconds but only 2 of those can be errors, so we have 2 divided by 13, roughly a 85 to 15% split. Awesome. Also I optimised the string generation by working with numbers rather than a character string. So basically our string we want to brute force has an alphabet of 64 characters. So it’s like a base64 number system. Which means we can convert between base10 and base64. Don’t confuse it with base64 encoding, I’m really talking here about the mathematical numeral system base 64. Maybe you had to convert base 10 to base 16 or base 3 in school, that’s basically what I did. So I created two functions to convert a base64 number to a base10 number and vice versa. So now I can treat the binary search as a search of a number. The highest value is basically lowercase zzzzzz, which is a huge number. And this is the code I came up with. I use the requests module to perform the gql injection request. Then I define the alphabet for the flag in ascii order. Here are my functions to convert from base64 to base10 and vice versa. And a function to display a number line to visualise the search. And here are the important search variables. At the beginning the highest number is basically zzzzzzz And lowest is obviously 0. The current flag we will check, so the search index is initialized with 85% from the top. So that it’s skewed towards higher values and our real password is probably lower. And those are lists to count the exceptions, so wrong passwords and regular requests I make. At the beginning of the search loop I have a look at the lists that remember all exceptions and requests and remove the requests that are older than 30 seconds, because they don’t matter anymore. But if we have had more than 1 exception in the past 30 seconds, or had more than 11 regular requests, we are going to sleep for a second. Then we clean up the list again and maybe sleep again, until the condition is not true anymore. Then we are allowed to perform another request. So we convert the current search index to the flag string and perform the request. Some nice log output And then we check the result. If it was wrong username, then our guess was bigger than the real password, so we can set the highest possible value to that and move the search index down a little bit. But always move it in the 85:15% ratio. If we get wrong password, we get an exception, so we rememebr the time we had the exception and we also know our password was greater than our guess, so we take the upper part and move the search index higher. And that’s it. We just have to let it run now. Doesn’t it look beautiful? Here you can see how the search index, the X always skews to the higher values and how the search space is narrowed down. And there is a nice ratio now of wrong username and wrong password requests. This takes now a while. Basically 2240 seconds or 37 minutes. But we will still just barely make it in that time. So I started many instances in parallel and hoped that at least one will succeed. And this is where I started to become nervous. Because the end of the CTF was approaching and I was not sure if it will work, I didn’t have one successful run yet. Will the flag I find work or will it break? Will I do my calculations right? Do I have bugs? And about 10 minutes before the end two processes approach the final guesses. There we go, search space is apparently now 0. We found our flag. hopefully. So I tried to enter the flag, super shaky hands because I had to be fast with minutes left but it didn’t work. Wrong flag. Also I couldn’t login with this flag. It was not correct. DAMN. But I had a hunch what the problem was. I probably didn’t quite get the calculations correct, so I was probably 1 or 2 numbers off. So i just adjusted the last character of the flag and after a few attempts, I got the right flag. Damn that was close. But really happy at the end, because just FYI, I spend probably like 12 hours on this challenge.
Info
Channel: LiveOverflow
Views: 68,720
Rating: undefined out of 5
Keywords: Live Overflow, liveoverflow, hacking tutorial, how to hack, exploit tutorial, gql, google query language, gql injection, binary search, binary search algorithm, blind gql, blind sql, sql injection, google graph language, google ctf, googlectf, gee clue elle, a7, OWASP A7, owasp, a7 controversy
Id: za_9hrq-ZuA
Channel Id: undefined
Length: 14min 25sec (865 seconds)
Published: Fri Jun 30 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.