Merhaba arkadaşlar, bugün adını sıkça duymaya başladığımız AMSI (Antimalware Scan Interface) hakkında elimden geldiğince bir şeyler karalamaya çalışacağım. Konuya girmeden önce AMSI nedir ne işe yarar veya nasıl çalışır gibi sorulara cevap niteliğinde bir giriş yapalım
Microsoft, zararlı yazlımlara karşı son kullanıcının daha iyi korunabilmesi için AMSI ürünü/yazılımı geliştirdi. Varsayılan olarak Windows Defender, yürütülecek zararlı komutu önlemek için Windows Script Host teknolojisini kullanarak Powershell komut dosyalarını, VBA makrolarını veya Javascript gibi kod çalıştırılabilecek dosyaları taramak için AMSI API’si ile iletişime geçer ve içerisinde zararlı kod var ise bunu bloklar.
Anladık Peki o Zaman AMSI Tam Olarak Nasıl Çalışır?
Bir kullanıcı komut çalıştırdığında yada Powershell,CMD gibi süreçleri başlattığında AMSI.dll işlem bellek alanına (Process Memory Space) enjekte edilir. Dosya veya betik çalıştırılmadan önce “AmsiScanBuffer() ve AmsiScanString()” iki API, dosya içerisindeki kodları analiz etmeye başlar. Veri tabanında var olan bir zararlı imza tespit edilirse dosya çalıştırılmaz ve Defender (Bazı antivirüslerde bloklayabilir) tarafından engellendiğini belirten bir mesaj bizi karşılar. Bunun güzel yanlarından biri ise bu API’leri herkes kendi yazılımına da entegre edebilir. Process Hacker ile Powershell process’inin içeriğini kontrol ettiğimizde amsi.dll’in çalıştığını görebiliriz.
Aşağıdaki ekran görüntüsünde de AMSI adımlarını görebilirsiniz
Biraz daha detaylı baktığımızda ise aşağıdaki ekran görüntüsünde de gördüğümüz gibi AmsiScanBuffer kendisinden sonra AmsiScanString çağırdığını görebiliriz.
Peki Bunu Nasıl Bypass Edebiliriz?
Baktığımızda Powershell’de Invoke-Mimikatz, amsiInitFailed, AmsiUtils vb. komutları çalıştırmak istediğimizde hata ile karşılaşıyoruz. Bunları en basit hali ile atlatmanın yolu powershell versiyon 2 kullanmaktır. Microsoft Powershell 2.0’ı desteklemiyor fakat işletim sistemlerinde bu varsayılan olarak yüklü gelmektedir.
Bir sonraki atlatma yöntemlerinden biri de komutu parçalarına ayırmaktır. Nasıl yani derseniz aşağıdaki ekran görüntülerine bakabilirsiniz.
Aslında baktığımızda AMSI bazı sözcükleri algılıyor ve bunlara kızıyor. Bizde komutu parçalara ayırarak AMSI’den kaçırmış oluyoruz. Abilerimiz bir kaç tool yazarak bunları otomatize hale de getirmiş. Bir google yapmakta fayda var.
Bir diğer bypass yöntemi ise yama (patching) yapma yöntemidir. Amsi dll kitaplığı işlemin aynı sanal bellek alanına yüklendiğinden AmsiScanBuffer işlevine yama (pacthing) yapmaktır. Bu nedenle bu adres alanına hemen hemen tam konrole sahibiz diyebiliriz. Şimdi Frida ile powershell tarafından yapılan AMSI API çağrılarına bakalım
Burada Amsi.dll içerisinde “Amsi” ile başlayan tüm API çağrılarını dinliyoruz. Ama burada gördüğünüz gibi fonksiyona iletilen argümanları veya AMSI taramasının döndürdüğü sonuçları göremiyoruz. Bunun için Frida ile içli dışı olan arkadaşlar bilirler bir script yazıp veya düzenleyip ekrana bastırmamız gerekecek. Bunun için “C:\Users\kullaniciniz__handlers__\amsi.dll” dosyası içerisindeki AmsiScanBuffer.JS dosyasını aşağıdaki gibi editliyoruz.
Zararlı olmayan bir sözcük girildiğinde result “1”, zararlı bir sözcük girildiğinde ise result “32768” değerini döndürmektedir.
AmsiScanBuffer fonksiyonuna daha yakından bakalım.
Tarama sol kutudaki talimatlarla gerçekleşir. Sağdaki talimat ise iletilen argümanların gerçerli olmadığı durumda çağrılıyor. Buda “80070057h” değerine karşılık gelir ve sonrasında fonksiyon biter. Peki AmsiScanBuffer() fonksiyonunun başlangıcına yama (patching) yaparsak ne olur? Yani fonksiyonu her çağırdığımızda gerçek AMSI taraması yapmak yerine hata kodu döndürsek? Biraz daha kurcaladığımızda bahsetmiş olduğumuz talimata yani düzgün AMSI taramasının gerçekleştiği yere karşılık gelen baytın b85700780 olduğunu görüyoruz.
AmsiScanBuffer fonksiyonunun başlangıcını şu şekilde değiştirmemiz gerekiyor:
b857000780 mov eax, 80070057h
c3 ret
Yukarıdaki talimatlara karşlık gelen baytlar ise: b857000780c3
Little Endian mimarisi nedeni ile baytları tersine çeviriyoruz:
Sonrasında tekrar zararlı bir sözcüğü kullanarak komut çalıştırdığımızda Result değerinin “32768” değil de “1” döndüğünü görüyoruz ve AMSI’nin tetiklenmediğini görüyoruz.
Biz bunu WinDBG kullanarak yaptık fakat her zaman WinDBG’ye bağlı kalmak istemeyebiliriz. Bunun içinde aslında yardımımıza LoadLibrary, GetProcAddress ve VirtualProtect API’leri koşuyor. Nedir bunlar?
LoadLibrary: Amsi.dll kütüphanesini adres alanına yüklemek için kullanılır
GetProcAddress: AmsiScanBuffer() fonksiyonunun adresini almak için kullanırız
VirtualProtect: Adres alanını varsayılan olarak RX olarak yazılabilir yapmak için kullanırız.
Bu bahsettiğim fonksiyonları kullanarak WinDBG kullanmadan da bahsettiğim işlem gerçekleştirilebilir. Yazı çok uzadı zamanım olur ise bir sonraki yazımda bunu da yazmaya çalışırım. Ama ufak bir Google ile de bu yöntemi bulabilirsiniz
Bir sonraki yazımda görüşmek üzere