1. İlk İnceleme ve Statik Analiz
Dosyayı ilk incelediğimde klasik strings komutuyla okunabilir metinleri dump ettim.
strings crackme1
Şekil 1: Strings çıktısı
Çıktıyı incelediğimde karşıma kabak gibi FLAG{Tebrikler_Dogru_Flag} şeklinde bir string çıktı. Ayrıca başarılı veya başarısız durumlara ait herhangi bir çıktı mesajı tebrikler veya yanlış flag gibi göremedim. Bu kadar açık bir flag “Rabbit Hole” tuzağı olabiliceği konusunda şüphelendirdi
2. Dinamik Analiz ve Anti-Debug Tespitleri
Şüphemde haklı olup olmadığımı görmek için dosyayı GDB (GNU Debugger) üzerinde çalıştırdım. Aynı zamanda dinamik kütüphane çağrılarını izlemek için ltrace denemesi yaptım.
Şekil 2: Fake flag
GDB üzerinde programı çalıştırdığımda program benden flag istedi. strings çıktısında bulduğum FLAG{Tebrikler_Dogru_Flag} metnini girdiğimde program Tunelin ucu :) https://www.youtube.com/watch?v=3ksNL9uJssw mesajını verdi. Linke gittiğimde alaycı bir şekilde Kemal Sunal film repliği kullanılarak buranın gerçekten tuzak olduğu gösterilmiş.Ayrıca programı GDB içerisinde çalıştırırken araya giren SIGTRAP kesmesi içeride kasıtlı bir anti-debugging mekanizmasının bulunduğunu ve debugger tespit edildiği için sahte bir akışa yönlendirildiğimi kanıtladı. Asıl flag’i bulabilmek için Ghidra programına geçtim.
Şekil 3: Tünelin ucu kötü bi yere çıktı
3. Ghidra ve Algoritma Analizi
Dosyayı Ghidra’ya yükleyip decompile ettiğimde asıl koruma mekanizmalarını net bir şekilde gördüm. Programın entry kısmını bulduktan sonraki fonksiyona ilerlediğimde main kısımda olduğumu kod parçacığından anladım. Karşılama mesajı vs burada barınıyordu.
signal(5,FUN_001011bc);
FUN_00101159("=== Trackbook Crackme 1 ===\n");
FUN_00101159("Lutfen flag giriniz: ");
FUN_00101199(local_98,0x3f);
local_fc = 0;
do {
if (0x3f < local_fc) {
LAB_001015c8:
pcVar1 = (code *)swi(3);
(*pcVar1)();
return;
// Ghidra decompile işlemini burada kesti!
Koruma 1: Exception Based Control Flow
Kod içerisinde signal(5, ...) ile işletim sistemine SIGTRAP handler tanımlanmış ve hemen ardından swi(3) yani assembly karşılığı olan INT 3 kesmesi çağrılmıştı.
- Program normal çalışırken bu
INT 3işletim sistemi tarafından yakalanıp handler’a düşüyor ve arka planda bir değişkeni true yapıyor. - Dinamik analizde GDB kullandığım için bu exception GDB tarafından yutuldu handler çalışmadı ve kontrol değişkeni 0 kaldı. Bu da bana sahte flag bulduran asıl sebepti.
Decompiler’ı Düzeltme (NOP Patching)
Ghidra, INT 3 instruction’ını gördüğü an program akışının orada sonlandığını varsaydığı için asıl kontrol bloklarını decompile etmemişti. Kodun devamı için assembly üzerinden patch uyguladım.
Orijinal Assembly Akışı:
001015bd 7e 09 jle LAB_001015c8
001015bf 8b 45 fc mov eax,dword ptr [rbp + local_fc]
...
LAB_001015c8
001015c8 cc INT 3 ; Breakpoint
Buradaki CC opcode’unu 90 ile değiştirdim:
Patchlenmiş Assembly Akışı:
LAB_001015c8
001015c8 90 NOP ; Ghidra artık buraan sonra devam edicek
Bu işlemin ardından Ghidra fonksiyonu yeniden otomatik analiz etti ve kesintiden sonraki asıl doğrulama bloğu ortaya çıktı. Decompile çıktısı güncellendiğinde, uygulamanın durum değişkenine göre dallandığını gördüm. Doğru akıştaki gizli fonksiyon çağrısını takip ederek FUN_001012fe ve FUN_001011d0 fonksiyonlarına ulaştım.
if (0x3f < local_fc) {
code_r0x001015c9:
if (_DAT_0010402c == 1) {
iVar1 = FUN_001012fe(local_98);
if (iVar1 == 0) {
FUN_001011d0(&local_f8,0x13);
}
else {
FUN_001011d0(&local_58,0x42);
}
}
else {
iVar1 = strcmp(local_98,"FLAG{Tebrikler_Dogru_Flag}");
if (iVar1 == 0) {
FUN_001011d0(&local_d8,0x3d);
}
else {
FUN_001011d0(&local_f8,0x13);
}
}
4. String Obfuscation (XOR) ve Bayrağı Elde Etme
Debugger tespit edilmediği durumda çalışan gerçek kontrol fonksiyonuna (FUN_001012fe) girdiğimde gerçek bayrağın plain-text olarak tutulmadığını gördüm.
Koruma 2: In-Memory XOR Obfuscation
Fonksiyonun decompile edilmiş C kodu şu şekildeydi:
local_28[0] = 0x7c;
local_28[1] = 0x6b;
//diğer byte lar
local_28[0xf] = 0x4c;
local_30 = 0;
do {
//uzunluk ve güvenlik kontrolü
if ((int)(char)(*(byte *)(param_1 + local_30) ^ 0x31) != (uint)local_28[local_30]) {
uVar1 = 0;
goto LAB_0010139f; //hatalı flag
}
local_30 = local_30 + 1;
} while( true );
Sistem kullanıcının girdiği her bir karakteri 0x31 key değeri ile XOR işlemine sokuyor ve belleğe statik olarak gömülmüş olan şifreli hexadecimal dizi ile eşleştiriyordu. Ayrıca ekrana basılan mesajların da 0x55 key değeri ile anlık olarak deşifre edilip yazdırıldığını tespit ettim.
void FUN_001011d0(long param_1,int param_2)
{
long in_FS_OFFSET;
int local_9c;
byte local_98 [136];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
//local_98 buffer'ının 0 ile doldurulduğu kısımlar]
for (local_9c = 0; local_9c < param_2; local_9c = local_9c + 1) {
local_98[local_9c] = *(byte *)(param_1 + local_9c) ^ 0x55;
}
local_98[param_2] = 0;
FUN_00101159(local_98); //Şifresi çözülen metnin ekrana basılması
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return;
}
Gerçek flagi elde etmek için CyberChef aracını kullanarak elde ettiğim statik hex dizisine 0x31 anahtarı ile XOR işlemi uyguladım:
- Input Hex:
7c 6b 67 68 4a 78 5f 54 5a 6e 62 50 53 50 5f 4c - XOR Key:
31(Hex)
Şekil 3: Gerçek flag
Deşifre Edilen Gerçek Bayrak: MZVY{Inek_Saban}
5. Doğrulama
Elde ettiğim flagi doğrulamak amacıyla programı bu kez herhangi bir debugger kullanmadan normal terminal akışında çalıştırdım.
Şekil 5: Gerçek flagin doğrulaması
Yapımcının takdirini kabul ediyo ve yerime oturuyorum :)
Şekil 6: Aferin Şaban