It’s been some time since I’ve poked my head and tried to write something technical online, in December I gave a walkthrough of a CTF VM that I developed for a local CTF. I didn’t fully finish the walkthrough (theres a lot!). In this part, the technical challenges start to spice up, involving knowledge in data type overflows in PHP and reverse engineering Linux ELF files. I really enjoyed making this challenge, although sometimes I feel I made it a little too abstract, especially with the integer overflows and login flags, but never the less, it was a learning experience! I apologise to anyone who can’t follow my writing, I’m not the greatest writer. I left the last post which you can find here where we were on a login screen, it looks a bit like a famous web shell, at least, the theme on the web response does, perhaps not the functionality.
The first thing you should notice is the last request to a page, youre_just_not_my_type with two integers being requested in a GET request. This is a reference to how integers actually have a maximum value in PHP, this is a bit mean of me, because the hint isn’t very good. Its quite fun looking at the characteristics of data types and how they react in different ways to how people would expect sometimes, I highly recommend reading this blog post if you are intrigued in any way. If an operation where a variable is expected to be an integer is too large to be that data type in PHP, it will be reinterpreted as a float, this is outlined in the PHP documentation on integers. This can lead to interesting behaviour for when saving data to databases or interpreting numbers in functions for currencies or large numbers.
When inspecting this page, we get a response with the two number simply added together. If one of the two parameters are above PHP_INT_MAX, the global variable set as the highest possible integer, the page responds with failure. Quite simply, we want to cause an integer overflow so that the data is reinterpreted as a float, for this to happen we can simply use the highest possible number an integer can be in one value and the other value can be set as 1 or more, so that the integer can no longer be a integer anymore. I simply modify the values in Iceweasel by going on the network tab of developers tools and resending the GET request that way, there is many ways you can do this, there are no real restrictions on the page requests. As we can see from the picture below, I have entered the largest possible integer and just put 1 in the other parameter, this has set off a integer overflow, I use the is_int() function to confirm the fact an overflow has taken place.
The second challenge could be done in multiple ways, the login for the authentication mechanism could be bruteforced. It has a username of admin and a password of 123abc, but there is another way of achieving the flag without even getting the credentials completely correct, the input for the login has the username set by default as “admin”, although this can changed. When viewing the source for this, we can see a request is sent to login.php and if correct goes to a page called cp.php. For someone who is very switched on, you may notice that the response is split when a correct credential is noticed and the hash is sent to cp.php. If you make multiple queries on authentication to login.php, you will notice that the hash response is the username. What I find when attempting to hack things is to get creative, try everything and hopefully something works, this is what I’m trying to emulate. If the username hash (admin) is sent to cp.php it will spit out a flag for you.
We can see from the source code there are quite a few references to a include folder, throughout the challenges thus far, index has been present through all the directories (on purpose), you couldn’t use an automated web scanner to find these folders because they are far too long to be hashed and aren’t linked unless challenges are solved correctly. There are two last challenges involving RE where I do not give a hint where they are, include is an open directory that will allow you to notice there is a folder within it called cmp, here you will find two files which will be ELF files, these are the last two challenges!
We first start with the file absolute_cmp, both files are 64-bit ELF files, to confirm you can use the Linux file command or any binary identifier, there is no obfuscation or packing on both files, the aim of these files is to get people used to linux ELF binaries (that also includes me!). When looking at the absolute_cmp, we should take into consideration, why I have called it absolute compare, the file names are used for hints. When debugging through GDB, we can see that a file operation is made just after entering the main() function of the program. An IO operation is logged on GDB and we can also see fopen is being used from stepping through the debugger.
serial.key is trying to be opened, you can probably guess that if serial.key is not present, we exit, we can confirm this fact by seeing after a jump decision is made, the puts call is made, we can print the memory address location that is being moved to be printed to confirm the fact that we need a file called serial.key to be present, we could also simply just the program and we would recieve this message (but wheres the fun in that? 😉 )
Now to understand why the challenge file is called absolute_cmp, after stepping a little bit further we reach the fread function, right after that we get a reoccurence of cmp instructions. If we look at the instructions in more depth, we notice that a byte is being put into the EAX register, to which one of the 16 bit registers al is being read for a comparison. The location of the byte is also being changed as we can see from the instructions 20, 1f, 1e and 1d. We can see a consistent jump to the address 0x400765 throughout the compairsons of bytes, this leads to what most call the bad boy message. We can again confirm this by following where this goes, as previously, its a puts message, in this case “Invalid Key”.
So we need to make sure we have the values set in the laborious compairson that has taken place where the assembly is checking every byte for a certain hexidecimal value. In this case, 62 41 6e 54, which is bAnT in ASCII. After this decision to not jump onto the bad boy message, we see a large amount of assembly code which is moving singular bytes into a location, this is so CTF players cant use strings to try and get the flag easily. This is why some students couldn’t use a hex editor to find all the flags 😉 In conclusion, if the file, serial.key, which is in the same directory in the binary as the ASCII string bAnT in it, the program will give you the flag! Simple right?
Now to the the environment challenge! We could look at this binary in a debugger and make an informed guess where we need to end up, much like the absolute_cmp challenge, bytes are individually put in to be printed. We can explore the binary to try and find this area, this will not take long, from here we can work back to understand the decisions that need to be made. We can, also, try and understand the functionality of the program, like a good RE would do (right? RIGHT?!) If you break on main like you should on both binaries, we can see that the current working directory function is being called, the one of these characters, the 1st one, is compared with the static value 72 (‘r’). If it is true that it contains r, we jump to somewhere else, you guessed it, to print a bad boy message saying, Wrong Environment! So we want to execute this file, outside of /root/ current working directory (huge assumptions people are running here but there is another check too). We put it into a parent directory or anywhere which isn’t root or a folder with an r and we can move onto the next stage.
We can see a ptrace call being made, if we look closely at the instructions it resembles a debug check that is made in a lot of Linux binaries, you can see a PoC here. Many may assume that this is a anti-debugging function, but actually, we want the person to be debugging, this means you will only get the flag printed if you are in a debugger! With these two stipulations, if correct, the flag is printed. You could of course, print the assembly and get the flag manually, but that would be too 1337 for me.
In a debugger it should print something like this!