2. Geleneksel Stajyer CTF Soru ve Cevapları - 2017

2. si düzenlenen CRYPTTECH Stajyer CTF'inde sorulan soruların çözümlerinden önce yarışma içeriğine dair birkaç istatistik ile başlangıcı yapalım.

CTF ‘17’de sorduğumuz soruların genel görüntüsü aşağıdaki gibidir.
Flag formatıyla beraber toplamda 16 soru sorulmuştur. 75 kayıtlı kullanıcının olduğu CTF’te aktif 42 katılım olmuştur. Sorulara göz attıktan sonra korkup kaçanların sayısını bilemiyoruz. 2000’e yakın cevabın girildiği sistemde; 213 doğru cevap, 1682 yanlış cevap girilmiştir. 
 Sorulara verilen cevapların dağılımı aşağıdaki gibi olmuştur;
CEVAPLAR

Soru 1:
Başlangıç olarak sonraki sorularda verilecek cevapların formatının nasıl olması gerektiğini belirten format üzerine kurulu bir sorumuz vardı;

Bu soruya inatla yanlış cevaplar veren ve cevap soruda yazmasına rağmen ipucu isteyip 7 puanını kaptıran arkadaşlara selam olsun.

Cevap 1: CT_{flag}

Soru 2:
Sorudan anlaşılması beklenen, verilen klasördeki 66 resim içerisinden aynı hash (heş) değerine sahip olan iki resmin bulunup, cevap olarak ortak hash değerinin formata uygun şekilde girilmesiydi.

Çözüm için md5sum komutuyla hashleri alınan dosyalar cut-sort-uniq gibi komutlar kullanılarak sıralanıp, ortak hash değeri bulunabilirdi;
Cevap 2: CT_{e06723d4961a0a3f950e7786f3766338}

Soru 3:
Bu soru yarışmanın en kolay sorularındandı. Verilen Apache access.log dosyasında boolean based bir sql injection saldırısının logları bulunmaktaydı.
MySQL'de;
ORD fonksiyonu bir karakterin 'decimal' değerini döndürür.
MID fonksiyonu karakter dizisinden parçalar seçmek için kullanılmaktadır.
IFNULL fonksiyonu iki değer alır. İlk değer 'null' değilse ilk değeri değilse ikinci değerini döndürür.
CAST fonksiyonu aldığı değeri herhangi bir tipe çevirir.
Bu açıklamalarla birlikte dönen içeriğin boyutuna göre değerlendirme yapılması gerekiyordu.

256.348.741.931 - - [20/May/2017:11:56:02 0000] "GET /c6403276da17922103da8bf28eae8556/?f=-4154 OR ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM flag_burada.table_flag ORDER BY flag LIMIT 0,1),1,1))>64 HTTP/1.1" 200 193

256.348.741.931 - - [20/May/2017:11:56:02 0000] "GET /c6403276da17922103da8bf28eae8556/?f=-4154 OR ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM flag_burada.table_flag ORDER BY flag LIMIT 0,1),1,1))>96 HTTP/1.1" 200 179

193 TRUE, 173 FALSE olarak düşünüldüğünde, sadece karşılaştırmaların sonucunu karakterlere çevirmek kalıyordu.

Cevap 3: CT_{bi_bitmedi_b00lean}

Soru 4:
İçerisinde Hmm isim AP’ye ait handshake bulunan wireless trafik dosyasının parolasının kırılması ve cevap formatlı şekilde girilmesi gerekmekteydi. Parolayı bulmak adına not verilmişti, bu nottan yola çıkarak crypttech veya sitesi üzerinden yola çıkarak önce password profiling, ardından password mangling yaparak cevabı içeren bir wordlist oluşturmaktı.
Yukarıda verildiği şekilde CeWL gibi bir araç ile site üzerinden olası anahtar kelimeler toplanabilirdi. Ardından JtR (John The Ripper) ile parola listesi genişletilebilirdi. Bunun sonucunda 124.581 satırlık bir parola dosyası elde edilmiştir.

