Skip to main content

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
Output of Nmap:
Terminal Output
A few key notes:
  • 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 to http://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
/etc/hosts
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
  • /login and /register endpoints exist — we can create an account.
  • /dashboard redirects 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.
The application provides a Dockerfile and requirements.txt for download, which immediately tells us the server-side environment: Dockerfile:
Terminal Output
requirements.txt:
Terminal Output
  • The server is running TensorFlow 2.13.1 in a Python 3.8 environment.
  • The upload endpoint expects .h5 files — the standard format for saving Keras/TensorFlow models.
The page also includes a sample model-building script:
Terminal Output
  • This confirms .h5 is 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.
We hit a frustrating wall initially — running the exploit inside Docker on our Kali VM (VirtualBox) consistently produced:
Terminal Output
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 Illegal instruction error immediately.
Steps to build and run the exploit container: Step 1: Build the Docker image from the provided Dockerfile (inside the folder containing it):
Attacker Linux
Step 2: Run the container with our working directory mounted so we can access the generated exploit file:
Attacker Linux
  • -v $(pwd):/app mounts our current directory into /app inside the container, so files created inside are accessible on our host.
Inside the container, we run the exploit script to generate a malicious exploit.h5 file with a reverse shell payload embedded in a Lambda layer.

Getting a Shell

We upload the generated exploit.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 app user.

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 in app.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 gael first — they’re likely the primary user on the box.

Cracking gael’s Hash

Attacker Linux
Terminal Output
Terminal Output
We also check /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
A few key findings: Active Ports:
Terminal Output
  • Port 9898 is listening locally — likely the Backrest service we saw in /opt.
  • Port 5000 is also internal (probably the Flask app backend).
Interesting backup file:
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 sysadm group, so we can read backrest_backup.tar.gz.
Let’s look at what’s in /opt/backrest:
Terminal Output
  • backrest is a backup management tool running as root, with a web UI on port 9898.
  • restic is the underlying backup engine. It’s listed on GTFOBins as a tool that can be abused for privilege escalation.
Running ./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
Now we can browse to 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
Extract it (note: despite the .gz extension, it’s a plain .tar):
Attacker Linux
Inside we find a config.json with Backrest’s user configuration:
Terminal Output
  • A user named backrest_root with a passwordBcrypt field. 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. Since restic (Backrest’s backend) runs as root, we can use it to read any file on the system. The approach:
  1. Create a new backup repository at /tmp/backup
  2. Add the /root directory as a backup source
  3. Run the backup job to snapshot the /root directory
  4. Use the web CLI to dump files directly from the snapshot
Using the Backrest web CLI:
Terminal Output
This shows us the snapshot ID. We then dump the root flag:
Terminal Output
Terminal Output
  • Root flag: 0f05692aec3981fe17d04719ff75f304.
Let’s also grab root’s SSH private key for persistence:
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 cause Illegal 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.

Tags


Initial Access

#Web_Exploit #File_Upload #AI_Model_RCE #Public_Exploit #Credentials_Hunting

Privilege Escalation

#Credentials_Hunting #Backup_Abuse #Backup_Tools
Last modified on February 17, 2026