Heap de Libc
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Conceptos básicos del heap
El heap es básicamente el lugar donde un programa puede almacenar datos cuando solicita memoria llamando a funciones como malloc, calloc… Además, cuando esta memoria ya no se necesita se libera llamando a la función free.
Como se muestra, está justo después de donde el binario se carga en memoria (revisa la sección [heap]):
.png)
Asignación básica de chunks
Cuando se solicita almacenar datos en el heap, se reserva un espacio del heap para ello. Este espacio pertenecerá a un bin y solo se reservará para el chunk la memoria solicitada + el espacio de los encabezados del bin + el tamaño mínimo del bin. El objetivo es reservar la menor cantidad de memoria posible sin complicar la localización de cada chunk. Para ello se usa la información de metadatos del chunk para saber dónde está cada chunk usado/libre.
Hay diferentes maneras de reservar el espacio, principalmente dependiendo del bin usado, pero una metodología general es la siguiente:
- El programa empieza solicitando una cierta cantidad de memoria.
- Si en la lista de chunks hay alguno disponible lo bastante grande para satisfacer la petición, se usará.
- Esto puede incluso significar que parte del chunk disponible se use para esta petición y el resto se añada a la lista de chunks.
- Si no hay ningún chunk disponible en la lista pero todavía hay espacio en la memoria heap ya asignada, el gestor de heap crea un nuevo chunk.
- Si no hay suficiente espacio en el heap para asignar el nuevo chunk, el gestor de heap pide al kernel que expanda la memoria asignada al heap y luego usa esa memoria para generar el nuevo chunk.
- Si todo falla,
mallocdevuelve null.
Ten en cuenta que si la memoria solicitada supera un umbral, se usará mmap para mapear la memoria solicitada.
Arenas
En aplicaciones multithreaded, el gestor del heap debe prevenir race conditions que podrían provocar crashes. Inicialmente, esto se hacía usando un global mutex para asegurar que solo un hilo pudiera acceder al heap a la vez, pero esto causaba problemas de rendimiento debido al cuello de botella que genera el mutex.
Para resolverlo, el asignador de heap ptmalloc2 introdujo “arenas”, donde cada arena actúa como un heap separado con sus propias estructuras de datos y mutex, permitiendo que múltiples threads realicen operaciones del heap sin interferir entre sí, siempre que usen arenas distintas.
La arena “main” por defecto maneja las operaciones del heap para aplicaciones single-threaded. Cuando se añaden nuevos threads, el gestor del heap les asigna arenas secundarias para reducir la contención. Primero intenta adjuntar cada nuevo thread a una arena sin usar, creando nuevas si es necesario, hasta un límite de 2 veces el número de núcleos de CPU para sistemas de 32 bits y 8 veces para sistemas de 64 bits. Una vez alcanzado el límite, los threads deben compartir arenas, lo que puede provocar contención.
A diferencia de la arena main, que se expande usando la llamada al sistema brk, las arenas secundarias crean “subheaps” usando mmap y mprotect para simular el comportamiento del heap, permitiendo flexibilidad en la gestión de memoria para operaciones multithreaded.
Subheaps
Los subheaps sirven como reservas de memoria para arenas secundarias en aplicaciones multithreaded, permitiendo que crezcan y gestionen sus propias regiones de heap separadas del heap inicial. Así es como los subheaps difieren del heap inicial y cómo operan:
- Inicial heap vs. Subheaps:
- El heap inicial está ubicado directamente después del binario del programa en memoria, y se expande usando la llamada al sistema
sbrk. - Los subheaps, usados por arenas secundarias, se crean mediante
mmap, una llamada al sistema que mapea una región de memoria especificada.
- Reserva de memoria con
mmap:
- Cuando el gestor del heap crea un subheap, reserva un bloque grande de memoria mediante
mmap. Esta reserva no asigna memoria inmediatamente; simplemente designa una región que otros procesos del sistema o asignaciones no deberían usar. - Por defecto, el tamaño reservado para un subheap es de 1 MB para procesos de 32 bits y 64 MB para procesos de 64 bits.
- Expansión gradual con
mprotect:
- La región de memoria reservada se marca inicialmente como
PROT_NONE, indicando que el kernel no necesita asignar memoria física a este espacio todavía. - Para “crecer” el subheap, el gestor del heap usa
mprotectpara cambiar los permisos de página dePROT_NONEaPROT_READ | PROT_WRITE, lo que obliga al kernel a asignar memoria física a las direcciones previamente reservadas. Este enfoque paso a paso permite que el subheap se expanda según sea necesario. - Una vez que se agota todo el subheap, el gestor del heap crea un nuevo subheap para continuar la asignación.
heap_info
Esta struct almacena información relevante del heap. Además, la memoria del heap podría no ser continua después de más asignaciones; esta struct también guardará esa información.
// 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
Cada heap (main arena u otras arenas de threads) tiene una estructura malloc_state.
Es importante notar que la estructura main arena malloc_state es una variable global en libc (por lo tanto ubicada en el espacio de memoria de libc).
En el caso de las estructuras malloc_state de los heaps de los threads, están ubicadas dentro del propio “heap” del thread.
There some interesting things to note from this structure (see C code below):
-
__libc_lock_define (, mutex);Está ahí para asegurar que esta estructura del heap sea accedida por 1 thread a la vez -
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)
- El `mchunkptr bins[NBINS * 2 - 2];` contiene **punteros** a los **primeros y últimos chunks** de los **bins** small, large y unsorted (el -2 es porque el índice 0 no se usa)
- Por lo tanto, el **primer chunk** de estos bins tendrá un **puntero hacia atrás a esta estructura** y el **último chunk** de estos bins tendrá un **puntero hacia adelante** a esta estructura. Lo que básicamente significa que si puedes l**eak estas direcciones en el main arena** tendrás un puntero a la estructura en la **libc**.
- Las structs `struct malloc_state *next;` y `struct malloc_state *next_free;` son listas enlazadas de arenas
- El chunk `top` es el último "chunk", que es básicamente **todo el espacio restante del heap**. Una vez que el top chunk está "vacío", el heap está completamente usado y necesita solicitar más espacio.
- El chunk `last reminder` surge en casos donde no hay disponible un chunk de tamaño exacto y, por lo tanto, se divide un chunk más grande; un puntero a la parte restante se coloca aquí.
```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
Esta estructura representa un chunk de memoria en particular. Los distintos campos tienen diferentes significados para chunks asignados y no asignados.
// 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;
Como se comentó anteriormente, estos chunks también tienen ciertos metadatos, muy bien representados en esta imagen:
.png)
https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png
Los metadatos suelen ser 0x08B indicando el tamaño actual del chunk usando los últimos 3 bits para indicar:
A: If 1 it comes from a subheap, if 0 it’s in the main arenaM: If 1, this chunk is part of a space allocated with mmap and not part of a heapP: If 1, the previous chunk is in use
Luego, el espacio para los datos de usuario, y finalmente 0x08B para indicar el tamaño del chunk anterior cuando el chunk está disponible (o para almacenar datos de usuario cuando está asignado).
Además, cuando está disponible, los datos de usuario también se usan para contener algunos datos:
fd: Puntero al siguiente chunkbk: Puntero al chunk anteriorfd_nextsize: Puntero al primer chunk en la lista que es más pequeño que él mismobk_nextsize: Puntero al primer chunk en la lista que es más grande que él mismo
.png)
https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png
Tip
Observa cómo enlazar la lista de esta manera evita la necesidad de tener un array donde cada chunk esté registrado.
Punteros de chunks
Cuando se usa malloc se devuelve un puntero al contenido que puede escribirse (justo después de los headers), sin embargo, al gestionar chunks, se necesita un puntero al inicio de los headers (metadatos).
Para estas conversiones se usan estas funciones:
// 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))
Alineación y tamaño mínimo
El puntero al chunk y 0x0f deben ser 0.
// 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);
}
Tenga en cuenta que al calcular el espacio total necesario solo se suma SIZE_SZ una vez porque el campo prev_size puede usarse para almacenar datos; por lo tanto, solo se necesita la cabecera inicial.
Obtener datos del chunk y modificar metadatos
Estas funciones funcionan recibiendo un puntero a un chunk y son útiles para comprobar/establecer metadatos:
- Comprobar flags del chunk
// 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)
- Tamaños y punteros a otros 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)))
- 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))
- Establecer encabezado y pie de página (cuando se usan números de chunk)
/* 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))
- Obtener el tamaño de los datos realmente utilizables dentro del 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;
}
Ejemplos
Ejemplo rápido de Heap
Ejemplo rápido de Heap de https://guyinatuxedo.github.io/25-heap/index.html pero en arm64:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{
char *ptr;
ptr = malloc(0x10);
strcpy(ptr, "panda");
}
Coloca un breakpoint al final de la función main y averigüemos dónde se almacenó la información:
.png)
Se puede ver que la cadena panda se almacenó en 0xaaaaaaac12a0 (que fue la dirección devuelta por malloc dentro de x0). Si se inspeccionan 0x10 bytes antes, se puede ver que 0x0 indica que el chunk anterior no está usado (longitud 0) y que la longitud de este chunk es 0x21.
El espacio extra reservado (0x21-0x10=0x11) proviene de los added headers (0x10) y 0x1 no significa que se reservaron 0x21B, sino que los últimos 3 bits de la longitud del header actual tienen algunos significados especiales. Como la longitud siempre está alineada a 16 bytes (en máquinas de 64 bits), esos bits en la práctica nunca se usan en el valor de la longitud.
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
Ejemplo de multihilo
Multihilo
```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>
Depurando el ejemplo anterior es posible ver cómo al principio solo existe 1 arena:
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
Luego, después de llamar al primer thread, el que llama a malloc, se crea una nueva arena:
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
y dentro de ella se pueden encontrar algunos chunks:
<figure><img src="../../images/image (2) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
## Bins & Memory Allocations/Frees
Consulta cuáles son los bins, cómo están organizados y cómo se asigna y libera memoria en:
<a class="content_ref" href="bins-and-memory-allocations.md"><span class="content_ref_label">Bins & Memory Allocations</span></a>
## Heap Functions Security Checks
Las funciones relacionadas con el heap realizarán ciertas comprobaciones antes de ejecutar sus acciones para intentar asegurarse de que el heap no esté corrupto:
<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. For long linear copies (~0x15555555 bytes), keep the span mapped (avoid holes from released groups) and place the victim allocation adjacent to the 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. This shifts overwrite offsets (e.g., LSB pointer hits) unless you control reuse counts or stick to strides without slack (e.g., Lua `Table` objects at stride 0x50 show offset 0). Inspect offsets with muslheap’s `mchunkinfo`:
```gdb
pwndbg> mchunkinfo 0x7ffff7a94e40
... stride: 0x140
... cycling offset : 0x1 (userdata --> 0x7ffff7a94e40)
- Preferir la corrupción de objetos en tiempo de ejecución sobre metadatos del allocator: mallocng mezcla cookies/metadatos protegidos fuera de banda, así que apunta a objetos de mayor nivel. En la Lua 5.1 de Redis,
Table->arrayapunta a un array de valores etiquetadosTValue; sobrescribir el LSB de un puntero enTValue->value(p. ej., con el byte terminador JSON0x22) puede pivotar referencias sin tocar los metadatos de malloc. - Depuración de Lua stripped/static en Alpine: Compila una Lua equivalente, lista símbolos con
readelf -Ws, elimina símbolos de función víaobjcopy --strip-symbolpara exponer los layouts de struct en GDB, luego usa pretty-printers compatibles con Lua (GdbLuaExtension para Lua 5.1) más muslheap para comprobar los valores stride/reserved/cycling-offset antes de disparar el overflow.
Estudios de caso
Estudia primitivas específicas del allocator derivadas de bugs del mundo real:
Virtualbox Slirp Nat Packet Heap Exploitation
Gnu Obstack Function Pointer Hijack
Referencias
- 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
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


