Libc Heap

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

Heap Temelleri

Heap, temelde bir programın malloc, calloc gibi fonksiyonları çağırarak veri talep ettiğinde veriyi depolayabileceği yerdir. Ayrıca, bu bellek artık gerekmediğinde free fonksiyonu çağrılarak serbest bırakılır.

Görüldüğü gibi, ikili dosyanın belleğe yüklendiği yerin hemen sonrasındadır (bkz. [heap] bölümü):

Temel Chunk Tahsisi

Bir veri heap’te saklanmak üzere talep edildiğinde, heap’ten bir alan ayrılır. Bu alan bir bin’e ait olur ve chunk için yalnızca istenen veri + bin başlıklarının kapladığı alan + minimum bin boyutu offset’i ayrılır. Amaç, her chunk’ın yerini bulmayı zorlaştırmadan mümkün olduğunca az bellek ayırmaktır. Bunun için hangi chunk’ın kullanıldığını/boşta olduğunu belirlemek üzere metadata chunk bilgisi kullanılır.

Alanı ayırmanın kullanılan bin’e bağlı olarak farklı yolları vardır, ama genel yöntem şöyledir:

  • Program belirli miktarda bellek talep ederek başlar.
  • Chunk listesinde isteği karşılayacak kadar büyük bir uygun chunk varsa, o kullanılır.
  • Bu, mevcut chunk’ın bir kısmının bu istek için kullanılması ve kalan kısmın chunk listesine eklenmesi anlamına gelebilir.
  • Listede uygun chunk yok ama ayrılmış heap belleğinde hâlâ boş alan varsa, heap yöneticisi yeni bir chunk oluşturur.
  • Yeni chunk’ı ayırmak için yeterli heap alanı yoksa, heap yöneticisi kernel’den heap’e ayrılan belleği genişletmesini ister ve sonra bu belleği yeni chunk’ı oluşturmak için kullanır.
  • Her şey başarısız olursa, malloc null döner.

Talep edilen bellek bir eşik değerini aşarsa, istenen belleği haritalamak için mmap kullanılacağını unutmayın.

Arenas

Çok iş parçacıklı uygulamalarda, heap yöneticisi çökmelere yol açabilecek yarış koşullarını önlemelidir. Başlangıçta bu, aynı anda sadece bir iş parçacığının heap’e erişebilmesini sağlamak için bir global mutex kullanılarak yapılıyordu, ancak bu mutex’in neden olduğu darboğaz yüzünden performans sorunlarına yol açtı.

Bunu çözmek için ptmalloc2 heap allocator “arenas” kavramını getirdi; burada her arena, kendi veri yapıları ve mutex’i ile ayrı bir heap gibi davranır, böylece farklı arenas’ları kullandıkları sürece birden fazla iş parçacığı birbirini etkilemeden heap işlemleri yapabilir.

Varsayılan “main” arena tek iş parçacıklı uygulamalar için heap işlemlerini yönetir. Yeni iş parçacıkları eklendiğinde, heap yöneticisi rekabeti azaltmak için onlara secondary arenas atar. Her yeni iş parçacığını önce kullanılmayan bir arenaya bağlamaya çalışır; gerekirse yeni arena’lar oluşturur, bu 32-bit sistemlerde CPU çekirdek sayısının 2 katına, 64-bit sistemlerde ise 8 katına kadar sınırlandırılmıştır. Bu sınır aşıldığında, iş parçacıkları arena’ları paylaşmak zorunda kalır, bu da potansiyel rekabete yol açar.

Main arena’nin brk sistem çağrısı ile genişlemesinin aksine, secondary arenas mmap ve mprotect kullanarak heap davranışını taklit eden “subheaps” oluşturur; bu, çok iş parçacıklı işlemler için bellek yönetiminde esneklik sağlar.

Subheaps