Elde edilen parola dosyası aircrack veya benzeri bir araç ile soruda verilen dosya üzerinde denenebilirdi. Aircrack ilk çalıştırıldığında, trafik dosyası içeriğinde çok sayıda AP’ye ait trafik bulunduğu için parola kırma işleminin hangisi üzerinde gerçekleştirileceğini gösteren bir
Resimden de görüleceği üzere trafik içerisinde handshake bulunan sadece bir tane Access Point vardır. Hmm isimli AP seçilerek parola kırma işlemine devam edilir;
Cevap 4: CT_{2017crypttech}

Soru 5:
CTF’teki en kolay sorulardan biri olan Apeka için, Decompile edilen apk dosyası içerisinde kolaylıkla görülebilen giriş bilgilerinin cevap olarak girilmesi gerekmekteydi;
Cevap 5: CT_{flag@flag.com:hardCodedCredz}

Soru 6:
Yarışmanın en çok çözülen sorusuydu. Bakarak bile çözülebiliyordu. Resmin en ortasından 64x64 (piksel) bir kare seçilip dışarı doğru kenarlardan 32 piksel arttırılarak her seferinde sola 90 derece döndürülüyordu. Bu işlemleri geri alacak GIMP üzerinde çalışan basit bir python script aşağıda verilmiştir.
image = gimp.image_list()[0]
drw=pdb.gimp_image_get_active_layer(image)

size = 512
coords = 0

while size > 0:
 pdb.gimp_rect_select(image,coords,coords,size,size,2,0,0)
 pdb.gimp_rotate(drw,0,3.14159/2)
 flt_sel = pdb.gimp_image_floating_selection (image)
 pdb.gimp_floating_sel_anchor(flt_sel)
 size -= 64
 coords += 32

Cevap 6: CT_{bitcoin_ne_artti_be}

Soru 7:
Soru basit bir SQL injection sorusuydu. Verilen URL üzerinde, butona tıklama ile browser keşfi yapan bir uygulama çalışmakta idi.
Arka planda çalışan ajax.php’ye HTTP POST metoduyla gönderilen parametrelerden browser üzerinde SQL injection zafiyetiyle, ctf_database veritabanı içerisinde yer alan secret tablosunun içerisinde cevap olarak verilmesi gereken flag değeri yer almaktaydı.
Cevap 7: CT_{SqL1_c0k_s3xy}

Soru 8:
Bu soruda PEiD gibi bir araçla oluşturulan exe dosyasını hangi teknoloji ile yazıldığı kontrol edilebilirdi.
Uygulamanın .NET ile yazıldığı öğrenilince ILSpy gibi bir araçla decompile edilince, uygulamanın bağlanmaya çalıştığı adres görülebilirdi.
Ya da uygulamanın istek gönderdiği adres, trafiğin dinlenmesiyle veya hexeditor gibi bir araç yardımıyla bulunabilirdi.

Buradan gitmeye çalışılan adrese HTTP GET isteğiyle (browser ile) ulaşılmaya çalışıldığında 31337 yazan bir sayfa geldiği görülecekti. Daha sonra bu IP'deki 31337 porta netcat veya telnet ile bağlanıldığında flag değerine ulaşmak için denemeler yapılabilirdi. help veya ? kullanıldığında dönen cevapta komutlar bulunmaktaydı. Burada ls komutu kullanıldığında flag.txt olduğu görülecekti. read veya cat komutuyla flag.txt okunduğunda cevaba ulaşılabilirdi.
Cevap 8: CT_{z0rlamad1_s4nki}

Soru 9:
Verilen zip.db dosyasının, zip dosyası olduğu ihtimali düşünülüp, magic byte'lar kontrol edildiğinde, byte'ların sondan başa dizildiği görülebilir ve basit bir python scripti ile bu değerler tersine çevrilebilirdi.
with open("db.zip", "rb") as file:
    with open("zip.db", "wb") as zippo:
        data = []
        byte = file.read(1)
        while byte != "":
            data.append(byte)
            byte = file.read(1)
        for i in reversed(data):
            zippo.write(i)

Daha sonra zip dosyasından çıkan mdb dosyası bir araçla açılabilir veya linux’taki strings komutuyla flag değeri görülebilirdi.
Cevap 9: CT_{Bu_N3yd1_Simdi}

