Unsorted Bin Attack

Reading time: 11 minutes

tip

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) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Temel Bilgiler

For more information about what is an unsorted bin check this page:

Bins & Memory Allocations

Unsorted listeleri, unsorted_chunks (av) adresini chunk'ın bk adresine yazabilir. Bu yüzden, eğer bir saldırgan unsorted bin içindeki bir chunk'ta bk işaretçisinin adresini değiştirebilirse, bu adresi rastgele (arbitrary) bir adrese yazabiliyor; bu, Glibc adreslerini leak etmek veya bazı savunmaları aşmak için faydalı olabilir.

Yani temelde bu saldırı, bir arbitrar adrese büyük bir sayı yazmaya izin verir. Bu büyük sayı bir adres olup heap ya da Glibc adresi olabilir. Klasik hedeflerden biri, fast binlerin daha büyük boyutlara izin vermesi için global_max_fast idi (böylece bir unsorted bin attack'tan fast bin attack'a geçilebilirdi).

  • Modern not (glibc ≥ 2.39): global_max_fast 8 bitlik bir global haline geldi. Buraya unsorted-bin write ile körü körüne bir pointer yazmak artık çevresindeki libc verilerini bozacak ve fastbin limitini güvenilir şekilde yükseltmeyecektir. glibc 2.39+ karşısında başka hedefler veya başka primitives tercih edin. Aşağıdaki "Modern constraints" kısmına bakın ve istikrarlı bir primitive elde ettiğinizde bunu bir large bin attack veya bir fast bin attack ile kombinlemeyi düşünün.

tip

T> aking a look to the example provided in https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle and using 0x4000 and 0x5000 instead of 0x400 and 0x500 as chunk sizes (to avoid Tcache) it's possible to see that nowadays the error malloc(): unsorted double linked list corrupted is triggered.

Bu nedenle, günümüzde bu unsorted bin attack (diğer kontrollerin yanında) çift bağlı listeyi düzeltme yeteneği gerektirir: yani victim->bk->fd == victim veya victim->fd == av (arena) kontrolü atlatılmalı. Bu, yazmak istediğimiz adresteki fd pozisyonunun sahte chunk adresini içermesi ve sahte chunk'ın fd'sinin arena'ya işaret etmesi gerektiği anlamına gelir.

caution

