🏛️Olympus

A custom CMS and a puzzle-like exploit chain with some SUID fun for privesc.

Reconnaissance

Diving right in, we add olympus.thm to the /etc/hosts and start with a basic port scan.

We can now follow up on these two ports with a more in depth nmap scan:

sudo nmap -A -p22,80 --script=default,vuln olympus.thm

As with many other boxes we can quickly rule out ssh as an entry point due to its version and the fact that the room description tells us that brute forcing any login is out of scope.

Hence, we're left with a fairly recent Apache webserver which looks surprisingly small at first glance: only a single landing page without links or much content.

Service Enumeration

In order to further enumerate the webserver we can fire up nikto, though the scan doesn't really come back with a lot of information:

nikto -h olympus.thm

It does show the /static directory but neither the particles library nor the CSS and images in there contain anything useful. Since there isn't really much to go with so far we'll start a gobuster scan:

gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://olympus.thm/

After trying multiple wordlists and extensions, the common.txt yields an interesting path:

/~webmaster -> navigating to it we are greeted by what looks like a sloppy custom CMS created by the challenge author himself.

The hygiene of this self made CMS seems pretty poor and while gobuster continues to search for recursive directories we'll find that there doesn't seem to be much content on it. However, we do find multiple interesting input elements:

  • a search bar

  • a login field

  • a category URL parameter: http://olympus.thm/~webmaster/category.php?cat_id=2

Just out of curiosity let's try and search for ' to see if that breaks any potential query - and sure enough it does.

We could continue to look at the login form and the URL parameter, as well as the admin panel that we can reach with BurpSuite by ignoring the redirects back to index.php in the location header, but while all of these are fun to play around with we'll stick to the SQL injection we got here.

Fortunately, the error message above is very verbose and we learn not just the DBMS but also an important bit of the query that's being generated. Analyzing the POST that is triggered by clicking on the search icon we could quickly come up with an sqlmap command to exploit the SQLI automatically.

sqlmap -u http://olympus.thm/~webmaster/search.php --data="search=x&submit=" --level=5 --risk=3 --dbms=MySQL --technique=u --dump

But that's no fun is it?

For the sake of practice, let's see how we'd go about exploiting this manually step by step.

Manual SQL Injection

Feel free to skip to the next chapter if you already got a database dump and want to continue with the CTF.

First of all, we'll start the BurpSuite proxy, catch a search query and send it to the Repeater tab so that we can test our queries efficiently.

The error message reveals that the SQL query in the background could look something like this:

SELECT post FROM table WHERE col LIKE '%<INPUT>%' AND post_status='publish';

Since we control the <INPUT> and our goal is to build a working query, we'll have to

  1. close the matching string with a '

  2. put in any SQL query that we like - for example via UNION

  3. disregard the rest of the query by commenting it with -- - (the space is important for the syntax of a comment - the last dash is just for visibility)

With that in mind we can create and test a simple payload like this: ' UNION SELECT NULL-- -.

We get an error that indicates a "different number of columns". This happens because the UNION statement requires both the left and right SQL query to return the same amount of columns -> meaning that we will have to try again with more values: ' UNION SELECT NULL, NULL-- -

Increasing the amount of NULL values one by one we find that with 10 values in our SELECT statement we finally receive no error and all posts are displayed.

For visibility we'll exchange the NULL values with numbers and create a query that will yield no original post: x' UNION SELECT 0,1,2,3,4,5,6,7,8,9-- -.

We can pinpoint the locations where our injected values are reflected on the website. For example 2 was probably the title column, 3 the author, 4 a date and 6 the content. It seems that not every column is being reflected here but all we need is one for what we're about to do.

With the proof of concept working it's now time to get to the good stuff. Let's skip the version enumeration (@@version for MySQL) and jump straight to listing the available tables with this query:

-- select non default table names in a MySQL database
(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema NOT IN ('mysql', 'information_schema','performance_schema'))

Let's put that query in field 4 and don't forget to URL encode the entire query (CTRL+u in BurpSuite).

We can paste those table names somewhere else - format them - and search for interesting tables. chats, comments, posts and users seem pretty interesting but let's get the flag first!

Having discovered an interesting table name we should now enumerate the column names to see what that table has to offer. For this purpose we can use the following query:

(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='flag')

Again, pasting the query in any reflected position we get the result: flag. That means we have a table flag with a single column flag. Let's extract that with (SELECT flag from flag).