Soru 10:
Soruda verilen trafik analiz edildiğinde DNS protokolünde bir gariplik olduğu fark edilecektir. DNS protokolü filtrelenerek incelemeler yapıldığında crypttech.com domaini için olmayan subdomainlerin isim çözümlemesi yapılmaya çalışılmıştır.

Subdomainler yanyana yazıldığı zaman ortaya
SGVyZSBpcyB5b3VyIGZsYWc6IENUX3tIM3JfVHVSTHVfNG40TDF6X0czTDFSXzNMMW1kM259” şeklinde Base64 ile encode edilmiş bir karakter dizisi çıkacaktı. Sonuç decode edildiğinde cevap
ortaya çıkacaktı.

Cevap 10: CT_{H3r_TuRLu_4n4L1z_G3L1R_3L1md3n}

Soru 11:
Literatürde özellikle metin madenciliği çalışmalarında çok kullanılan bir metriktir. Kosinüs benzerliğinin formülü aşağıda verilmiştir. Yalnızca, İki vektör arasındaki kosinüs açısı bulunacaktı. Kosinüs benzerliği 1’e yakın olması iki vektörün benzer olduğunu gösterir.
Aşağıda yer alan koddaki en önemli metot iki vektör arasındaki kosinüs benzerliğini bulan cosineSimilarity isimli metottur. En fazla benzeyen 3 vektörün sahip olduğu sınıf cevap olacaktı. Gerisi test ve train dosyaların okunması ve aradaki vektörlerin arasındaki kosinüs benzerliklerinin bulunması ve sıralanmasından ibarettir.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;

public class CosineSimilarityClassifier {
 //İki vektör arasındaki kosinus benzerliğini bulur.(0-1 arasında sonuç verir)
 public static double cosineSimilarity(double[] vectorA, double[] vectorB) {
  double dotProduct = 0.0;
  double normA = 0.0;double normB = 0.0;
  for (int i = 0; i < vectorA.length; i++) {
   dotProduct += vectorA[i] * vectorB[i];
   normA += Math.pow(vectorA[i], 2);
   normB += Math.pow(vectorB[i], 2); }   
  return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
 }
 static class Similarities implements Comparable< Similarities>{
  double similarity;
  String class_name;
  @Override
  public int compareTo(Similarities sim) {//Benzerlik oranlarını büyükten küçüğe sıralamak için
   return Double.compare(sim.similarity,this.similarity);}
 }
 public static void main(String[] args) throws Exception {
  File fileTrain = new File("train.txt");
  File fileTest = new File("test.txt");
  BufferedReader brTest= new BufferedReader(new FileReader(fileTest));
  String readLine = "";
  double[] test_instance = new double[13];
  double[] train_instance = new double[13];
  while ((readLine = brTest.readLine()) != null) { // Test dosyası okunuyor.
   String []parts=readLine.split(",");
   for (int i = 0; i < 13; i++) 
    test_instance[i] = Double.parseDouble(parts[i]);
   BufferedReader brTrain= new BufferedReader(new FileReader(fileTrain));
   ArrayList<Similarities>listSimilarity=new ArrayList<Similarities>();
   while ((readLine = brTrain.readLine()) != null) {//Train dosyası okunuyor.
    Similarities sim=new Similarities();
    String []partsTrain=readLine.split(",");
    for (int i = 0; i < partsTrain.length-1; i++) 
     train_instance[i] = Double.parseDouble(partsTrain[i]);
    sim.similarity=cosineSimilarity(train_instance,test_instance);
    sim.class_name=partsTrain[partsTrain.length-1];
    listSimilarity.add(sim);
   }
   Collections.sort(listSimilarity);
   DecimalFormat df = new DecimalFormat("####0.000");
   for(int i=0; i<3;++i){  // 3 tane benzeyen sınıf
    System.out.print(listSimilarity.get(i).class_name+" "+(df.format(listSimilarity.get(i).similarity)+" "));
   }
   System.out.println();}}}

