HackTheBox / Secret

htb secret
LinkDifficultyCreator
SecretEasyz9fr

Scan / Enumeration

Makinedeki bütün açık portları erişebilmek için nmap‘e -p- parametresini verdiğim bir tarama ile keşif aşamasına başlıyorum. Varsayılan olarak -T3 hızında tarama yapan nmap’in hızını -T4 parametresi ile arttırıyorum. Tarama bitmeden bulduğu port numaralarını ekrana basması ve taramanın ne kadar daha süreceği hakkında düzenli olarak bilgi vermesi için -vv parametresini kullanıyorum.

sudo nmap -T4 -vv -p- $IP
nmap çıktısı

Bu tarama sonucunda 22, 80 ve 3000 portlarının açık olduğunu görüyorum. Yaptığımız varsayılan tarama herhangi bir versiyon taraması yapmadığı için çıktıda bize söylediği servislerin, o port numarasında genelde kullanılan servisler olduğunu biliyorum ve doğru bilgiye ulaşabilmek için ayrıntılı bir nmap taraması yapıyorum.

-p parametresini kullanarak hangi portları taramak istediğimi belirtiyorum. -sVC parametresi ile hem versiyon taraması hem de script taraması yapmasını istiyorum. -oN parametresiyle tarama tamamlandığında çıktıyı bir dosyaya yazmasını söylüyorum.

sudo nmap -sVC -p 22,80,3000 $IP -oN secret.nmap
nmap çıktısı

22 portunda SSH – Secure Shell – servisi, 80 portunda HTTP web server’ı ve 3000 portunda ise ilk taramada söylediği ppp servisinin aksine HTTP servisinin çalıştığını görüyorum. Temel nmap keşfinden sonra en küçük port numarasından başlayarak, bütün servisleri keşfetmeye ve olası zafiyetleri sömürmeye çabalıyorum.

22 OpenSSH

22 portunda çalışan SSH servisiyle başlıyorum. searchsploit‘i kullanarak OpenSSH 8.2p1 versiyonu için zafiyet araması yapıyorum fakat bir şey bulamıyorum. Google aracılığıyla da OpenSSH servisinin bu versiyonu için herhangi bir zafiyet olup olmadığını kontrol ediyorum fakat elle tutulur bir şey ile karşılaşmıyorum. Son olarak nmap’in ssh için bulundurduğu scriptleri locate .nse | grep ssh komutuyla kontrol edip inceliyorum fakat buradaki scriptlerin çoğunun enumeration’a yönelik olduğunu görüp diğer servislerden bir sonuç olamazsam dönmek üzere bırakıyorum.

ssh exploit search

80 & 3000 HTTP

80 portundaki HTTP servisi için tarayıcı üzerinden web sayfasını inceleyerek araştırmaya başlıyorum. Öncelikle safyadaki yönlendirme linklerinin nerelere yönlendirdiğini inceliyorum daha sonra ise ulaşabildiğim sayfaların kaynak kodlarına göz gezdiriyorum. Sayfa içerisinde /download/files.zip dizini altındaki dosyanın sitenin pazarlamaya çalıştığı ürün olan Auth API uygulamasına ait kaynak kodlar olduğunu görüyorum. Dosyayı indiriyorum ve incelemeye başlıyorum.

Dosyayı incelediğim esnada website üzerinde daha fazla bilgi edinebilmek için gobuster aracılığıyla dizin taraması yapıyorum. dir argumanı ile dizin taraması yapmasını söylüyorum. -u parametresiyle üzerinde tarama işlemini gerçekleştireceği url’i belirtiyorum. -w parametresiyle kullanacağı wordlist’i veriyorum. -x parametresiyle olası dosya uzantıları verip, wordlistteki her kelime için verdiğim uzantıları kullanarak dosya araması da yapmasını istiyorum. -t parametresiyle varsayılan olarak 10 olan thread sayısını arttırıp taramanın hızlanmasını sağlıyorum. -o parametresiyle tarama işlemi sonuçlandığında çıktısını bir dosyanın içine yazmasını istiyorum.

gobuster dir -u http://<IP>/ -w common.txt -x db,bak,php,txt -t 30 -o secret.buster