From here, the process of enumerating and dumping a table just repeats over and over again (it's a good basic SQL practice).

We successfully used manual UNION based SQL injection to enumerate and dump a table from the MySQL backend database.

Whether doing this manually or with a tool like sqlmap - we get a bunch of information from these tables. To keep things short, here are the interesting bits and pieces:

  • A user prometheus with the password hash $2y$10$YC6u...REDACTED...Dvnj3C

    • 2 more users with a salted password hash and the emails root@chat.olympus.thm and zeus@chat.olympus.thm respectively

  • A message about an uploaded file with the original name of prometheus_password.txt but stored on the server as 47c3210d51761686f3af40a875eeaaea.txt

  • A mention of an upload folder

  • A mention of poor password quality for the prometheus user

Password Cracking

Since we're informed that prometheus is using bad passwords and we found his password hash in the database, let's try to crack that. A quick search in hashcat examples or haiti reveals the type of hash:

└─$ haiti "\$2y\$10\$YC6u...REDACTED...Dvnj3C"
bcrypt [HC: 3200] [JtR: bcrypt]
Blowfish(OpenBSD) [HC: 3200] [JtR: bcrypt]
Woltlab Burning Board 4.x

Using john or hashcat and rockyou.txt we can easily retrieve the password.

└─$ john -w=/usr/share/wordlists/rockyou.txt hash --format=bcrypt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
[REDACTED]       (?)

Unfortunately, the credentials don't work on ssh. But we didn't encounter another login so far and we also haven't found the upload directory that was mentioned in the database yet.

So we're back at enumerating the webserver.

Virtual Host Enumeration

One thing we haven't tested for yet, are subdomains (i.e. other virtual hosts on the target). Let's attempt that with wfuzz.

# attempting to fuzz without a restriction shows that all responses
# come back with status code "302" which is a redirect
# so in order to fuzz correctly, we add the flag --follow
wfuzz -c -u http://olympus.thm -H "Host: FUZZ.olympus.thm" --follow -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt

Et voilà, we get a 200 response for chat.olympus.thm. Adding this to /etc/hosts and visiting it we are greeted with a login form. How fortunate!

We try the credentials we found and gain access to the Olympus Chat App as prometheus.

On a side note, we could've also deduced chat.olympus.thm being a valid vhost by just looking at the email domains of the zeus and root user in the database.

Anyway, see the prometheus_password.txt? Looks familiar because we already saw that being mentioned in the database dump of the chats table. We could start a gobuster scan in the background but common upload directories would be /upload or /uploads and by testing them manually we rapidly find that /uploads/ actually exists.

Trying to access prometheus_password.txt fails but we know from the database that the file was renamed to 47c3210d51761686f3af40a875eeaaea.txt -> and that does exist.

The file itself doesn't contain anything useful but now we know where uploaded files are stored and how to retrieve the uploaded files name.

Initial Access

The Olympus Chat App does not provide a lot of interaction. We can send messages and upload files or log out and reset our password.

Although we can easily test for XSS on the messages (yes, it's possible) the file upload is much more interesting. Let's just dive right in and upload a standard php reverse shell. Fair enough, no filter, no errors - the upload was successful.

Using sqlmap we can determine the file name of our uploaded reverse shell.

sqlmap -u http://olympus.thm/~webmaster/search.php --data="search=x&submit=" --level=5 --risk=3 --dbms=MySQL --technique=u -D olympus -T chats -C file --dump --fresh-queries

In my case it's at chat.olympus.thm/uploads/e90a754682b2fd927a16491b085e7872.php.

With access as www-data our next step will be to escalate privileges.

Privilege Escalation

www-data is not part of any interesting groups, has no interesting files in his home (except one directory which is owned by root and zeus but that might be important later) and only restricted access to the home folder of a user named zeus.

Once we get to check for SUID binaries though, we get something interesting:

find / -type f -a \( -perm -u+s -o -perm -g+s \) -exec ls -l {} \; 2> /dev/null

...
-rwsr-xr-x 1 zeus zeus 17728 Apr 18 09:27 /usr/bin/cputils
...

A custom looking binary that's owned by zeus and has the SUID bit set. Simply executing it we can see that it can be used to copy files from any source to any destination with the permissions of zeus. Additionally, the copied file will be equipped with the octal permissions 0666, i.e. read-write for everyone.

Since ssh is running and we can see a .ssh directory in zeus home folder we can attempt to extract his private ssh key this way (assuming he didn't remove or rename it and that it's valid).

Saving it on our Kali and connecting to ssh we realize that it still requires a password though. But leveraging ssh2john key and then using john with the wordlist rockyou.txt on the resulting hash, we're able to extract the password quite easily. And finally, we can access the machine via ssh as zeus.

We compromised zeus and can now submit the second flag.

Now that we are zeus we can come back to the weird directory in the /var/www/html/ folder that we found earlier. (And if we weren't curious back then, we could have found it by searching for group accessible files now.) In there we find a weird looking php file. Let's see what it does.

At the very beginning of the script we got a form that checks for a password. Fortunately for us, the password is put there in plaintext. Accessing the form via the browser and utilising the password we get this:

It appears we've discovered prometheus backdoor. The script is kind enough to let us know how to get a shell so we follow the instructions and gain root access.

We succesfully compromised root and can submit the third flag.

Bonus Flag

As we saw in the final hint beneath the root flag prometheus seems to have hidden another flag on the server somehwere. For this part we can use a common grep command that i'd usually use for password hunting:

grep --color=auto -rnw / -Iie "flag{" 2>/dev/null

We can read the.b0nus.fl4g file and submit the last flag.

Mitigations

This box has been more like a puzzle than a realistic target and was very well prepared by the author to showcase quite a few vulnerabilities. However, if i were to mention the main issues i think we'd have:

  • Severe web-vulnerabilities including SQLI, XSS, Arbitrary File Upload and Broken Authentication (being able to see the admin panel even though we're not logged in)

    • Most of these stem from using unsanitized user input or missing filters -> never trust user input of any form and use proper input handling and filters

  • Weak passwords (although the passwords were stored in encrypted form, we were able to crack them) -> enforce a stronger password policy

  • Insecure application permission -> remove the SUID bit if it's not absolutely necessary

  • Insecure file permissions -> apply the principle of least privilege

Last updated