Bu saldırının unsorted bin'i bozduğunu unutmayın (dolayısıyla small ve large da etkilenir). Bu yüzden artık sadece fast bin'den allocation kullanabiliriz (daha karmaşık bir program başka allocation'lar yapıp çökebilir) ve bunu tetiklemek için aynı boyutta allocation yapmamız gerekir, yoksa program çöker.

global_max_fast'ı overwrite etmek bu durumda yardımcı olabilir; fast bin'in exploit tamamlanana kadar diğer allocation'larla başa çıkacağını varsayabilirsiniz.

The code from guyinatuxedo explains it very well, although if you modify the mallocs to allocate memory big enough so don't end in a Tcache you can see that the previously mentioned error appears preventing this technique: malloc(): unsorted double linked list corrupted

How the write actually happens

  • The unsorted-bin write is triggered on free when the freed chunk is inserted at the head of the unsorted list.
  • During insertion, the allocator performs bck = unsorted_chunks(av); fwd = bck->fd; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
  • If you can set victim->bk to (mchunkptr)(TARGET - 0x10) before calling free(victim), the final statement will perform the write: *(TARGET) = victim.
  • Later, when the allocator processes the unsorted bin, integrity checks will verify (among other things) that bck->fd == victim and victim->fd == unsorted_chunks(av) before unlinking. Because the insertion already wrote victim into bck->fd (our TARGET), these checks can be satisfied if the write succeeded.

Yazının gerçekte nasıl gerçekleştiği

  • Unsorted-bin yazısı, freed chunk unsorted listenin başına eklendiğinde free üzerinde tetiklenir.
  • Ekleme sırasında allocator şu işlemleri yapar: bck = unsorted_chunks(av); fwd = bck->fd; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
  • Eğer free(victim) çağırmadan önce victim->bk'i (mchunkptr)(TARGET - 0x10) olarak ayarlayabiliyorsanız, son ifade şu yazmayı yapar: *(TARGET) = victim.
  • Daha sonra allocator unsorted bin'i işlerken unlink etmeden önce (diğer kontrollerin yanında) bck->fd == victim ve victim->fd == unsorted_chunks(av) doğrulamalarını yapar. Ekleme zaten bck->fd (yani bizim TARGET) içine victim yazdığı için, yazma başarılı olduysa bu kontroller sağlanabilir.

Modern kısıtlamalar (glibc ≥ 2.33)

Güncel glibc'de unsorted‑bin yazılarını güvenilir şekilde kullanmak için:

  • Tcache müdahalesi: tcache'a giren boyutlar için free'ler oraya yönlendirilir ve unsorted bin'e dokunmaz. Ya
    • istekleri MAX_TCACHE_SIZE'tan büyük yapın (64‑bit için varsayılan ≥ 0x410), ya da
    • ilgili tcache bin'ini (7 entry) doldurun ki ek free'ler global bin'lere ulaşsın, ya da
    • ortam kontrol edilebiliyorsa tcache'i devre dışı bırakın (ör. GLIBC_TUNABLES glibc.malloc.tcache_count=0).
  • Unsorted liste üzerinde bütünlük kontrolleri: unsorted bin'i inceleyen sonraki allocation yolunda glibc (basitleştirilmiş):
    • bck->fd == victim ve victim->fd == unsorted_chunks(av) kontrollerini yapar; aksi halde malloc(): unsorted double linked list corrupted ile abort eder.
    • Bu, hedeflediğiniz adresin iki yazmayı tolere edebilmesi gerektiği anlamına gelir: önce free anında *(TARGET) = victim; sonra chunk çıkarılırken *(TARGET) = unsorted_chunks(av) (allocator bck->fd'i tekrar bin başına yazar). Sadece büyük, sıfır olmayan bir değerin zorlanmasının işe yaradığı hedefleri seçin.
  • Modern exploitlerde tipik güvenilir hedefler:
    • "Büyük" değerleri bayrak/limit olarak kullanan uygulama veya global durum.
    • Dolaylı primitives (ör., sonraki bir [fast bin attack](Fast Bin Attack) için hazırlık ya da daha sonra bir write‑what‑where'a pivot yapmak).
    • Yeni glibc'de __malloc_hook/__free_hook'tan kaçının: 2.34'te kaldırıldılar. global_max_fast'ı ≥ 2.39'da kullanmaktan kaçının (bir sonraki notu okuyun).
  • global_max_fast hakkında (yeni glibc)
    • glibc 2.39+'ta global_max_fast 8‑bitlik bir globaldir. Klasik yöntem olan heap pointer'ı buraya yazarak fastbinleri büyütme artık temiz çalışmaz ve muhtemelen allocator durumunu bozacaktır. Diğer stratejileri tercih edin.

Minimal exploit tarifi (modern glibc)

Amaç: unsorted‑bin insertion primitive'ini kullanarak arbitrar bir adrese tek bir heap pointer yazısı elde etmek, programı çökertmeden.

  • Yerleşim / hazırlık
    • Tcache'i atlamak için yeterince büyük boyutlarda A, B, C allocate edin (ör. 0x5000). C top chunk ile birleşmeyi önler.
  • Bozma
    • A'dan B'nin chunk header'ına overflow ile B->bk = (mchunkptr)(TARGET - 0x10) ayarlayın.
  • Tetikleme
    • free(B). Ekleme zamanında allocator bck->fd = B çalıştırır; dolayısıyla *(TARGET) = B.
  • Devam
    • Eğer allocate etmeye devam etmeyi planlıyorsanız ve program unsorted bin'i kullanıyorsa, allocator daha sonra *(TARGET) = unsorted_chunks(av) yazacaktır. Her iki değer de tipik olarak büyük olup sadece "büyük" kontrolü yapan hedeflerde boyut/limit semantiğini değiştirmeye yetecektir.

Pseudocode skeleton:

c
// 64-bit glibc 2.35–2.38 style layout (tcache bypass via large sizes)
void *A = malloc(0x5000);
void *B = malloc(0x5000);
void *C = malloc(0x5000); // guard

// overflow from A into B’s metadata (prev_size/size/.../bk). You must control B->bk.
*(size_t *)((char*)B - 0x8) = (size_t)(TARGET - 0x10); // write fake bk

free(B); // triggers *(TARGET) = B (unsorted-bin insertion write)

note

• Eğer tcache'i boyut ile atlatamıyorsanız, bozuk chunk'ı free etmeden önce seçilen boyut için tcache bin'ini (7 free) doldurun ki free unsorted'a gitsin. • Eğer program unsorted-bin kontrolleri nedeniyle sonraki allocation'da hemen abort ediyorsa, victim->fd'nin hâlâ bin head'e eşit olduğunu ve ilk write'tan sonra TARGET'ınızın tam olarak victim pointer'ını tuttuğunu tekrar kontrol edin.

Unsorted Bin Infoleak Attack

This is actually a very basic concept. The chunks in the unsorted bin are going to have pointers. The first chunk in the unsorted bin will actually have the fd and the bk links pointing to a part of the main arena (Glibc).
Therefore, if you can put a chunk inside a unsorted bin and read it (use after free) or allocate it again without overwriting at least 1 of the pointers to then read it, you can have a Glibc info leak.

A similar attack used in this writeup, was to abuse a 4 chunks structure (A, B, C and D - D is only to prevent consolidation with top chunk) so a null byte overflow in B was used to make C indicate that B was unused. Also, in B the prev_size data was modified so the size instead of being the size of B was A+B.
Then C was deallocated, and consolidated with A+B (but B was still in used). A new chunk of size A was allocated and then the libc leaked addresses was written into B from where they were leaked.

Referanslar ve Diğer örnekler

  • https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#hitcon-training-lab14-magic-heap
  • Amaç, global bir değişkeni 4869'dan büyük bir değerle overwrite etmek; böylece flag alınabiliyor ve PIE etkin değil.
  • İstenilen boyutta heap overflow bulunan, rassal boyutlarda chunk üretilebilen bir senaryo mevcut.
  • Attack, overflow'u suistimal etmek için chunk0, overflow edilecek chunk1 ve top chunk'ın önceki chunklarla consolidate olmasını önlemek için chunk2 olmak üzere 3 chunk oluşturarak başlıyor.
  • Ardından chunk1 free ediliyor ve chunk0 overflow edilerek chunk1'in bk pointer'ının işaret ettiği değer şu şekilde ayarlanıyor: bk = magic - 0x10
  • Sonra chunk3, chunk1 ile aynı boyutta allocate ediliyor; bu unsorted bin attack'ı tetikleyecek ve global değişkenin değerini değiştirerek flag alınmasını mümkün kılacak.
  • https://guyinatuxedo.github.io/31-unsortedbin_attack/0ctf16_zerostorage/index.html
  • merge fonksiyonu, eğer aynı index iki kez geçirilirse söz konusu bölgeyi realloc edip sonra free ettiğinden dolayı vulnerable; freed bölgeye pointer döndürülüyor ve bu use after free için kullanılabiliyor.
  • Bu yüzden 2 chunk oluşturuluyor: kendisiyle merge edilecek olan chunk0 ve top chunk ile consolidate olmasını engellemek için chunk1. Ardından merge fonksiyonu chunk0 ile iki kez çağrılıyor ve bu use after free'e yol açıyor.
  • Sonra view fonksiyonu index 2 (use after free pointer'ının index'i) ile çağrılıyor ve bu libc address leak'ine neden oluyor.
  • Binary, sadece global_max_fast'tan büyük boyutlarda malloc yapılmasına izin veren korumalar içerdiği için fastbin kullanılmıyor; bunun yerine global_max_fast'ı overwrite etmek için unsorted bin attack kullanılacak.
  • Daha sonra, edit fonksiyonu index 2 (use after free pointer) ile çağrılarak bk pointer'ı p64(global_max_fast-0x10)'a overwrite ediliyor. Ardından yeni bir chunk yaratmak, önce kompromize edilmiş free adresini (0x20) kullanacak ve unsorted bin attack'ı tetikleyerek global_max_fast'ı çok büyük bir değere overwrite edecek; böylece artık fast bin'lerde chunk oluşturmak mümkün olacak.
  • Şimdi bir fast bin attack gerçekleştiriliyor:
  • Öncelikle __free_hook lokasyonunda fast chunk'larla (size 200) çalışılabildiği keşfediliyor:
  • gef➤  p &__free_hook
    

$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook> gef➤ x/60gx 0x7ff1e9e607a8 - 0x59 0x7ff1e9e6074f: 0x0000000000000000 0x0000000000000200 0x7ff1e9e6075f: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000

  • Eğer bu lokasyonda 0x200 boyutunda bir fast chunk elde edebilirsek, çalıştırılacak bir function pointer'ı overwrite etmek mümkün olacak.
  • Bunun için 0xfc boyutunda yeni bir chunk oluşturuluyor ve merge fonksiyonu bu pointer ile iki kez çağrılıyor; böylece fast bin'de 0xfc*2 = 0x1f8 boyutunda freed bir chunk pointer'ı elde ediliyor.
  • Ardından edit fonksiyonu bu chunk'ta çağrılarak fast bin'in fd adresi önceki __free_hook fonksiyonuna işaret edecek şekilde değiştiriliyor.
  • Sonra 0x1f8 boyutunda bir chunk oluşturulup fast bin'deki önceki işe yaramaz chunk alınıyor; başka bir 0x1f8 chunk daha oluşturularak fast bin'den __free_hook lokasyonunda bir chunk alınıp bu lokasyon system fonksiyonunun adresi ile overwrite ediliyor.
  • Ve nihayet /bin/sh\x00 içeren bir chunk delete fonksiyonu ile free edilerek __free_hook fonksiyonu tetikleniyor; __free_hook şimdi system'e işaret ediyor ve parametre olarak /bin/sh\x00 alıyor.
  • CTF https://guyinatuxedo.github.io/33-custom_misc_heap/csaw19_traveller/index.html
  • 1B overflow suistimal edilerek unsorted bin'de chunk'ların consolidate edilmesiyle libc infoleak elde edilip daha sonra malloc hook'u one gadget adresiyle overwrite etmek için fast bin attack yapılan başka bir örnek.
  • Robot Factory. BlackHat MEA CTF 2022
  • Sadece 0x100'den büyük boyutlarda chunk allocate edilebiliyor.
  • Unsorted Bin attack kullanılarak global_max_fast overwrite ediliyor (ASLR nedeniyle 1/16 çalışıyor; çünkü 12 bit değiştirmemiz gerekiyor, ama 16 bit değiştirmeliyiz).
  • Fast Bin attack ile global bir chunks array'i modify ediliyor. Bu, arbitrary read/write primitive veriyor ve GOT'u değiştirip bazı fonksiyonları system'e yönlendirmeyi sağlıyor.

Referanslar

  • Glibc malloc unsorted-bin integrity checks (example in 2.33 source): https://elixir.bootlin.com/glibc/glibc-2.33/source/malloc/malloc.c
  • global_max_fast and related definitions in modern glibc (2.39): https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c

tip

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) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin