Hacktrick 2017 Tersine Mühendislik Eğitimi CTF
Giriş
Bu yazımda sizlere Hacktrick 2017 Tersine Mühendislik eğitiminin sonunda yapılmış olan ve öğrendiklerimizi kullandığımız basit 4 soruluk yarışmadan bahsedeceğim. Soruları GitHub‘a yükledim, eğitimde kullanılan sanal makinenin indirme linki ile soruların linkini yazının sonuna ekledim. Bir önceki yazımda Hacktrick 2017 etkinliğine biraz değinip Tersine Mühendislik eğitimine ve bu eğitime alım için yapılan 2 soruluk eleme sınavından bahsetmiştim. O yazıyı okumak isterseniz buraya tıklayabilir yada yazının sonunda paylaştığım linklerde bulabilirsiniz. Hemen sorulara geçiyorum.
Sorular
Soru 1
İlk soru için verilen dosyayı (soru1
) çalıştırdığımızda bizden seri numarası girmemizi istiyor. Doğruysa “Serial is valid!”, değilse “Serial is not valid!” diye çıktı veriyor:
|
|
Tabi burada amaç doğru seri numarasını bulmak, yoksa bir hex editor ile “if” kontrolünün yapıldığı (koşullu dallanma) opcode’u (operation code/işlem kodu : 74) “if not” (75) olarak değiştirip kontrolü rahatça atlayabiliriz:
Bu değişiklikten sonra program, yanlış girilen tüm seri numaralarını doğru kabul edecektir:
|
|
Fakat bu soruda bize sorulan, doğru seri numarasını bulmamız o yüzden programın assembly kodlarını incelemeye başlıyoruz. Bu aşamada kullanabileceğimiz birçok araç bulunmakta (IDA, OllyDbg, vb.) ancak eğitimde Linux’ta bulunan gdb
ve objdump
araçları gösterildiği için onları kullanacağım. Öncelikle file
komutu ile dosyanın stripped olup olmadığına bakıyorum çünkü stripped elf dosyalarında farklı bir yöntem izlemem gerekecek.
|
|
Çıktıda da yazdığı üzere dosyanın stripped olmadığını anlıyoruz. Daha sonra terminale gdb -q soru1
yazıyorum ve ardından disas main
diyerek programın (soru1
) main fonksiyonunu disassemble ediyorum. (Intel syntax’ına alışkınsanız main’i disassemble etmeden önce set disassembly-flavor intel
komutunu girmeniz yeterli):
Burada assembly kodları daha iyi anlaşılabilmesi için parçalara bölerek numaralandırdım ve numara sırasına göre sizlere bu işlemleri anlatacağım. Tabi sadece ilk soru için bütün assembly kodlarını koyup açıklıyorum, diğer sorular için sadece gerekli kısımları koyacağım. GitHub‘a yüklediğim soruları siz de indirip deneyerek bu işlemleri yapabilirsiniz.
Assembly Bölüm 1
Üstteki resmin “1” diye işaretlenen kısmında, başlangıç adresi 0x8048590 olan değer, eax register’ına kopyalıyor, daha sonra buradan stack’e kopyalayıp printf fonksiyonu ile ekrana bastırıyor. Bu adresin içerisine baktığımızda:
|
|
Buradaki “x” parametresi, belleğin belirli bir adresindeki içeriği kendi belirlediğimiz formatta görüntülememizi sağlar. Belirlediğimiz format ise “s”, yani string. Anladığımız üzere bu adres, “Enter your licence key:” yazı dizisinin başlangıç adresi.
Assembly Bölüm 2
Burada yani <main+19>’da yine bir adresteki değeri eax register’ına kopyalıyor. Hemen bakalım içindeki değere:
|
|
Anlıyoruz ki burada kullanıcıdan 4 tane “???????-???????-???????-???????” formatında değer isteniyor. Zaten devamında da kullanıcıdan 4 tane değer alıyor. Bunuda “[esp+0x…]”daki “+” dan anlıyoruz.
Assembly Bölüm 3
Bu kısımda (main+60) kullanıcıdan alıp stack’e koyduğu değerleri, stack’ten register’a kopyalayıp teker teker birbirleri ile xor işlemine sokuyor ve en son çıkan değeri “0x1ce7ea” hex değeri ile karşılaştırıyor (cmp:compare). Eğer bu değere eşit ise (je:jump if equal) “0x80484b7” adresine (main+99) atlıyor, yani 5. adıma atlıyor. 5. adımda puts komutu ile “0x80485ce” adresinden başlayan değeri ekrana basıyor:
|
|
Daha sonra 6. adıma geçip sıfır değeri döndürerek programdan çıkıyor. Eğer xor işleminden çıkan değer, “0x1ce7ea” hex değerine eşit değil ise 4. adımdan devam ederek “0x80485b9” adresinden başlayan değeri ekrana basıyor:
|
|
Ardından jmp (jump) komutu ile “0x80484c3” adresine (main+111) yani 6. adıma atlıyor ve programdan çıkış yapıyor. Şimdi akıllarda şöyle bir soru olabilir, neden “0x1ce7ea” değeri ile karşılaştırılıyor. Cevabı basit, soruyu hazırlayan Robin Dimyanoğlu’nun Twitter adresi (@1ce7ea) oluyor bu hex değeri 😃
Programın baştan sona assembly kodlarını anlatmaya gerek yoktu elbette ama ilk soru olduğu için gerekli olduğunu düşündüm. Şuan programın nasıl çalıştığını aşama aşama öğrendiğimize göre doğru bir seri numarası bulma işlemine geçebiliriz. Programda girilen 4 tane değer, birbiri ile xor işlemine girmeseydi sadece “0x1ce7ea” değerinin decimal (ondalık) karşılığını girip çok kolay bir şekilde doğru seri numarasına erişebilirdik ancak burada ihtiyacımız olan xor bilgisi. Hemen kısaca xor tablosuna bakalım ve ne olduğunu öğrenelim:
Seri Numarası Bulma
Programın baştan sona assembly kodlarını anlatmaya gerek yoktu elbette ama ilk soru olduğu için gerekli olduğunu düşündüm. Şuan programın nasıl çalıştığını aşama aşama öğrendiğimize göre doğru bir seri numarası bulma işlemine geçebiliriz. Programda girilen 4 tane değer, birbiri ile xor işlemine girmeseydi sadece “0x1ce7ea” değerinin decimal (ondalık) karşılığını girip çok kolay bir şekilde doğru seri numarasına erişebilirdik ancak burada ihtiyacımız olan xor bilgisi. Hemen kısaca xor tablosuna bakalım ve ne olduğunu öğrenelim:
Tabloyu kısaca size özetlemem gerekirse xor işleminde binary değeri aynı olanlar 0 (sıfır), farklı olanlar 1 (bir) olarak dışarı çıkıyor. “0x1ce7ea” hexadecimal (16 tabanında) sayısının decimal (on tabanında) karşılığına bakarsak:
|
|
1ce7ea = 1894378 olduğunu görürüz. “0x1ce7ea” binary karşılığı ise: “0000 0000 0001 1100 1110 0111 1110 1010”. Şimdi xor’un güzelliği burada ortaya çıkıyor 🙂 ne demiştik, aynılar 0 (sıfır), farklılar 1 (bir). Biz bu binary’i sıfır ile xor işlemine sokarsak ne çıkar bakalım hemen:
Gördüğümüz gibi sayının kendisi çıkıyor. Yani biz elimizdeki sayıyı (1894378) sıfır değerleri ile xor işlemine sokarsak yine aynı sayı çıkacak ve girdiğimiz seri numarası doğru olacak. Örnek: “1894378-0000000-0000000-0000000”:
Tabii ki tek doğru serial key bunlar değil, örneğin: “1894378-45-4-41” veya “75-1894320-58-43”. Nasıl olduğunu merak ediyorsanız binary’e çevirip xor işlemlerini yapabilirsiniz 🙂 hatta C, Python, vb. dilleri ile seri numarası oluşturan çok kısa bir program yazabilirsiniz. Evet birinci sorunun çözümü bitti, diğer soruları kısa tutmaya çalışacağım 😃.
Soru 2
İkinci soruda verilen dosyayı (soru2
) çalıştırdığımızda çıkan sonuç:
|
|
Terminale gdb -q soru2
, ardından disas main
yazıp assembly kodlarını incelediğimde sadece puts komutu ile ekrana yukarıdaki çıktıyı verdiğini gördüm, yani programın main fonksiyonunda flag olabilecek hiçbir şey yoktu. Ardından programın içindeki fonksiyonlara göz atmak istedim. Bunun için terminalde gdb için info functions
, readelf: readelf -s soru2
veya objdump ile objdump -d -M intel soru2
diyerek programın içindeki fonksiyonları inceleyebilirsiniz.
|
|
Programın içinde hiding adlı bir fonksiyon tanımlanmış ancak bu fonksiyon çağırılmadığı için görmüyoruz tabi 🙂 fonksiyonun içine bakmak için disas hiding
yazmak yeterli:
Fonksiyonun çok ayrıntısına girmeyeceğim, kırmızı oklar bu fonksiyonun bir for loop (döngü) olduğunu gösteriyor. İlk başta stack’e 0 (sıfır) sayısını atıyor, daha sonra bunu 0xa (10) değeri ile karşılaştırıyor. küçük eşit ise içerisindeki işlemleri 11 kez gerçekleştirip çıkıyor. Tabi burada ekrana birşey basmadığı için fonksiyonu çalıştırabilsek bile ekrana bir çıktı vermeyecek, o yüzden yapmamız gereken bu fonksiyona atlamak için main’e bir break point koyup (break main
) daha sonra hiding fonksiyonu çalışıp çıkmasın diye o fonksiyonun sonuna da bir break point koyacağız: break *0x080483f3
. Bu aşamada programı run
veya r
komutu ile çalıştırdığımızda fonksiyona atlayıp for döngüsü bittikten sonra program kapanmadan duracaktır. Başka bir adrese atlamak için bir komut daha var, programı çalıştırdıktan sonra jump hiding
veya jump *[adres]
şeklinde girerseniz oraya da atlayacaktır. Şimdi son bir aşama kaldı, o da sonucu ekrana yazdırmak:
Görüldüğü gibi flag’i bulduk: “x0r_s0_3asy” 🙂
Soru 3
Üçüncü soruyu da biraz zorlaştırmak için striplemişler. Soru yine kolay tabi ama amaç burada öğrendiklerimizi kullanmak. soru3
ü çalıştırdığımızdaki çıktı:
|
|
Burada heap hafızasına bakmamızı istiyor muhtemelen o yüzden main’de neler oluyor bir bakalım. Tabi dosya stripped olduğu için gdb‘de disassemble main
dediğimizde hata verecektir çünkü öyle bir fonksiyon bulamayacaktir. main fonksiyonunun başladığı adresi bulabilmek için objdump aracını kullanacağım. Terminale objdump -d -M intel soru3
yazarak programı komple disassemble ediyorum. Burada bakacağım ilk yer .text bölümü olacak.
main fonksiyonunun başladığı adresi hemen __libc_start_main@plt fonksiyonunun çağırıldığı satırın bir üstündeki satırda görebilirsiniz (“0x80483f4”). Daha sonra yine objdump çıktısından bu adresi bulalım ve main’e ulaşalım. main’i biraz incelemeye değer bulduğum için disassemble edilmiş kodların tamamını koymayı tercih ediyorum:
- 1 olarak işaretlenen bölümde malloc fonksiyonu ile heap hafızadan 16’lık bir boş alan ayrılıyor.
Daha sonra oklardan da anlaşılacağı üzere for döngüsüne giriliyor. Bu for döngüsü de tam 16 kez dönüyor. Döngü her döndüğünde sırasıyla 0x8049640 adresinden bir değer alıp bu değerden 3 çıkararak tekrar sırası ile heap hafızadan ayırdığımız yere kopyalıyor. Peki bu (0x8049640) adreste ne var diye bakarsak grb|rxboryhblw yazısını görüyoruz. Bu karakterlerden teker teker 3 çıkarırsak, yani alfabede 3 geri kaydırırsak do you love it yazısının çıktığını görürüz 🙂 tabii bunun daha kısa yolu ise heap hafızaya bakmak olacaktır. Onu da şöyle yapıyoruz:
Burada programı çalıştırdığımızda çıkmasın diye programın sonuna bir break point koyuyoruz, ardından programı çalıştırıyoruz. Program break point’te durduğunda x/3xw $sp
komutu ile 3 tane 32 bit hex word şeklinde stack pointer’dan adres göster diyoruz. İlk adres “Do you know how to look heap !” cümlesinin başlangıç adresi, ikinci ise bizim aradığımız heap alanı. x/s 0x0804a008
komutu ile de ekrana bastırıyor ve “do_you_love_it\n” flag’ini buluyoruz.
Eveeet 3. sorunun da anlatımı bitti. soru_son
un çözümünü başka bir yazıya saklıyorum, bu yazı beklediğimden çok uzun oldu. 4. sorunun cevabı zaten başlı başına upuzun 🙂 Umarım sıkılmadan okumuşsunuzdur. Aklınıza takılan yerleri sormayı unutmayın veya bir yanlışım varsa beni düzeltirseniz sevinirim 🙂