İndirdiğim dosyada .env dosyası içerisinde TOKEN_SECRET isminde bir değer buluyorum. Bu değer secret olduğu için gerçek bir değer mi yoksa kullanıcılar doğru değere erişemesin diye öylesine mi yazılmış emin olamıyorum. index.js dosyasını okuduğumda çalışan app’in makinedeki 3000 portunu dinlediğini görüyorum. Daha fazla bilgiye ulaşabilmek için diğer kod dosyalarını okumadan önce .git dizinini incelemeye karar veriyorum.

.git dizini içerisinde git log komutunu kullanarak repoya önceden girilmiş commitleri görebiliyorum ve burada .env'in silindiğine dair bir commit ile karşılaşıyorum.

git log

Bu dosyanın silinmediği haline erişebilmek için git checkout $commitId komutunu kullanarak dosyanın silinmesinden bir önceki commit’e gidiyorum ve dosyaya erişebiliyorum.

secret token

Burada bulduğum token’i uygulamanın neresinde kullanacağımı anlamak için 3000 portunda çalışan servisi incelemeye başlıyorum. Bu portta da 80 portunda çalışan websitenin aynısının çalıştığını görüyorum. Biraz daha inceliyorum ve bir fark göremeyince denemelerime 80 portunda devam etmeye karar verip geri dönüyorum.

Anasayfada bulunan yönlendirmeleri takip ederek docs dizini altına gelip uygulamanın dökümantasyonunu okumaya başlıyorum. Burada kullanıcının kayıt olup auth-token‘e erişmesinden sonra /api/priv dizinine header’larına auth-token’i koyup GET request atarak panele erişebileceği söyleniyor. Bu yöntem ile hem 80 portundaki uygulamaya hem 3000 portundaki uygulamaya bağlanmaya çalışıyorum fakat Invalid Token uyarısını alıyorum.

invalid token

Dokümanı tekrar ve daha dikkat ederek okuyorum. Anlıyorum ki elimdeki auth-token . ile ayrılan 3 temel kısımdan oluşuyor ve benim elimdeki token muhtemelen bu tokenin data kısmını oluşturan bölüm. Sisteme kendi kullanıcı adım ile kayıt olup tam bir auth-token aldıktan sonra data kısmına elimdeki token’i girerek yetki yükseltebileceğimi düşünüyorum.

cURL aracını kullanarak /api/user/register adresine gerekli POST request’i gönderebiliyorum. -X parametresiyle request metodunu POST olarak belirtiyorum. -H parametresine Content-Type header’ını vererek göndereceğim bilginin JSON olduğunu server’a anlatıyorum. -d parametresiyle request’in body’sinde iletmem gereken JSON datayı iletiyorum.

curl -X POST http://$IP/api/user/register -H "Content-Type: application/json" -d '{"name":"$name","email":"$email","password":"$password"}'

Kayıt olduğum bilgilerle /api/user/login dizinine request atarak kullanıcıma ait auth-token’i alıyorum.

curl -X POST http://$IP/api/user/login -H "Content-Type: application/json" -d '{"email":"$email","password":"$pass"}'
post request

Kayıt olup elde ettiğim auth-token‘in data kısmıyla elimdeki token’i kıyasladığımda aynı tipte olmadıklarını anlıyorum. Kaynak koddaki dosyaları biraz daha inceleyip sistemin tam olarak ne yaptığını anlamaya çalışıyorum. Kaynak kodda routes isimli dizindeki auth.js dosyasında auth-token’i oluşturmak için jwt isminde bir değişken kullandığını görüyorum. Dosyanın ilk satırlarında ise bu değişkenin jsonwebtoken‘in yerine kullanıldığını görüp googleda arama yapıyorum. Elimdeki secret-token’i kullanarak auth-token üzerinde oynama yapabileceğim bir websitesi buluyorum.


Gain Shell

Token üzerinde ne gibi oynama yapmam gerektiği ve bu tokeni nerelerde kullanabileceğime dair daha fazla bilgiye erişmek için kodları okumaya devam ediyorum. routes dizini altındaki dosyalarda kullanıcı adının theadmin olması durumunda farklı çıktılar alacağımı görüp token üzerinde name kısmını theadmin olacak şekilde değiştirip yeni bir auth-token elde ediyorum.

theadmin token

Bu tokeni kullanarak /api/priv dizinine request attığımda server’ın beni admin olarak gördüğüne dair bir response alıyorum.

theadmin request

