Null community is an open security community where pen-testers, bug bounty hunters, students, security researchers come under the same roof and exchange knowledge. One of its chapter in Ahmedabad had CTF this 22nd. They made the CTF online and I was a volunteer of the community so it was obvious I was going to participate in it.

It had 6 challenges in total. Two web challenges, one binary exploitation, one steganography, one crypto, and one steganography + cryptography combined.

This writeup is about a web challenge named “Prove yourself as 1337”. It was of 550 points, the highest of all challenges in the CTF. And looking at the difficulty it had, I think that even 550 points are less.

Initially, there was an URL which leads to a web application. Below is the initial description of the challenge. It was sure that brute-forcing the parameters isn’t going to work as it was written: “don’t waste time”. Also, it was said that: “A variable can lead to source code of index.php if set”. So, it gave us a hint that the variable might be boolean.

Challenge

Now, as opening the URL gives, there was the following screen:

webroot

It took two inputs, one username, and password. I supplied 1337 and 1337 as username, password pair and I found a really interesting thing in the URL. So, two inputs were visible but it actually took 5 inputs out of which three were hidden.

The URL was: http://165.22.218.145/buggy/?username=1337&password=1337&random1=65616f96af8b27efd6c16f5249e58700&random2=7ec7e3cb456c9f07229df3fd1127ed42&source=0

Here, username and password were as it were. random1 and random2 were some random hash like values. And then there was a parameter named “source” and it was set to 0. I quickly figured out that this has something to do with the source code. I remembered the challenge description and it said, “A variable can lead to source code of index.php if set” so now I knew that I need to “set” this variable. I changed the value of it to 1 from 0 and it showed me the source code.

Source Code

I saw that there was a lot of md5 logic going on in the source code. I was wondering if this was a web challenge or a crypto challenge.

The flags were coming from some other PHP script called as “flag.php”. I tried accessing it directly but no luck! There were three flags in total which; when concatenated, forms one final flag. Each of these individual flags had some conditions behind them. Which means the flag will be concatenated only if the condition falls true. There were three different conditions.

Condition #1

Condition 1

In this, flag1 was concatenated to final_flag only if random1 and random2 variables were not identical but the hash of random1 variable concatenated with some secret variable called salt was identical to the hash of random2 variable concatenated with the salt. But one of the aims of hashes says that “Two different values must not produce the same hash”. So, looks like we are stuck here, or are we? However, here is an attack called hash collision attack which I quickly remembered in which two different values produce the exact same hash. I googled a lot to find such strings and I actually found them. But they were in binary format and we cannot pass it in the query string. I spent hours and hours searching for such strings but no luck.

Then the other web challenge clicked in my head which was exploiting the type conversion of PHP(but not type juggling). Type conversion fails and gives a warning when supplying the incompatible type and did not throw and error. I checked in the PHP documentation of md5() and saw it takes a string as an input. Then I wondered what if I supply array as an input to the function like I did in the previous challenge. I changed random1=65616f96af8b27efd6c16f5249e58700 and random2=7ec7e3cb456c9f07229df3fd1127ed42 to random1[]=65616f96af8b27efd6c16f5249e58700 and random2[]=7ec7e3cb456c9f07229df3fd1127ed42 and the application threw the first part of the flag.

Flag 1

Condition #2

Condition 2

Here, the first condition satisfies if both username and password are not empty. Then, username and password are concatenated and stored in a string called concat. Now, the second condition satisfies if the md5 hash of the concatenated string was equal to the string itself. But how is this even possible? We have learned that hash(string) ≠ string. But here, another PHP specific vulnerability lies here. And that was, yes as you have already guessed it, “Type Juggling”. Type juggling was introduced as a feature of PHP but turned out to be a critical authentication vulnerability. It means, type of variable is decided upon the context it is used in. Which means, if (“0e001234”==“0”) {} will return true even if they are not the exact same values.

So, I needed to find such a value that it’s hash’s first one-digit are the same as the value’s first digit in order to make the condition true. It was a difficult process to do it all manually so I created a simple python script and ran it which eventually crashed my computer. I googled a lot to find such strings but could not find anything. I was out of luck. Then I decided to take a hint from the CTF platform which cost me 150 points but in turn returned me a “working” script which did not crash my computer. I ran it and in like 10 minutes, the number was there!!!

username=0e2159&password=62017&random1[]=65616f96af8b27efd6c16f5249e58700&random2[]=7ec7e3cb456c9f07229df3fd1127ed42&source=1

I put half of the number in username variable and the other half in password variable as they both were concatenated. And boom! Got the second part of the flag!

Flag 2

Condition #3

Condition 3

First of all, here is a call to the parse_str() function which parses query string into an array called as res. I could not think of anything in this part of the challenge. I scratched my head for hours. Then I looked up for vulnerabilities in parse_str() function as the last attempt. And it actually had a vulnerability. So, we can overwrite any already existing variable by passing it in a query string which was later parsed by parse_str(). So, I supplied the hash given in the source code as the value of the hashed variable using the query string:

username=0e2159&password=62017&random1[]=65616f96af8b27efd6c16f5249e58700&random2[]=7ec7e3cb456c9f07229df3fd1127ed42&source=1&hashed=ba6e12df1edab45f11f70b547dba9959

And, finally, after hours of hard work, the flag was there!!!

Flag 3

It was a really fun challenge to solve. Kudos to everyone who made this CTF possible.