Subheaps, çok iş parçacıklı uygulamalarda secondary arenas için bellek rezervleri olarak hizmet eder; böylece ana heap’ten ayrı olarak büyüyebilir ve kendi heap bölgelerini yönetebilirler. Subheaps’in başlangıç heap’inden nasıl farklılaştığına ve nasıl çalıştığına dair şu bilgiler:

  1. Initial Heap vs. Subheaps:
  • İlk heap, programın ikili dosyasının hemen sonrasında bellekte bulunur ve sbrk sistem çağrısı ile genişler.
  • Secondary arenas tarafından kullanılan subheaps ise, belirtilen bir bellek bölgesini haritalayan mmap sistem çağrısı ile oluşturulur.
  1. Memory Reservation with mmap:
  • Heap yöneticisi bir subheap oluşturduğunda, mmap ile büyük bir bellek bloğu rezerve eder. Bu rezervasyon hemen bellek ayırmaz; sadece diğer sistem süreçleri veya tahsisatların kullanmaması gereken bir bölge belirler.
  • Varsayılan olarak, bir subheap için ayrılan boyut 32-bit işlemler için 1 MB ve 64-bit işlemler için 64 MB’tır.
  1. Gradual Expansion with mprotect:
  • Rezerve edilmiş bellek bölgesi başlangıçta PROT_NONE olarak işaretlenir; bu, kernel’in henüz bu alana fiziksel bellek ayırması gerekmediğini gösterir.
  • Subheap’i “büyütmek” için, heap yöneticisi sayfa izinlerini PROT_NONE’dan PROT_READ | PROT_WRITE’a değiştirmek üzere mprotect kullanır; bu, kernel’in önceden rezerve edilmiş adreslere fiziksel bellek ayırmasını tetikler. Bu adım adım yaklaşım subheaps’in gerektiği kadar genişlemesine izin verir.
  • Subheap tamamen tükenince, heap yöneticisi tahsisata devam etmek için yeni bir subheap oluşturur.

heap_info

Bu struct heap ile ilgili bilgileri ayırır. Ayrıca, daha fazla tahsisattan sonra heap belleği sürekli olmayabilir; bu struct bu bilgiyi de depolayacaktır.

// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/arena.c#L837