Yukarıdaki java kodu çalıştırıldığında aşağıdaki çıktı alınır.
Cevap 11: CT_{0,0,1,1,4,4,2,2,3,3,0,0,1,4,4,4,0,4,0,0,2,4,2,3,0}

Soru 12:
Soruda içerisinde makro barındıran bir Word dosyası bulunmaktaydı. Acemi bir heçkırın hazırlamış olduğu makro bazen çalışıp, bazen çalışmamakta idi. Bu yüzden makroyu bizzat çalıştırıp oluşan trafiği izlemek gerekmekteydi.
Makro çalıştırıldıktan sonra görüldüğü üzere uygulama powershell üzerinden 128.199.62.84 IP adresinin 56788 port numarasıyla iletişim kurmaya çalışmaktadır. Bu adrese netcat yada telnet ile bağlandıktan sonra yazılan herhangi bir içeriğe sunucu flag değerini cevap olarak dönmektedir.
Cevap 12: CT_{m4kr0d4_4n4liz_3d3r1m}

Soru 13:
Öncelikle bu problem torba, sırt çantası ya da İngilizce adıyla knapsack problem diye anılır. Bir hırsız eve girmiştir. Elindeki çantasının belirli bir ağırlığı vardır. Amacı evden mümkün oldukça yükü az ama pahada ağır eşyaları alıp, kaçmaktır.
Soruda, çantasının ağırlık yerine, süre parametresinin olduğu varsayılmıştır.
Bu problemin optimal çözümü için dinamik programlama yaklaşımının kullanılması gerekir. Problemi çözmek adına her zaman optimal çözüm vermese bile greedy (aç gözlü) algoritmasından da faydalanabilirsiniz.

Dinamik Programlama ile Çözümü Kodu:
import java.util.ArrayList;
publicclass Knapsack {
 public static void main(String[] args) {

      int[] price={4637,2923,....,4159,4922};
       int[] weights ={461,441,....,151,171};
 intsackCapacity = 7200;
 boolean[][] keep = getItemsToPick(price, weights, sackCapacity);
 printSelectedItems(keep, sackCapacity, price, weights);
 }
 privatestaticboolean[][] getItemsToPick(int[] price, int[] weights, intsackCapacity) {
  intnItems = price.length;
  //dp[i][w] - the maximum value of sub problem with i items and with w sack capacity
  int[][] dpTable = newint[nItems + 1][sackCapacity + 1];
  boolean[][] keep = newboolean[nItems][sackCapacity + 1];
  //iterate through all of the items
  for (inti = 1; i<= nItems; i++) {
   //calculate sub problem for all weights
   for (intw = 1; w<= sackCapacity; w++) {
    if (weights[i - 1] >w) {
     dpTable[i][w] = dpTable[i - 1][w];// we can not take this weight as it exceeds sub problem with weight w and i items
    } else {
     intpYes = price[i - 1] + dpTable[i - 1][w - weights[i - 1]];//Price if we include item i
     //Price if we include item i
     intpNo = dpTable[i - 1][w];
     if (pYes>pNo) {
      //this item MAY go into sack
      keep[i - 1][w] = true;
      dpTable[i][w] = pYes;
     } else {
      dpTable[i][w] = pNo;
     }
    }
   }
  }
  returnkeep;
 }
 publicstaticvoid printSelectedItems(boolean[][] keep, intsackCapacity, int[] price, int[] weights) {
  System.out.println("Secilen dosyalar:");
  intK = sackCapacity;
  intn = price.length;
  intwsel = 0;
  intvsel = 0;
  ArrayList<Integer>listFileId=new ArrayList<Integer>();
  for (inti = n - 1; i>= 0; i--) { // need to go in the reverse order
   if (keep[i][K] == true) {
    System.out.println("FileId: "+(i+1)+ "\tDeger=" + price[i] + "\tSure=" + weights[i]);  // FileId i değerinin 1 fazlasıdır.
    listFileId.add(i+1);
    wsel += weights[i];
    vsel += price[i];
    K = K - weights[i];
   }
  }
  System.out.println("Secilen dosyaların toplam degeri: " + vsel + " ve sure: " + wsel);
  System.out.println("Maksimum sure: " + sackCapacity);
  System.out.print("Sonuc: " );
  System.out.print("CT_{");
  for(inti=listFileId.size()-1;i>0;--i){
   System.out.print(listFileId.get(i)+",");
  }
  System.out.print(listFileId.get(0)+"}");
 }
}

