Libc Heap
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Heap Basiese
Die heap is basies die plek waar ’n program data kan stoor wanneer dit geheue versoek deur funksies soos malloc, calloc … aan te roep. Verder, wanneer hierdie geheue nie meer nodig is nie, word dit vrygemaak deur die funksie free aan te roep.
Soos getoon, is dit net na waar die binary in geheue gelaai word (kyk die [heap] afdeling):
.png)
Basic Chunk Allocation
Wanneer data versoek word om in die heap gestoor te word, word ’n gedeelte van die heap vir dit toegeken. Hierdie ruimte sal aan ’n bin behoort en slegs die aangevraagde data + die ruimte van die bin headers + minimum bin size offset sal vir die chunk gereserveer word. Die doel is om soveel moontlik minimale geheue te reserveer sonder om dit moeilik te maak om te vind waar elke chunk is. Hiervoor word die metadata chunk-inligting gebruik om te weet waar elke gebruikte/vrye chunk is.
Daar is verskeie maniere om die ruimte te reserveer, hoofsaaklik afhangend van die gebruikte bin, maar ’n algemene metodologie is die volgende:
- Die program begin deur ’n sekere hoeveelheid geheue te versoek.
- As daar in die lys van chunks ’n een beskikbaar is wat groot genoeg is om die versoek te bevredig, sal dit gebruik word
- Dit kan selfs beteken dat ’n deel van die beskikbare chunk vir hierdie versoek gebruik sal word en die res by die chunks-lys gevoeg sal word
- As daar geen beskikbare chunk in die lys is nie maar daar steeds ruimte in die toegekende heap-geheue is, skep die heap manager ’n nuwe chunk
- As daar nie genoeg heap-ruimte is om die nuwe chunk toe te ken nie, vra die heap manager die kernel om die geheue wat aan die heap toegeken is uit te brei en gebruik dan hierdie geheue om die nuwe chunk te genereer
- As alles misluk, keer
mallocterug met null.
Let wel dat as die aangevraagde geheue ’n drempel oorskry, sal mmap gebruik word om die aangevraagde geheue te map.
Arenas
In multithreaded toepassings moet die heap manager race conditions voorkom wat tot crashes kan lei. Aanvanklik is dit gedoen met ’n globale mutex om te verseker dat slegs een thread op ’n slag toegang tot die heap kon kry, maar dit het prestasieprobleme veroorsaak as gevolg van die mutex-induse bottleneck.
Om dit te adresseer het die ptmalloc2 heap allocator “arenas” bekendgestel, waar elke arena as ’n afsonderlike heap optree met sy eie datastrukture en mutex, wat toelaat dat verskeie threads heap-operasies uitvoer sonder om mekaar te steur, solank hulle verskillende arenas gebruik.
Die standaard “main” arena hanteer heap-operasies vir single-threaded toepassings. Wanneer nuwe threads bygevoeg word, ken die heap manager hulle sekondêre arenas toe om kontensie te verminder. Dit probeer eers om elke nuwe thread aan ’n ongebruikte arena te koppel, en skep nuwe eenhede indien nodig, tot ’n limiet van 2 keer die aantal CPU-kerne vir 32-bit stelsels en 8 keer vir 64-bit stelsels. Sodra die limiet bereik is, moet threads arenas deel, wat tot potensiële kontensie kan lei.
Anders as die main arena, wat uitbrei deur die brk system call te gebruik, skep sekondêre arenas “subheaps” deur mmap en mprotect te gebruik om die heap-gedrag na te boots, wat buigbaarheid in geheuebestuur vir multithreaded operasies moontlik maak.
Subheaps
Subheaps dien as geheue-reserwes vir sekondêre arenas in multithreaded toepassings, wat hulle toelaat om te groei en hul eie heap-streke afsonderlik van die hoofheap te bestuur. So werk subheaps en hoe hulle verskil van die aanvanklike heap:
- Initial Heap vs. Subheaps:
- Die aanvanklike heap is geleë direk na die program se binary in geheue, en dit brei uit deur die
sbrksystem call te gebruik. - Subheaps, gebruik deur sekondêre arenas, word geskep deur
mmap, ’n system call wat ’n gespesifiseerde geheuegebied map.
- Memory Reservation with
mmap:
- Wanneer die heap manager ’n subheap skep, reserveer dit ’n groot blok geheue deur
mmap. Hierdie reservasie ken nie dadelik fisiese geheue toe nie; dit dui bloot ’n streek aan wat ander stelsels of toedelinge nie behoort te gebruik nie. - By verstek is die gereserveerde grootte vir ’n subheap 1 MB vir 32-bit prosesse en 64 MB vir 64-bit prosesse.
- Gradual Expansion with
mprotect:
- Die gereserveerde geheuegebied word aanvanklik gemerk as
PROT_NONE, wat aandui dat die kernel nie fisiese geheue aan hierdie area hoef toe te ken nie. - Om die subheap te “groei”, gebruik die heap manager
mprotectom bladsy-permissies vanPROT_NONEnaPROT_READ | PROT_WRITEte verander, wat die kernel dwing om fisiese geheue aan die voorheen gereserveerde adresse toe te ken. Hierdie stap-vir-stap benadering laat die subheap uitbrei soos nodig. - Sodra die hele subheap uitgeput is, skep die heap manager ’n nuwe subheap om voort te gaan met toekenning.
heap_info
Hierdie struct stoor relevante inligting oor die heap. Boonop mag heap-geheue nie aaneenlopend wees na verdere toekenninge nie; hierdie struct sal daardie inligting ook stoor.
// 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.
Dit is belangrik om te let dat die main arena malloc_state struktuur ’n global variable in the libc is (daarom geleë in die libc memory space).
In die geval van malloc_state strukture van die heaps van threads, is hulle geleë inside own thread “heap”.
There some interesting things to note from this structure (see C code below):
-
__libc_lock_define (, mutex);Is daar om te verseker dat hierdie struktuur van die heap deur 1 thread op ’n slag toeganklik is -
Vlae:
-
#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)
- Die `mchunkptr bins[NBINS * 2 - 2];` bevat **pointers** na die **first and last chunks** van die small, large en unsorted **bins** (die -2 is omdat die index 0 nie gebruik word nie)
- Daarom sal die **first chunk** van hierdie bins 'n **backwards pointer to this structure** hê en die **last chunk** van hierdie bins sal 'n **forward pointer** na hierdie struktuur hê. 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**.
- Die structs `struct malloc_state *next;` en `struct malloc_state *next_free;` is gekoppelde lyste van arenas
- Die `top` chunk is die laaste "chunk", wat basies **all the heap reminding space** is. Sodra die top chunk "empty" is, is die heap volledig gebruik en moet dit meer ruimte request.
- Die `last reminder` chunk ontstaan in gevalle waar 'n exact size chunk nie beskikbaar is nie en daarom 'n groter chunk gesplit word; 'n pointer na die oorblywende deel word hier geplaas.
```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
Hierdie struktuur verteenwoordig ’n spesifieke geheue chunk. Die verskillende velde het verskillende betekenisse vir gealloceerde en nie-gealloceerde chunks.
// 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;
Soos vroeër opgemerk, het hierdie chunks ook ’n paar metadata, baie goed uitgebeeld in hierdie prent:
.png)
https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png
Die metadata is gewoonlik 0x08B wat die huidige chunk-grootte aandui, en gebruik die laaste 3 bis om te dui:
A: As dit 1 is, kom dit van ’n subheap; as dit 0 is, is dit in die main arenaM: As dit 1 is, is hierdie chunk deel van ’n spasie wat met mmap gealloceer is en nie deel van ’n heap nieP: As dit 1 is, is die vorige chunk in gebruik
Dan die ruimte vir die user data, en uiteindelik 0x08B om die vorige chunk-grootte aan te dui wanneer die chunk beskikbaar is (of om user data te stoor wanneer dit gealloceer is).
Verder, wanneer beskikbaar, word die user data ook gebruik om sekere data te bevat:
fd: Pointer na die volgende chunkbk: Pointer na die vorige chunkfd_nextsize: Pointer na die eerste chunk in die lys wat kleiner is as selfbk_nextsize: Pointer na die eerste chunk in die lys wat groter is as self
.png)
https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png
Tip
Let op hoe die koppeling van die lys op hierdie manier die behoefte aan ’n array waarin elke chunk geregistreer is, voorkom.
Chunk-aanwysers
Wanneer malloc gebruik word, word ’n pointer na die inhoud wat geskryf kan word teruggegee (net na die headers); wanneer egter chunks bestuur word, is ’n pointer na die begin van die headers (metadata) nodig.
Vir hierdie omskakelings word hierdie funksies gebruik:
// 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))
Uitlijning & minimale grootte
Die pointer na die chunk en 0x0f moet 0 wees.
// 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);
}
Let wel: wanneer die totale vereiste ruimte bereken word, word SIZE_SZ slegs een keer bygetel omdat die prev_size veld gebruik kan word om data te stoor; daarom is slegs die aanvanklike header nodig.
Get Chunk data and alter metadata
Hierdie funksies neem ’n pointer na ’n chunk as argument en is nuttig om metadata te check/set:
- Check chunk flags
// 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)
- Groottes en pointers na ander chunks
/*
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)))
- Inuse 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))
- Stel kop- en voetskrif (wanneer chunk nos in gebruik
/* 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))
- Kry die grootte van die werklike bruikbare data binne die chunk
#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;
}
Voorbeelde
Vinnige Heap Voorbeeld
Vinnige heap voorbeeld van https://guyinatuxedo.github.io/25-heap/index.html maar in arm64:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{
char *ptr;
ptr = malloc(0x10);
strcpy(ptr, "panda");
}
Stel ’n breakpoint aan die einde van die main-funksie en kom ons vind uit waar die inligting gestoor is:
.png)
Dit is moontlik om te sien dat die string panda gestoor is by 0xaaaaaaac12a0 (wat die adres was wat as reaksie deur malloc in x0 gegee is). As jy 0x10 bytes vroeër nakyk, kan jy sien dat die 0x0 aandui dat die vorige chunk nie gebruik is (lengte 0) en dat die lengte van hierdie chunk 0x21 is.
Die ekstra gereserveerde ruimte (0x21-0x10=0x11) kom van die bygevoegde headers (0x10) en 0x1 beteken nie dat daar 0x21B gereserveer is nie, maar dat die laaste 3 bits van die lengte van die huidige header sekere spesiale betekenisse het. Aangesien die lengte altyd 16-byte gealigneer is (op 64-bit masjiene), gaan hierdie bits in werklikheid nooit deur die lengtegetal gebruik word nie.
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
Multithreading-voorbeeld
Multithread
```c #includevoid* 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>
Deur die vorige voorbeeld te debug, is dit moontlik om te sien dat daar aan die begin net 1 arena is:
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
Dan, nadat die eerste thread aangeroep is — die een wat malloc aanroep — word 'n nuwe arena geskep:
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
en binne daarvan kan 'n paar chunks gevind word:
<figure><img src="../../images/image (2) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
## Bins & Memory Allocations/Frees
Kyk na wat die bins is en hoe hulle georganiseer is, en hoe memory toegewys en vrygestel word in:
<a class="content_ref" href="bins-and-memory-allocations.md"><span class="content_ref_label">Bins & Memory Allocations</span></a>
## Heap Functions Security Checks
Funksies wat by die heap betrokke is, sal sekere kontroles uitvoer voordat hulle hul aksies doen om te probeer seker maak dat die heap nie gekorrupteer is nie:
<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 use mmap()'d groups whose slots are fully `munmap()`'d when empty. Vir lang lineêre kopieë (~0x15555555 bytes), hou die span gemapped (vermy gapings wat deur vrygestelde groups geskep word) en plaas die victim allocation langs die source slot.
- **Cycling offset mitigation:** On slot reuse mallocng may advance the user-data start by `UNIT` (0x10) multiples when slack fits an extra 4-byte header. Dit skuif overwrite offsets (bv. LSB pointer hits) tensy jy reuse counts beheer of by strides sonder slack bly (bv. Lua `Table` objects by stride 0x50 toon offset 0). Inspekteer offsets met muslheap’s `mchunkinfo`:
```gdb
pwndbg> mchunkinfo 0x7ffff7a94e40
... stride: 0x140
... cycling offset : 0x1 (userdata --> 0x7ffff7a94e40)
- Prefer runtime-object corruption over allocator metadata: mallocng meng cookies/guarded out-of-band metadata, dus teiken hoërvlak-objekte. In Redis’s Lua 5.1 verwys
Table->arrayna ’n array vanTValuetagged values; die oorskrywing van die LSB van ’n pointer inTValue->value(bv. met die JSON terminator byte0x22) kan verwysings pivot sonder om malloc metadata aan te raak. - Debugging stripped/static Lua on Alpine: Bou ’n paslike Lua, lys simbole met
readelf -Ws, haal funksiesimbole uit metobjcopy --strip-symbolom struct lay-outs in GDB bloot te lê, en gebruik dan Lua-aware pretty-printers (GdbLuaExtension for Lua 5.1) plus muslheap om stride/reserved/cycling-offset waardes te kontroleer voordat jy die overflow aktiveer.
Case Studies
Bestudeer allocator-spesifieke primitives wat afgelei is van werklike bugs:
Virtualbox Slirp Nat Packet Heap Exploitation
Gnu Obstack Function Pointer Hijack
References
- https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/
- https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/
- Pumping Iron on the Musl Heap – Real World CVE-2022-24834 Exploitation on an Alpine mallocng Heap
- musl mallocng enframe (v1.2.4)
- muslheap GDB plugin
- GdbLuaExtension (Lua 5.1 support)
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.


