Starting the box
Link to the box: https://app.hackthebox.com/machines/Artificial.
Port Scan
We start off the box by running a port scan on the provided IP.Attacker Linux
Terminal Output
- Port 22 (SSH) is open. Running
OpenSSH 8.2p1— we’ll keep this in mind for later. - Port 80 (HTTP) is open, served by
nginx 1.18.0. The Nmap output tells us the server immediately redirects tohttp://artificial.htb/, so we need to add this to our hosts file. - OS is Linux (Ubuntu).
Edit the Hosts file
As always, we edit the/etc/hosts file to add the hostname:
Attacker Linux
Nano Interface
Initial Foothold
Enumerating Port 22: SSH
OpenSSH 8.2p1 doesn’t have any straightforward publicly known exploits. Without credentials, SSH isn’t useful right now. Let’s focus on the web server.
Enumerating Port 80: Web Server
Directory Busting
Feroxbuster:
Attacker Linux
Terminal Output
/loginand/registerendpoints exist — we can create an account./dashboardredirects to/login, confirming it requires authentication.- Nothing too unusual yet. Let’s explore the application.
Manual Enumeration
After registering an account and logging in, the dashboard greets us with:Terminal Output
- This is an AI model management platform that lets users upload models.
- We tried template injection payloads in available input fields — nothing returned.
Terminal Output
Terminal Output
- The server is running TensorFlow 2.13.1 in a Python 3.8 environment.
- The upload endpoint expects
.h5files — the standard format for saving Keras/TensorFlow models.
Terminal Output
- This confirms
.h5is the expected format and that models are loaded server-side. This is a classic file-upload-to-RCE scenario if the model format is unsafe.
Exploiting TensorFlow: Malicious .h5 Model (CVE-2024-3660)
A quick search reveals CVE-2024-3660 — Remote Code Execution via a malicious TensorFlow/Keras model. When the server loads an untrusted .h5 model file, arbitrary Python code embedded in the model’s Lambda layers gets executed.
- Splinter0/tensorflow-rce — exploit PoC for CVE-2024-3660
- The vulnerability affects TensorFlow 2.13 — which is exactly what this box uses.
Building the Exploit in Docker
Since we need to generate the malicious.h5 file in an environment that matches the target (TensorFlow 2.13 / Python 3.8), we use the provided Dockerfile to build a matching container.
Dead-end: VirtualBox Docker issues (feel free to read along what I did)
Dead-end: VirtualBox Docker issues (feel free to read along what I did)
We hit a frustrating wall initially — running the exploit inside Docker on our Kali VM (VirtualBox) consistently produced:This turned out to be a known VirtualBox/QEMU CPU compatibility issue with certain TensorFlow builds — not a problem with the exploit itself. After trying various approaches (clearing Docker cache, rebuilding from scratch), the fix was to run Docker on Windows via WSL instead of inside the VirtualBox VM. This resolved the
Terminal Output
Illegal instruction error immediately.Attacker Linux
Attacker Linux
-v $(pwd):/appmounts our current directory into/appinside the container, so files created inside are accessible on our host.
exploit.h5 file with a reverse shell payload embedded in a Lambda layer.
Getting a Shell
We upload the generatedexploit.h5 via the web dashboard. The server loads the model, triggering our embedded payload. Our listener catches the connection:
Attacker Linux
Terminal Output
- We have a shell as the
appuser.
Post-Initial Enumeration (as app)
Finding Credentials in the Flask App
Let’s look at the web application source. We find the Flask secret key inapp.py:
Terminal Output
- Noted for potential session forging, though we won’t need it.
Finding the SQLite Database
Searching the app’s instance directory, we find a user database:Terminal Output
- Five users with password hashes. The 32-character hex format is MD5 (hashcat mode 0).
- Let’s target
gaelfirst — they’re likely the primary user on the box.
Cracking gael’s Hash
Attacker Linux
Terminal Output
Terminal Output
/etc/passwd to confirm gael has a shell:
Terminal Output
Logging in as gael
Attacker Linux
Terminal Output
- User flag:
42550009838ced1ae7aa3a56397487b6.
Privilege Escalation
Post-ex Enumeration
Running LinPEAS
Let’s transfer and run LinPEAS to look for escalation paths:Attacker Linux
Terminal Output
Terminal Output
- Port
9898is listening locally — likely the Backrest service we saw in/opt. - Port
5000is also internal (probably the Flask app backend).
Terminal Output
- A large backup archive owned by root, group-readable by
sysadm. Let’s check if we’re in that group.
Discovering Backrest
Terminal Output
- We’re in the
sysadmgroup, so we can readbackrest_backup.tar.gz.
/opt/backrest:
Terminal Output
backrestis a backup management tool running as root, with a web UI on port9898.resticis the underlying backup engine. It’s listed on GTFOBins as a tool that can be abused for privilege escalation.
./backrest shows us the version and confirms the web UI is bound to 127.0.0.1:9898:
Terminal Output
- The service is already running. We need to access it from our attacker machine via port forwarding.
Accessing Backrest via SSH Port Forwarding
Attacker Linux
http://127.0.0.1:9898 on our machine. The Backrest login page is presented — but our current credentials don’t work.
Terminal Output
- No public exploits for this version. We need valid credentials.
Extracting Credentials from the Backup Archive
Let’s pull the backup archive down to our machine:Attacker Linux
.gz extension, it’s a plain .tar):
Attacker Linux
config.json with Backrest’s user configuration:
Terminal Output
- A user named
backrest_rootwith apasswordBcryptfield. The value looks like base64 — let’s decode it.
Attacker Linux
Terminal Output
- A bcrypt hash (
$2a$). Hashcat mode 3200.
Attacker Linux
Terminal Output
Terminal Output
- Logged into the Backrest web UI.
Abusing Backrest to Read Root Files
With admin access to Backrest, we can configure backup repositories and run backup jobs. Sincerestic (Backrest’s backend) runs as root, we can use it to read any file on the system.
The approach:
- Create a new backup repository at
/tmp/backup - Add the
/rootdirectory as a backup source - Run the backup job to snapshot the
/rootdirectory - Use the web CLI to dump files directly from the snapshot
Terminal Output
Terminal Output
Terminal Output
- Root flag:
0f05692aec3981fe17d04719ff75f304.
Terminal Output
Terminal Output
- We now have root’s private key and can SSH in directly as
root.
Key Learnings
1. TensorFlow/Keras Malicious Model RCE (CVE-2024-3660)
TensorFlow’s.h5 model format supports Lambda layers that execute arbitrary Python code when the model is loaded. By crafting a malicious .h5 file with a reverse shell payload in a Lambda layer, any application that loads untrusted models is vulnerable to RCE. Applications should never load model files from untrusted sources, and TensorFlow ≥ 2.13 is affected.
2. Docker Environment Compatibility
When generating exploit files that rely on specific library versions, the execution environment matters. Running Docker inside VirtualBox can causeIllegal instruction (core dumped) errors due to CPU instruction set mismatches (AVX/AVX2). If this happens, try running Docker natively on Windows via WSL instead.
3. Encoded Hashes in Configuration Files
Backup tools and other services sometimes store bcrypt hashes in their config files encoded in base64 rather than raw. Always run suspected hash strings through a base64 decode before attempting to crack them — a$2a$ prefix after decoding indicates bcrypt (hashcat mode 3200).
4. Abusing Backup Tools for Privileged File Read
Backrest (and its backend restic) runs with elevated privileges to perform system backups. If an attacker gains admin access to the Backrest web UI, they can configure a backup job targeting any directory (e.g., /root) and use the dump command to read arbitrary privileged files, including root.txt and SSH private keys.