Cıktı:
Secilen dosyaların toplam degeri: 150174 ve sure: 7194
Maksimum sure: 7200
Sonuc:
CT_{6.13.15.20.30.37.39.42.43.44.46.47.49.58.61.67.90.93.104.106.107.131.138.141,163.165.183.193.198.202.204.208.212.215.223.231.233.238.255.256}


Greedy Algoritması ile Çözüm:

Yapılacak tek şey Değer/Süre toplam faydayı hesaplayıp. Bu fayda sütununa göre veriler sıralanır. Daha sonra faydası çok olan az olana doğru süreler toplanır. Total Süre 7200 oluncaya kadar devam edilir.
Burada 224 fileId 7200 aşılmıştır. Eğer biraz daha devam edilirse, (5-10 dosya devam edilirse) 44 fileId 7200 aşmıyor. Son olarak o dosya eklenir. Daha sonra fileId sıralanır. Excel yardımıyla birkaç dakika içerisinde çözülebilir.
Cevap 13: CT_{6.13.15.20.30.37.39.42.43.44.46.47.49.58.61.67.90.93.104.106.107.131.138.141.163.165.183.1
93.198.202.204.208.212.215.223.231.233.238.255.256}

Soru 14:
Bir memory analiz sorusu olan Beyin bedava’da memory analiz aracı olan volatility kullanılarak soru çözülebilirdi. Burada ilk olarak soruda verilen imaj hakkında bilgi alabilmek adına imageinfo parametresiyle bilgi alınır.

Verilen imajın Windows XP 32 bir cihazdan alındığı görülebilir. Sonraki aşamada imaj alınırken hangi process’lerin çalıştığının görüntülenebilmesi adına pslist komutu çalıştırılabilir. Buradan elde edilen bilgiler ile sonraki aşamada nelerin yapılacağına karar verilebilir.
İlk aşamada göze çarpan notepad process’i olabilir. Burada açık olan dosya üzerinde flag değerinin bulunma ihtimalinden dolayı buraya bakılabilir. Burada volatility’nin doğrudan dosyanın içeriğini gösteren plugin’i çalıştırılabilir.

Dosya içeriğinin trollemesinden sonra yola devam edip, acaba clipboard’da bilgi veya flag kalmış olabilir mi diye düşünebiliriz. Burada volatility’nin gerekli plugin’i çalıştırılabilir.
Buradan da sonuç çıkmayınca, çalışan process’ler içerisinde yer alan puttytel.exe’nin veya ipucu olarak verilen bilgiden yola çıkarak, memory imajı alan bilgisayarın bir yerler ile iletişimde olduğu şüphesi doğabilir. Bunun için volatility içerisinde yer alan connscan plugin’i kullanılabilir.
Çıktıda ilgi çeken bir IP ve port değeri vardır. Bu IP adresine verilen port üzerinden netcat yada telnet gibi bir araç ile bağlanmaya çalıştığımız zaman, önceki sorularda olduğu gibi bizi karşılayan bir servis çıkacaktır. Biraz kurcaladıktan sonra veya help gibi bir komut gönderildikten sonra nelerin yapılacağı kolayca anlaşılabilir. Önce ls komutu çalıştırılır, bulunan dosyanın içeriğini okumak için cat komutu kullanılabilir. 

Cevap 14: CT_{m3m0_4n4lys1s+c0nnsc4n}

Soru 15:
Bu soru puanına göre görece kolay bir soruydu. Verilen android uygulamasının trafiği dinlenebilir veya decompile edilebilirdi. Böylece uygulamanın hangi adrese istek attığı görülebilirdi.

Decompile edilen uygulamanın MainActivity.java dosyasında http://188.166.3.228/8a5da52ed126447d359e70c05721a8aa/login adresine POST isteği yaptığı görülecekti.
Bunun bir web servis olduğu düşünülüp sorunun ana dizine gidildiğinde API Documentation adında bir sayfa ile karşılaşılacaktı. Bu sayfada web servise hangi isteklerin yapılabileceği görülmekteydi. Kaynak kodlara bakıldığında da ipucu görülecekti.