typedef struct _heap_info
{
mstate ar_ptr; /* Arena for this heap. */
struct _heap_info *prev; /* Previous heap. */
size_t size;   /* Current size in bytes. */
size_t mprotect_size; /* Size in bytes that has been mprotected
PROT_READ|PROT_WRITE.  */
size_t pagesize; /* Page size used when allocating the arena.  */
/* Make sure the following data is properly aligned, particularly
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
MALLOC_ALIGNMENT. */
char pad[-3 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;

malloc_state

Each heap (main arena or other threads arenas) has a malloc_state structure.
Önemli bir nokta: main arena malloc_state yapısı libc içinde bir global variable’dır (dolayısıyla libc bellek alanında bulunur).
Thread’lerin heap’lerine ait malloc_state yapıları ise kendi thread’in “heap“i içinde yer alır.

There some interesting things to note from this structure (see C code below):

  • __libc_lock_define (, mutex); Is there to make sure this structure from the heap is accessed by 1 thread at a time

  • Flags:

#define NONCONTIGUOUS_BIT (2U)

#define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0) #define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0) #define set_noncontiguous(M) ((M)->flags |= NONCONTIGUOUS_BIT) #define set_contiguous(M) ((M)->flags &= ~NONCONTIGUOUS_BIT)


- The `mchunkptr bins[NBINS * 2 - 2];` contains **pointers** to the **first and last chunks** of the small, large and unsorted **bins** (the -2 is because the index 0 is not used)
- Therefore, the **first chunk** of these bins will have a **backwards pointer to this structure** and the **last chunk** of these bins will have a **forward pointer** to this structure. Which basically means that if you can l**eak these addresses in the main arena** you will have a pointer to the structure in the **libc**.
- The structs `struct malloc_state *next;` and `struct malloc_state *next_free;` are linked lists os arenas
- The `top` chunk is the last "chunk", which is basically **all the heap reminding space**. Once the top chunk is "empty", the heap is completely used and it needs to request more space.
- The `last reminder` chunk comes from cases where an exact size chunk is not available and therefore a bigger chunk is splitter, a pointer remaining part is placed here.
```c
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1812

struct malloc_state
{
/* Serialize access.  */
__libc_lock_define (, mutex);

/* Flags (formerly in max_fast).  */
int flags;

/* Set if the fastbin chunks contain recently inserted free blocks.  */
/* Note this is a bool but not all targets support atomics on booleans.  */
int have_fastchunks;

/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];

/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;

/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;

/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];

/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];

/* Linked list */
struct malloc_state *next;

/* Linked list for free arenas.  Access to this field is serialized
by free_list_lock in arena.c.  */
struct malloc_state *next_free;

/* Number of threads attached to this arena.  0 if the arena is on
the free list.  Access to this field is serialized by
free_list_lock in arena.c.  */
INTERNAL_SIZE_T attached_threads;

/* Memory allocated from the system in this arena.  */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};

malloc_chunk

Bu yapı, belirli bir bellek chunk’ını temsil eder. Çeşitli alanların, allocated ve unallocated chunk’lar için farklı anlamları vardır.

// https://github.com/bminor/glibc/blob/master/malloc/malloc.c
struct malloc_chunk {
INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk, if it is free. */
INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
struct malloc_chunk* fd;                /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size.  */
struct malloc_chunk* fd_nextsize; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk_nextsize;
};

typedef struct malloc_chunk* mchunkptr;

Daha önce belirtildiği gibi, bu chunk’ların ayrıca bazı metadata’ları da vardır; bu durum bu görselde çok iyi gösterilmiştir:

https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png

Metadata genellikle 0x08B’dir ve son 3 biti kullanarak mevcut chunk boyutunu belirtir:

  • A: Eğer 1 ise subheap’ten gelmiştir, 0 ise main arena’dadır
  • M: Eğer 1 ise bu chunk mmap ile ayrılmış bir alana aittir ve bir heap’in parçası değildir
  • P: Eğer 1 ise, önceki chunk kullanımdadır

Ardından kullanıcı verisi için alan ve son olarak, chunk kullanılabilir olduğunda önceki chunk boyutunu belirtmek (veya allocate edildiğinde kullanıcı verisini depolamak) için 0x08B bulunur.

Ayrıca, kullanılabilir olduğunda, kullanıcı verisi bazı ek verileri içermek için kullanılır:

  • fd: Bir sonraki chunk’a işaretçi
  • bk: Önceki chunk’a işaretçi
  • fd_nextsize: Listedeki kendisinden daha küçük ilk chunk’a işaretçi
  • bk_nextsize: Listedeki kendisinden daha büyük ilk chunk’a işaretçi

https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png

Tip

Bu şekilde listeyi bağlamanın, her bir chunk’ın kaydedildiği bir diziye ihtiyaç duyulmasını nasıl önlediğine dikkat edin.

Chunk İşaretçileri

malloc kullanıldığında yazılabilecek içeriğe işaret eden bir pointer döndürülür (başlıklardan hemen sonra); ancak chunk’ları yönetirken, başlıkların (metadata) başlangıcına işaret eden bir pointer gerekir.
Bu dönüşümler için şu fonksiyonlar kullanılır:

// https://github.com/bminor/glibc/blob/master/malloc/malloc.c

/* Convert a chunk address to a user mem pointer without correcting the tag.  */
#define chunk2mem(p) ((void*)((char*)(p) + CHUNK_HDR_SZ))

/* Convert a user mem pointer to a chunk address and extract the right tag.  */
#define mem2chunk(mem) ((mchunkptr)tag_at (((char*)(mem) - CHUNK_HDR_SZ)))

/* The smallest possible chunk */
#define MIN_CHUNK_SIZE        (offsetof(struct malloc_chunk, fd_nextsize))

/* The smallest size we can malloc is an aligned minimal chunk */

#define MINSIZE  \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))

Hizalama & minimum boyut

chunk’a işaret eden pointer ve 0x0f 0 olmalıdır.

// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/sysdeps/generic/malloc-size.h#L61
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)

// https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/sysdeps/i386/malloc-alignment.h
#define MALLOC_ALIGNMENT 16


// https://github.com/bminor/glibc/blob/master/malloc/malloc.c
/* Check if m has acceptable alignment */
#define aligned_OK(m)  (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0)

#define misaligned_chunk(p) \
((uintptr_t)(MALLOC_ALIGNMENT == CHUNK_HDR_SZ ? (p) : chunk2mem (p)) \
& MALLOC_ALIGN_MASK)


/* pad request bytes into a usable size -- internal version */
/* Note: This must be a macro that evaluates to a compile time constant
if passed a literal constant.  */
#define request2size(req)                                         \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?             \
MINSIZE :                                                      \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/* Check if REQ overflows when padded and aligned and if the resulting
value is less than PTRDIFF_T.  Returns the requested size or
MINSIZE in case the value is less than MINSIZE, or 0 if any of the
previous checks fail.  */
static inline size_t
checked_request2size (size_t req) __nonnull (1)
{
if (__glibc_unlikely (req > PTRDIFF_MAX))
return 0;

/* When using tagged memory, we cannot share the end of the user
block with the header for the next chunk, so ensure that we
allocate blocks that are rounded up to the granule size.  Take
care not to overflow from close to MAX_SIZE_T to a small
number.  Ideally, this would be part of request2size(), but that
must be a macro that produces a compile time constant if passed
a constant literal.  */
if (__glibc_unlikely (mtag_enabled))
{
/* Ensure this is not evaluated if !mtag_enabled, see gcc PR 99551.  */
asm ("");

req = (req + (__MTAG_GRANULE_SIZE - 1)) &
~(size_t)(__MTAG_GRANULE_SIZE - 1);
}

return request2size (req);
}

Toplam gereken alanı hesaplarken SIZE_SZ sadece 1 kez eklenir çünkü prev_size alanı veri depolamak için kullanılabilir; dolayısıyla yalnızca ilk başlık gerekir.

Chunk verilerini al ve meta verilerini değiştir

Bu fonksiyonlar bir chunk’a işaretçi alarak çalışır ve meta verilerini kontrol etmek/ayarlamak için kullanışlıdır:

  • Chunk bayraklarını kontrol et
// From https://github.com/bminor/glibc/blob/master/malloc/malloc.c


/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1

/* extract inuse bit of previous chunk */
#define prev_inuse(p)       ((p)->mchunk_size & PREV_INUSE)


/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2

/* check for mmap()'ed chunk */
#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)


/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
from a non-main arena.  This is only set immediately before handing
the chunk to the user, if necessary.  */
#define NON_MAIN_ARENA 0x4

/* Check for chunk from main arena.  */
#define chunk_main_arena(p) (((p)->mchunk_size & NON_MAIN_ARENA) == 0)

/* Mark a chunk as not being on the main arena.  */
#define set_non_main_arena(p) ((p)->mchunk_size |= NON_MAIN_ARENA)
  • Boyutlar ve diğer chunk’lara işaretçiler
/*
Bits to mask off when extracting size

Note: IS_MMAPPED is intentionally not masked off from size field in
macros for which mmapped chunks should never be seen. This should
cause helpful core dumps to occur if it is tried by accident by
people extending or adapting this malloc.
*/
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)

/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p)         ((p)->mchunk_size)

/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))

/* Size of the chunk below P.  Only valid if !prev_inuse (P).  */
#define prev_size(p) ((p)->mchunk_prev_size)

/* Set the size of the chunk below P.  Only valid if !prev_inuse (P).  */
#define set_prev_size(p, sz) ((p)->mchunk_prev_size = (sz))

/* Ptr to previous physical malloc_chunk.  Only valid if !prev_inuse (P).  */
#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p)))

/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s)  ((mchunkptr) (((char *) (p)) + (s)))
  • Insue bit
/* extract p's inuse bit */
#define inuse(p)							      \
((((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size) & PREV_INUSE)

/* set/clear chunk as being inuse without otherwise disturbing */
#define set_inuse(p)							      \
((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size |= PREV_INUSE

#define clear_inuse(p)							      \
((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size &= ~(PREV_INUSE)


/* check/set/clear inuse bits in known places */
#define inuse_bit_at_offset(p, s)					      \
(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size & PREV_INUSE)

#define set_inuse_bit_at_offset(p, s)					      \
(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size |= PREV_INUSE)

#define clear_inuse_bit_at_offset(p, s)					      \
(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size &= ~(PREV_INUSE))
  • Başlık ve altbilgi ayarla (chunk numaraları kullanıldığında
/* Set size at head, without disturbing its use bit */
#define set_head_size(p, s)  ((p)->mchunk_size = (((p)->mchunk_size & SIZE_BITS) | (s)))

/* Set size/use field */
#define set_head(p, s)       ((p)->mchunk_size = (s))

/* Set size at footer (only when chunk is not in use) */
#define set_foot(p, s)       (((mchunkptr) ((char *) (p) + (s)))->mchunk_prev_size = (s))
  • Chunk içindeki gerçek kullanılabilir veri boyutunu al
#pragma GCC poison mchunk_size
#pragma GCC poison mchunk_prev_size

/* This is the size of the real usable data in the chunk.  Not valid for
dumped heap chunks.  */
#define memsize(p)                                                    \
(__MTAG_GRANULE_SIZE > SIZE_SZ && __glibc_unlikely (mtag_enabled) ? \
chunksize (p) - CHUNK_HDR_SZ :                                    \
chunksize (p) - CHUNK_HDR_SZ + (chunk_is_mmapped (p) ? 0 : SIZE_SZ))

/* If memory tagging is enabled the layout changes to accommodate the granule
size, this is wasteful for small allocations so not done by default.
Both the chunk header and user data has to be granule aligned.  */
_Static_assert (__MTAG_GRANULE_SIZE <= CHUNK_HDR_SZ,
"memory tagging is not supported with large granule.");

static __always_inline void *
tag_new_usable (void *ptr)
{
if (__glibc_unlikely (mtag_enabled) && ptr)
{
mchunkptr cp = mem2chunk(ptr);
ptr = __libc_mtag_tag_region (__libc_mtag_new_tag (ptr), memsize (cp));
}
return ptr;
}

Örnekler

Hızlı Heap Örneği

Hızlı heap örneği https://guyinatuxedo.github.io/25-heap/index.html ama arm64 için:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main(void)
{
char *ptr;
ptr = malloc(0x10);
strcpy(ptr, "panda");
}

main fonksiyonunun sonuna bir breakpoint koyun ve bilginin nerede saklandığını bulalım:

Görüldüğü gibi string panda 0xaaaaaaac12a0 adresinde saklanmıştı (bu, x0 içinde malloc tarafından döndürülen adresti). 0x10 byte geriye bakıldığında, 0x0’ın önceki chunk kullanılmamış (uzunluk 0) olduğunu ve bu chunk’ın uzunluğunun 0x21 olduğunu görmek mümkün.

Ayrılan ekstra boşluklar (0x21-0x10=0x11) eklenen header’lar (0x10) kaynaklıdır ve 0x1, bunun 0x21B olarak ayrıldığı anlamına gelmez; bunun yerine mevcut header’ın uzunluğunun son 3 biti bazı özel anlamlara sahiptir. Uzunluk her zaman 16-byte hizalıdır (64bits makinelerde), bu yüzden bu bitler aslında uzunluk değerinde kullanılmaz.

0x1:     Previous in Use     - Specifies that the chunk before it in memory is in use
0x2:     Is MMAPPED          - Specifies that the chunk was obtained with mmap()
0x4:     Non Main Arena      - Specifies that the chunk was obtained from outside of the main arena

Çoklu İş Parçacığı Örneği

Çoklu İş Parçacığı ```c #include #include #include #include #include

void* threadFuncMalloc(void* arg) { printf(“Hello from thread 1\n”); char* addr = (char*) malloc(1000); printf(“After malloc and before free in thread 1\n”); free(addr); printf(“After free in thread 1\n”); }

void* threadFuncNoMalloc(void* arg) { printf(“Hello from thread 2\n”); }

int main() { pthread_t t1; void* s; int ret; char* addr;

printf(“Before creating thread 1\n”); getchar(); ret = pthread_create(&t1, NULL, threadFuncMalloc, NULL); getchar();

printf(“Before creating thread 2\n”); ret = pthread_create(&t1, NULL, threadFuncNoMalloc, NULL);

printf(“Before exit\n”); getchar();

return 0; }

</details>

Önceki örneği debug ederken, başlangıçta yalnızca 1 arena olduğunu görebilirsiniz:

<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>

Sonra, ilk thread'i, malloc çağıranı çağırdıktan sonra yeni bir arena oluşturulur:

<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>

ve içinde bazı chunks bulunur:

<figure><img src="../../images/image (2) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>

## Bins & Bellek Ayırmaları/Boşaltmalar

Bins'ların ne olduğunu, nasıl organize edildiklerini ve belleğin nasıl ayrılıp serbest bırakıldığını şu dosyada inceleyin:


<a class="content_ref" href="bins-and-memory-allocations.md"><span class="content_ref_label">Bins & Memory Allocations</span></a>

## Heap Fonksiyonlarının Güvenlik Kontrolleri

Heap ile ilgili fonksiyonlar, heap'in bozulmadığından emin olmak için işlemlerini gerçekleştirmeden önce belirli kontroller yaparlar:


<a class="content_ref" href="heap-memory-functions/heap-functions-security-checks.md"><span class="content_ref_label">Heap Functions Security Checks</span></a>

## musl mallocng exploitation notes (Alpine)

- **Slab group/slot grooming for huge linear copies:** mallocng sizeclasses, slot'ları boş olduğunda tamamen `munmap()` edilen mmap()'lenmiş gruplar kullanır. Uzun doğrusal kopyalar (~0x15555555 byte) için span'i mapped tutun (release edilmiş gruplardan kaynaklanan boşluklardan kaçının) ve victim allocation'ı kaynak slot'un hemen yanına yerleştirin.
- **Cycling offset mitigation:** Slot yeniden kullanıldığında mallocng, slack fazladan bir 4-byte header sığdırdığında user-data başlangıcını `UNIT` (0x10) katları kadar ilerletebilir. Bu, overwrite offset'lerini kaydırır (ör. LSB pointer hits), eğer yeniden kullanım sayısını kontrol etmiyorsanız veya slack olmayan stride'lara bağlı kalmıyorsanız (ör. stride 0x50 olan Lua `Table` nesneleri offset 0 gösterir). Offset'leri muslheap’in `mchunkinfo` aracıyla inceleyin:
```gdb
pwndbg> mchunkinfo 0x7ffff7a94e40
... stride: 0x140
... cycling offset : 0x1 (userdata --> 0x7ffff7a94e40)
  • Prefer runtime-object corruption over allocator metadata: mallocng, cookies/korunan out-of-band meta verileri karıştırdığı için daha üst düzey objeleri hedefleyin. Redis’in Lua 5.1’inde, Table->array bir TValue etiketli değerler dizisini işaret eder; TValue->value içindeki bir işaretçinin LSB’sini (ör. JSON terminator byte 0x22) üzerine yazarak malloc metadata’ya dokunmadan referansları pivotlayabilirsiniz.
  • Debugging stripped/static Lua on Alpine: Uyumlu bir Lua derleyin, sembolleri readelf -Ws ile listeleyin, struct düzenlerini GDB’de görünür kılmak için fonksiyon sembollerini objcopy --strip-symbol ile çıkarın; ardından overflow’u tetiklemeden önce stride/reserved/cycling-offset değerlerini kontrol etmek için Lua-bilgili pretty-printer’lar (Lua 5.1 için GdbLuaExtension) ve muslheap’i kullanın.

Vaka İncelemeleri

Gerçek dünya hatalarından türetilmiş allocator-özgü primitive’leri inceleyin:

Virtualbox Slirp Nat Packet Heap Exploitation

Gnu Obstack Function Pointer Hijack

Referanslar

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