Terraform has become the de facto standard for infrastructure as code (IaC). From cloud-native startups to global enterprises, teams rely on Terraform to define, provision, and manage infrastructure with speed and consistency across cloud and on-prem environments.
In this write-up, we will explore the βPreviousβ machine from Hack The Box, categorised as an easy difficulty challenge. This walkthrough will cover the reconnaissance, exploitation, and privilege escalation steps required to capture the flag.
Objective:
The goal of this walkthrough is to complete the βPreviousβ machine from Hack The Box by achieving the following objectives:
User Flag:
After thoroughly enumerating the Next.js application running on port 80, we discovered a critical path traversal vulnerability in the publicly exposed /api/download endpoint. Consequently, by crafting a specially designed payload, we were able to read sensitive system files. From these files, we extracted user information that revealed a valid account. Using the discovered credentials, we then gained SSH access as a standard user and, ultimately, successfully retrieved the user flag located in the home directory.
Root Flag:
With initial access secured, we enumerated the userβs sudo privileges and discovered the ability to run a specific Terraform command as root in a controlled directory. By leveraging a misconfiguration in the local Terraform setup, we first prepared a carefully crafted binary. Then, during the privileged Terraform operation, the binary was loaded, which in turn executed our payload and consequently granted elevated permissions. This allowed us to obtain a root shell and read the final root flag from the protected location.
Enumerating the Previous Machine
Reconnaissance:
Nmap Scan:
Begin with a network scan to identify open ports and running services on the target machine.
nmap-sC-sV-oAinitial10.10.11.83
Nmap Output:
ββ[dark@parrot]β[~/Documents/htb/previous]ββββΌ$nmap-sC-sV-oAinitial10.10.11.83# Nmap 7.94SVN scan initiated Sat Jan 10 03:28:37 2026 as: nmap -sC -sV -oA initial 10.10.11.83Nmapscanreportfor10.10.11.83Hostisup (0.045s latency).Notshown:998closedtcpports (conn-refused)PORTSTATESERVICEVERSION22/tcpopensshOpenSSH8.9p1Ubuntu3ubuntu0.13 (Ubuntu Linux; protocol2.0)| ssh-hostkey:| 2563e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)|_25664:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)80/tcpopenhttpnginx1.18.0 (Ubuntu)|_http-server-header:nginx/1.18.0 (Ubuntu)|_http-title:Didnotfollowredirecttohttp://previous.htb/ServiceInfo:OS:Linux; CPE:cpe:/o:linux:linux_kernelServicedetectionperformed.Pleasereportanyincorrectresultsathttps://nmap.org/submit/.# Nmap done at Sat Jan 10 03:28:48 2026 -- 1 IP address (1 host up) scanned in 11.13 seconds
Analysis:
22/tcp: Open and running OpenSSH 8.9p1 on Ubuntu, providing secure shell access for remote login. The host exposes ECDSA and ED25519 keys for authentication.
Port 80/tcp: Open and serving HTTP via Nginx 1.18.0 on Ubuntu. The server header confirms the version, and the default page redirects to http://previous.htb
Exploitation on the Previous Machine
Web Application Exploration:
The browser displays the root path / as a fully public marketing landing page for PreviousJS. The page features a tagline about βthe technology of yesterdayβ and highlights three main benefits: βNo-Side Renderingβ, βHeavyweightβ, and βOpt-Out Middlewareβ.
During Gobuster directory enumeration with a small lowercase wordlist, /signinin emerges as the only accessible endpoint returning 200 OK, representing the actual login page with the double βinβ. In contrast, /api, /docs, and numerous /api* variants (such as api3, api_test, apidoc, and apis) consistently return 307 Temporary Redirects. As a result, these requests are forwarded to the sign-in page with corresponding callbackUrl parameters.
Access to /signinin?callbackUrl=http://localhost:3000/api loads the login page (note the double βinβ in path) with preserved localhost callbackUrl in the URL, confirming the application accepts and reflects this misconfigured origin
Authentication Flow Analysis on Prevous Machine
When requesting the root path / with an If-None-Match header matching the current ETag, the server returns 304 Not Modified. Meanwhile, the response continues to include the next-auth.callback-url cookie set to http://localhost:3000/api/download.
This shows a GET request to /_next/data/β¦/docs.json (a typical Next.js client-side data fetch route for static/SSG pages). The server responds with 307 Temporary Redirect and the custom header x-nextjs-redirect: /api/auth/signin?callbackUrl=%2Fdocs, forcing unauthenticated users to the sign-in page.
When accessing /api, the request triggers a redirect chain (307 β 302) that ultimately leads to /api/auth/signinin?callbackUrl=%2Fapi. At the same time, the server sets the next-auth.callback-url cookie to http://localhost:3000/api/download. This behavior clearly indicates a NextAuth.js misconfiguration, caused by the application using the default development base URL.
Β CVE-2025-29927 Enumeration
GET request to /signinin?callbackUrl=http://localhost:3000/api returns 200 OK with the full login page HTML (title βSign In Inβ, input fields for Username and Password, and βSign inβ button), preserving the suspicious localhost callback URL in the query string and continuing to carry the next-auth.callback-url=http://localhost:3000/api/download cookie, confirming the application fully accepts and reflects the development-origin misconfiguration in the authentication flow.
On the page, Next.js static chunks and scripts (e.g., _buildManifest.js, _ssgManifest.js) are loaded. Consequently, the localhost callback remains in the URL, and the persistent next-auth.callback-url cookie continues to point to http://localhost:3000/api/download.
A GET request to the Next.js data route /_next/data/β¦/docs.json triggers a 307 Temporary Redirect. The server includes a custom x-nextjs-redirect header that points to /api/auth/signinin?callbackUrl=%2Fdocs, proving that the application protects even client-side data fetches for the documentation page with authentication middleware. The persistent next-auth.callback-url cookie continues to reference http://localhost:3000/api/download
Path Traversal & Sensitive File Disclosure
The Next.js data route /_next/data/β¦/docs.json responds with 200 OK and returns the full rendered HTML of the PreviousJS documentation page. Additionally, the response includes a highly repeated X-Middleware-Subrequest header, with the middleware appearing five times.
The Next.js data route /_next/data/β¦/docs.json returns 200 OK. It delivers the full rendered HTML of the documentation overview page. The content prominently shows navigation links to β/docs/getting-startedβ and β/docs/examplesβ
Accessing /docs/content/examples via GET triggers a 307 Temporary Redirect. The custom x-nextjs-redirect header forwards the request to /api/auth/signinin?callbackUrl=%2Fdocs%2Fcontent%2Fexamples. This clearly shows that the nested documentation path stays protected by authentication middleware.
Accessing the path /docs/content/examples via GET results in a 307 Temporary Redirect. The custom x-nextjs-redirect header directs the request to /api/auth/signinin?callbackUrl=%2Fdocs%2Fcontent%2Fexamples. This clearly confirms that authentication middleware protects the nested documentation sub-route.
Path Traversal Vulnerability Discovery
The request includes a persistent next-auth.callback-url localhost cookie and repeated x-middleware-subrequest headers, indicating that the /api/download route bypasses authentication controls even though other application routes remain protected.
The response body contains the full contents of /etc/passwd. Exposed entries include standard system users such as root, bin, daemon, lp, sync, shutdown, halt, mail, uucp, operator, games, gopher, ftp, and nobody. Custom users like node (UID 1000) and nextjs (UID 1001) are also disclosed.
Sensitive File Disclosure
A request to /api/download?example=../../../../../../../../app/.env return a 200 OK response. The server replies with Content-Type: application/zip and a Content-Disposition header specifying filename=".env". This shows that the endpoint packages the requested file as a downloadable archive.
Accessing /api/download?example=../../../../../../../../app/.next/routes-manifest.json results in a 200 OK response. The server returns Content-Type: application/zip along with a Content-Disposition header specifying filename="routes-manifest.json".
Interaction with /api/download?example=../../../../../../../../app/.next/server/pages/api/auth/[...nextauth].js produces . Within the returned archive, the compiled NextAuth API route handler is exposed. This demonstrates that internal authentication logic can be retrieved as a downloadable file.
This leak confirms the intended credentials for login are:
Username: jeremy
Password: MyNameIsJeremyAndILovePancakes (as defined in ADMIN_SECRET)
Initial Access via SSH on Previous Machine
Successful SSH login as user jeremy to previous.htb (10.10.11.83) using the password obtained from the leaked NextAuth credentials code (MyNameIsJeremyAndILovePancakes), landing in a standard Ubuntu 22.04.5 LTS shell
Command execution on the target as user jeremy via SSH: cat user.txt displays the standard user flag format string
Escalate to Root Privileges Access
Privilege Escalation:
After running sudo -l as user jeremy on the target and entering the account password, the command reveals the following sudo privileges.
Matching Defaults entries include env_reset, env_delete+=PATH, mail_badpass, secure_path restricted to standard system bins, and use_pty.
User jeremy may run the following command as root without password: (root) /usr/bin/terraform -chdir=/opt/examples apply
Running /usr/bin/terraform without arguments as user jeremy displays the full Terraform CLI help output. The behavior confirms that Terraform is installed on the system. Its binary is accessible at /usr/bin/terraform.
Terraform as an InfrastructureβasβCode Tool
Terraform is an infrastructureβasβcode (IaC) tool created by HashiCorp.
In other words, Terraform allows you to define and manage infrastructure through configuration files rather than manually clicking through dashboards.
The listing confirms that Jeremy has full control over his home directory and Terraform-related files relevant to the sudo rule. Direct read access to the user flag is available without requiring privilege escalation.
The configuration defines a dev_overrides block. This forces Terraform to load a local provider binary from /usr/local/go/bin.
Verification confirms that /bin/bash exists on the system. Typical permissions are set for a default shell on an Ubuntuβbased machine.
This file is clearly the intended local privilege escalation vector.
Commands executed as user jeremy: gcc dark.c -o terraform-provider-examples compiles the fixed exploit source into a binary named terraform-provider-examples, followed by chmod +x terraform-provider-examples to make it executable.
Placement in /usr/local/go/bin allows Terraform to load the provider during the apply operation in /opt/examples. The dev_overrides setting in .terraformrc triggers this behavior. Together, these actions complete the setup for root privilege escalation.
Executing sudo /usr/bin/terraform -chdir=/opt/examples apply as user jeremy (after a password prompt) runs successfully as root, displaying the Terraform warning about active provider development overrides pointing to /usr/local/go/bin. Terraform refreshes state, finds no changes needed, and completes the apply with 0 resources added/changed/destroyed. The output confirms the custom provider was loaded without errors, and the malicious binary executed its payload (making /bin/bash SUID root), achieving full root privilege escalation via the dev override and sudo rule.
As a result of the exploit, the presence of s in both the owner and group execute bits (rwsr-sr-x) confirms that /bin/bash is now SUID root and SGID root. Consequently, any user can execute it and immediately gain a root shell, for example, by running bash -p. This is the direct result of the successful execution of the malicious Terraform provider binary, completing the root privilege escalation on the machine.
Execution of /bin/bash -p as user jeremy immediately spawns a new bash shell with the prompt changing to bash-5.1#, indicating successful privilege escalation to root shell via the now-SUID /bin/bash binary.