Fakat hala admin yetkisiyle ne yapabileceğimi bilmiyorum bu yüzden kodları incelemeye devam ediyorum. Uzun süre kodlara baktıktan sonra, baktığım kodların server’da çalışan uygulamanın son hali olmadığını hatırlıyorum. .env dosyasındaki secret-token‘i almak için eski bir commit’e gittiğimi ve hala bu commit’in içerisinde olduğumu hatırlayarak okuduğum local-web dizinini silerek daha önce indirdiğimiz files.zip dosyasından uygulamanın son halini çıkartıyorum.

private.js dosyası içerisinde, /logs adresinin file isminde bir query alıp bu query’nin değerini git log --oneline $file komutu ile çalıştırdığını görüyorum. Gönderdiğim requestteki query’i, ; id şeklinde vererek git log --oneline ; id komutunu server’a yollayıp id‘nin çıktısını almayı umuyorum. cURL aracılığıyla request’i kolay bir şekilde yollamak için gireceğim parametreyi URL Encode ile encode ederek gönderiyorum.

curl http://$IP/api/logs?file=%3B%20id -H "auth-token: $auth-token"
RCE

Serverda komut çalıştırabiliyorum ve reverse shell alabilmek için file parametresiyle bir payload yolluyorum.

reverse shell

Daha stabil bir shell’e erişebilmek için dasith kullanıcısının id_rsa dosyasını bulmak amacıyla .ssh dizinine bakıyorum fakat bulamıyorum. Kendi makinemde ssh-keygen ile bir id_rsa dosyası oluşturup id_rsa.pub‘ı dasith’in authorized_keys dosyasına ekliyorum ve kendi dosyamla SSH üzerinden bağlantı kurabiliyorum.


Privilege Escalation

sudo -l komutuyla dasith kullanıcısının sudo yetkisini kontrol etmek istiyorum fakat kullanıcının şifresini istediği için edemiyorum. find / -perm -4000 2>/dev/null komutuyla SUID dosyalarını kontrol ediyorum ve /opt/count dosyasının SUID dosyası olduğunu görüyorum.

count binary’sini strings komutuyla inceleyip ne yaptığını anlamaya çalışıyorum. Bir dosya veya dizin adresini input olarak alıp dosya veya satır sayısını veren bir C programı gibi görünüyor. Programı çalıştırıp deniyorum.

count

root kullanıcısının .ssh dizini altındaki id_rsa dosyası için de kodu çalıştırıyorum fakat bu seferde dosya içerisindeki satır sayısı gibi bilgilerden başka bir şey elde edemiyorum. Aynı dizinde yer alan code.c dosyasının count binary’sinin compile edilmemiş hali olduğunu anlıyorum ve incelemeye başlıyorum. Kodu uzun süre inceledikten sonra //Enable coredump generation yorum satırının olduğu yere odaklanmaya ve core dump’ı araştırmaya karar veriyorum.

coredump

Öncelikle Arch Wiki‘de gördüğüm bir yöntem ile count binary’sini çalıştırıp CTRL+Z ile arka plana aldıktan sonra core dump yapmayı deniyorum fakat başaramıyorum.

core dump fail

ptrace: Operation not permitted hatası ile başarısız oluyorum. Red Hat‘in bulduğum bir kaynağındaki gcore yöntemini deniyorum fakat onda da aynı hata nedeniyle başarısız oluyorum.

coredump fail 2

Stack Overflow‘dan bulduğum kill -ABRT $pid yöntemiyle core dump yapmayı başarıyorum. Crash raporlarının saklandığı dizin olan /var/crash dizinine baktığımda dosyaya ait hata raporunu görüyorum.

Dosyayı incelemeye çalışıyorum fakat büyük bir bölümü encoded olan dosyanın okunabilir kısmından elle tutulur bir şey elde edemiyorum. Crash dosyalarının nasıl incelenebileceğine dair araştırma yaptığımda AskUbuntu‘da konuyla ilgili bir başlık buluyorum. Burada gördüğüm gibi apport-unpack komutunu kullanarak raporu incelemeyi deniyorum.

apport-unpack

Çıkan dizindeki dosyaları sırasıyla incelemeye başlıyorum. CoreDump‘ı okumaya çalıştığımda önemden akan çoğu anlamsız karakterler arasında root kullanıcısına ait id_rsa dosyasının çıktısını görüyorum ve strings CoreDump komutuyla tekrar okuyup çıktıyı alıyorum.

id_rsa

Buradan elde ettiğim id_rsa dosyasını kullanarak secret makinesinin root kullanıcısına bağlanabiliyorum.

ssh root

Gev