file isteğinin kullanılmasının gerektiği buradan da anlaşılabilirdi. Soru adındaki LF1 da diğer ipucuydu.
http://188.166.3.228/8a5da52ed126447d359e70c05721a8aa/file/flag.txt isteği
gönderildiğinde cevap olarak ‘uzantının .txt olduğunu nerden çıkardın :(‘ cevabı dönecekti.
Sistemin php olduğu düşünülüp flag.php çekilmeye çalışılabilirdi. Fakat php dosyasını çektiğimizde yorumlandığı için bir çıktı görülmeyecekti. Bu yüzden php://filter kullanılabilirdi. / karakteri yerine %2f kullanılarak php:%2f%2ffilter%2fconvert.base64-encode%2fresource=flag.php isteği gönderildiğinde cevap olarak ‘PD9waHAgJGZsYWcgPSAic2Vrw7xyaXRpIiA/Pgo=’ base64 encode edilmiş karakter dizisi dönecekti. Bu karakter dizisi decode edildiğinde cevaba ulaşılacaktı.
<?php $flag = "seküriti" ?> 

Cevap 15: CT_{seküriti}

Soru 16:
Soru, A4 Kaynakları A.Ş. adlı firmanın internet sitesinden oluşuyor. Soruda 'Authentication' işlemi sırasında, gereksiz yere sistemi 'hashing' fonksiyonlarıyla boğmamak düşüncesiyle, bazen password ile username'i aynı anda sorgulamak yerine, ilk önce username sorgulaması yapılıp ancak olumlu sonuç alındıktan sonra password sorgulama ve geri kalan işlere geçilir.
Yalnız bu işlem bazı önemli bilgilerin 'sızmasına' neden olabilir.
  if( $row['user'] == $_POST["username"] )
  {
     //Username sorgulaması sonrası çalışacak blok
  }

Yukarıdaki temel username sorgulama kodundan da anlaşılacağı gibi, username değerinin doğru veya yanlış olması durumlarındaki toplamda programın çalışacağı sürede, username sorgulamasından sonra çalışacak bloğun yapacağı işe bağlı olarak (örneğin database işlemleri) gözle görülür
değişiklikler olabilir. Bu da, kötü niyetli bir insana, sistemdeki username'lerin tahmin edebilmesinin yolunu aralayabilir. Soruda verilen ipucu da kullanıcı adlarının tahminine yöneliktir.
Çoğu firma, mail adresleri için, çalışanlarının sadece isim ve soy isimleriyle bunların kombinasyonlarını kullanmaktadır. Giriş sayfasında yer alan bilgiler ile kolayca kullanıcı adı tahmin edilebilir.

Rastgele herhangi bir kullanıcı adı ve parola ile deneme yapıldığında isteğin cevaplanma süresi 62ms kadardır.


Aynı isteğin kullanıcı adının mertcanbagoglu olarak girildiği durumda isteğe gelen cevabın süresi ise 1422ms kadardır.
Beklenen kullanıcı adını bulduktan sonra sıra parola tahminine geldi. Burada internette en çok kullanılan https://github.com/danielmiessler/SecLists/blob/master/Passwords/500-worst-passwords.txt adresindeki parola listesi kullanılarak kullanıcının parolasına erişilebilir.

Bulunan kullanıcı adı ve parola olan mertcanbagoglu ile gandalf denendiğinde flag’in yer aldığı sayfaya otomatik yönlendirileceksiniz.
Cevap 16: CT_{Bug'li_Bagoglu}


Yorumlar

  1. Kazananlar ne zaman açıklanacak :))

    YanıtlaSil
  2. Kopya çeken arkadaşları belirleyip, sıralamadan sildikten sonra açıklanır

    YanıtlaSil

Yorum Gönder

Bu blogdaki popüler yayınlar

1. Geleneksel Stajyer CTF Soru ve Cevapları

B*-Tree (BTree, BPlusTree) Veri Yapısı ile Veri İndeksleme