Translated ['binary-exploitation/heap/bins-and-memory-allocations.md', '

This commit is contained in:
Translator 2024-06-11 17:32:22 +00:00
parent 60a041a2d5
commit dfedab7e3c
4 changed files with 575 additions and 156 deletions

View file

@ -1,4 +1,4 @@
# Bins & Asignaciones de Memoria
# Bins y Asignaciones de Memoria
<details>
@ -7,7 +7,7 @@
Otras formas de apoyar a HackTricks:
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Obtén [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.
@ -18,175 +18,582 @@ Otras formas de apoyar a HackTricks:
Para mejorar la eficiencia en cómo se almacenan los fragmentos, cada fragmento no está solo en una lista enlazada, sino que hay varios tipos. Estos son los bins y hay 5 tipos de bins: [62](https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=blob;f=malloc/malloc.c;h=6e766d11bc85b6480fa5c9f2a76559f8acf9deb5;hb=HEAD#l1407) bins pequeños, 63 bins grandes, 1 bin desordenado, 10 bins rápidos y 64 bins tcache por hilo.
La dirección inicial de cada bin desordenado, pequeño y grande está dentro del mismo array. El índice 0 no se utiliza, el 1 es el bin desordenado, los bins 2-64 son bins pequeños y los bins 65-127 son bins grandes.
La dirección inicial de cada bin desordenado, pequeño y grande está dentro del mismo array. El índice 0 no se usa, 1 es el bin desordenado, los bins 2-64 son bins pequeños y los bins 65-127 son bins grandes.
### Bins Tcache (Caché por Hilo)
Aunque los hilos intentan tener su propio heap (ver [Arenas](bins-and-memory-allocations.md#arenas) y [Subheaps](bins-and-memory-allocations.md#subheaps)), existe la posibilidad de que un proceso con muchos hilos (como un servidor web) **termine compartiendo el heap con otros hilos**. En este caso, la solución principal es el uso de **lockers**, lo que podría **ralentizar significativamente los hilos**.
Por lo tanto, un tcache es similar a un bin rápido por hilo en el sentido de que es una **lista enlazada simple** que no fusiona fragmentos. Cada hilo tiene **64 bins tcache enlazados individualmente**. Cada bin puede tener un máximo de [7 fragmentos del mismo tamaño](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l323) que van desde [24 a 1032B en sistemas de 64 bits y de 12 a 516B en sistemas de 32 bits](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l315).
Cuando un hilo libera un fragmento, si no es demasiado grande para ser asignado en el tcache y el bin tcache respectivo **no está lleno** (ya tiene 7 fragmentos), **se asignará allí**. Si no puede ir al tcache, deberá esperar a que el candado del heap esté disponible para poder realizar la operación de liberación global.
Cuando se asigna un **fragmento**, si hay un fragmento libre del tamaño necesario en el **tcache, se utilizará**, de lo contrario, deberá esperar a que el candado del heap esté disponible para encontrar uno en los bins globales o crear uno nuevo.\
También hay una optimización, en este caso, mientras tiene el candado del heap, el hilo **llenará su tcache con fragmentos del heap (7) del tamaño solicitado**, por lo que si necesita más, los encontrará en el tcache.
<details>
<summary>Agregar un ejemplo de fragmento tcache</summary>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunk;
chunk = malloc(24);
printf("Address of the chunk: %p\n", (void *)chunk);
gets(chunk);
free(chunk);
return 0;
}
```
Compílalo y depúralo con un punto de interrupción en el opcode ret de la función main. luego con gef puedes ver el bin tcache en uso:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=0, size=0x20, count=1] ← Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
```
</details>
#### Estructuras y Funciones de Tcache
En el siguiente código es posible ver los **bines máximos** y **trozos por índice**, la estructura **`tcache_entry`** creada para evitar liberaciones dobles y **`tcache_perthread_struct`**, una estructura que cada hilo utiliza para almacenar las direcciones de cada índice del bin.
<details>
<summary><code>tcache_entry</code> y <code>tcache_perthread_struct</code></summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c
/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
# define TCACHE_MAX_BINS 64
# define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)
/* Only used to pre-fill the tunables. */
# define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
/* When "x" is from chunksize(). */
# define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
/* When "x" is a user-provided size. */
# define usize2tidx(x) csize2tidx (request2size (x))
/* With rounding and alignment, the bins are...
idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit)
idx 1 bytes 25..40 or 13..20
idx 2 bytes 41..56 or 21..28
etc. */
/* This is another arbitrary limit, which tunables can change. Each
tcache bin will hold at most this number of chunks. */
# define TCACHE_FILL_COUNT 7
/* Maximum chunks in tcache bins for tunables. This value must fit the range
of tcache->counts[] entries, else they may overflow. */
# define MAX_TCACHE_COUNT UINT16_MAX
[...]
typedef struct tcache_entry
{
struct tcache_entry *next;
/* This field exists to detect double frees. */
uintptr_t key;
} tcache_entry;
/* There is one of these for each thread, which contains the
per-thread cache (hence "tcache_perthread_struct"). Keeping
overall size low is mildly important. Note that COUNTS and ENTRIES
are redundant (we could have just counted the linked list each
time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
uint16_t counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
```
</details>
La función `__tcache_init` es la función que crea y asigna el espacio para el objeto `tcache_perthread_struct`
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3241C1-L3274C2
static void
tcache_init(void)
{
mstate ar_ptr;
void *victim = 0;
const size_t bytes = sizeof (tcache_perthread_struct);
if (tcache_shutting_down)
return;
arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
if (!victim && ar_ptr != NULL)
{
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
/* In a low memory situation, we may not be able to allocate memory
- in which case, we just keep trying later. However, we
typically do this very early, so either there is sufficient
memory, or there isn't enough memory to do non-trivial
allocations anyway. */
if (victim)
{
tcache = (tcache_perthread_struct *) victim;
memset (tcache, 0, sizeof (tcache_perthread_struct));
}
}
```
</details>
### Bins rápidos
Los bins rápidos están diseñados para **acelerar la asignación de memoria para pequeños fragmentos** al mantener fragmentos liberados recientemente en una estructura de acceso rápido. Estos bins utilizan un enfoque de Último en Entrar, Primero en Salir (LIFO), lo que significa que el **fragmento liberado más recientemente es el primero** en ser reutilizado cuando hay una nueva solicitud de asignación. Este comportamiento es ventajoso para la velocidad, ya que es más rápido insertar y eliminar desde la parte superior de una pila (LIFO) en comparación con una cola (FIFO).
Además, **los bins rápidos utilizan listas enlazadas simples**, no dobles, lo que mejora aún más la velocidad. Dado que los fragmentos en los bins rápidos no se fusionan con los vecinos, no es necesario una estructura compleja que permita la eliminación desde el medio. Una lista enlazada simple es más simple y rápida para estas operaciones.
Básicamente, lo que sucede aquí es que el encabezado (el puntero al primer fragmento a verificar) siempre apunta al fragmento liberado más recientemente de ese tamaño. Entonces:
* Cuando se asigna un nuevo fragmento de ese tamaño, el encabezado apunta a un fragmento libre para usar. Como este fragmento libre apunta al siguiente fragmento a usar, esta dirección se almacena en el encabezado para que la próxima asignación sepa dónde obtener un fragmento disponible.
* Cuando se libera un fragmento, el fragmento libre guardará la dirección al fragmento disponible actual y la dirección a este fragmento recién liberado se colocará en el encabezado.
El tamaño máximo de una lista enlazada es `0x80` y están organizados de modo que un fragmento de tamaño `0x20-0x2f` estará en el índice `0`, un fragmento de tamaño `0x30-0x3f` estaría en el índice `1`...
{% hint style="danger" %}
Los fragmentos en los bins rápidos no se establecen como disponibles, por lo que se mantienen como fragmentos de bin rápido durante algún tiempo en lugar de poder fusionarse con otros fragmentos libres que los rodean.
{% endhint %}
```c
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
/*
Fastbins
An array of lists holding recently freed small chunks. Fastbins
are not doubly linked. It is faster to single-link them, and
since chunks are never removed from the middles of these lists,
double linking is not necessary. Also, unlike regular bins, they
are not even processed in FIFO order (they use faster LIFO) since
ordering doesn't much matter in the transient contexts in which
fastbins are normally used.
Chunks in fastbins keep their inuse bit set, so they cannot
be consolidated with other free chunks. malloc_consolidate
releases all chunks in fastbins and consolidates them with
other free chunks.
*/
typedef struct malloc_chunk *mfastbinptr;
#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])
/* offset 2 to use otherwise unindexable first 2 bins */
#define fastbin_index(sz) \
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
/* The maximum fastbin request size we support */
#define MAX_FAST_SIZE (80 * SIZE_SZ / 4)
#define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1)
```
<detalles>
<resumen>Agregar un ejemplo de fragmento fastbin</resumen>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunks[8];
int i;
// Loop to allocate memory 8 times
for (i = 0; i < 8; i++) {
chunks[i] = malloc(24);
if (chunks[i] == NULL) { // Check if malloc failed
fprintf(stderr, "Memory allocation failed at iteration %d\n", i);
return 1;
}
printf("Address of chunk %d: %p\n", i, (void *)chunks[i]);
}
// Loop to free the allocated memory
for (i = 0; i < 8; i++) {
free(chunks[i]);
}
return 0;
}
```
Observa cómo asignamos y liberamos 8 fragmentos del mismo tamaño para que llenen la tcache y el octavo se almacene en el fragmento rápido.
Compílalo y depúralo con un punto de interrupción en el opcode ret de la función principal. Luego, con gef, puedes ver el llenado del contenedor tcache y el fragmento único en el contenedor rápido:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=0, size=0x20, count=7] ← Chunk(addr=0xaaaaaaac1770, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1750, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1730, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1710, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac16f0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac16d0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] ← Chunk(addr=0xaaaaaaac1790, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
Fastbins[idx=1, size=0x30] 0x00
```
</details>
### Bin sin ordenar
El bin sin ordenar es una **caché** utilizada por el administrador del montón para acelerar la asignación de memoria. Así es como funciona: Cuando un programa libera un fragmento, y si este fragmento no puede ser asignado en una tcache o fast bin y no está colisionando con el fragmento superior, el administrador del montón no lo coloca inmediatamente en un bin específico pequeño o grande. En su lugar, primero intenta **fusionarlo con cualquier fragmento libre vecino** para crear un bloque más grande de memoria libre. Luego, coloca este nuevo fragmento en un bin general llamado "bin sin ordenar".
Cuando un programa **solicita memoria**, el administrador del montón **verifica el bin sin ordenar** para ver si hay un fragmento de tamaño suficiente. Si encuentra uno, lo utiliza de inmediato. Si no encuentra un fragmento adecuado en el bin sin ordenar, mueve todos los fragmentos de esta lista a sus bins correspondientes, ya sea pequeños o grandes, según su tamaño.
Tenga en cuenta que si un fragmento más grande se divide en 2 mitades y el resto es mayor que MINSIZE, se colocará nuevamente en el bin sin ordenar.&#x20;
Por lo tanto, el bin sin ordenar es una forma de acelerar la asignación de memoria reutilizando rápidamente la memoria liberada recientemente y reduciendo la necesidad de búsquedas y fusiones que consumen tiempo.
{% hint style="danger" %}
Tenga en cuenta que incluso si los fragmentos son de diferentes categorías, si un fragmento disponible está colisionando con otro fragmento disponible (incluso si pertenecen originalmente a diferentes bins), se fusionarán.
{% endhint %}
<details>
<summary>Agregar un ejemplo de fragmento sin ordenar</summary>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunks[9];
int i;
// Loop to allocate memory 8 times
for (i = 0; i < 9; i++) {
chunks[i] = malloc(0x100);
if (chunks[i] == NULL) { // Check if malloc failed
fprintf(stderr, "Memory allocation failed at iteration %d\n", i);
return 1;
}
printf("Address of chunk %d: %p\n", i, (void *)chunks[i]);
}
// Loop to free the allocated memory
for (i = 0; i < 8; i++) {
free(chunks[i]);
}
return 0;
}
```
Observa cómo asignamos y liberamos 9 fragmentos del mismo tamaño para que **llenen la tcache** y el octavo se almacene en el bin no ordenado porque es **demasiado grande para el fastbin** y el noveno no se libera, por lo que el noveno y el octavo **no se fusionan con el chunk superior**.
Compílalo y depúralo con un breakpoint en el opcode ret de la función main. luego con gef puedes ver el llenado del bin tcache y el único fragmento en el bin no ordenado:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=15, size=0x110, count=7] ← Chunk(addr=0xaaaaaaac1d10, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1c00, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1af0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac19e0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac18d0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac17c0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac12a0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
─────────────────────────────────────────────────────────────────────── Unsorted Bin for arena at 0xfffff7f90b00 ───────────────────────────────────────────────────────────────────────
[+] unsorted_bins[0]: fw=0xaaaaaaac1e10, bk=0xaaaaaaac1e10
→ Chunk(addr=0xaaaaaaac1e20, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in unsorted bin.
```
</details>
### Bins Pequeños
Los bins pequeños son más rápidos que los bins grandes pero más lentos que los bins rápidos.
Cada bin de los 62 tendrá **fragmentos del mismo tamaño**: 16, 24, ... (con un tamaño máximo de 504 bytes en 32 bits y 1024 en 64 bits). Esto ayuda en la rapidez para encontrar el bin donde se debe asignar un espacio e insertar y eliminar entradas en estas listas.
Cada bin de los 62 tendrá **trozos del mismo tamaño**: 16, 24, ... (con un tamaño máximo de 504 bytes en 32 bits y 1024 en 64 bits). Esto ayuda en la velocidad para encontrar el bin donde se debe asignar un espacio e insertar y eliminar entradas en estas listas.
### Bins Grandes
Así es como se calcula el tamaño del bin pequeño según el índice del bin:
A diferencia de los bins pequeños, que gestionan fragmentos de tamaños fijos, cada **bin grande maneja un rango de tamaños de fragmentos**. Esto es más flexible, permitiendo al sistema acomodar **varios tamaños** sin necesidad de un bin separado para cada tamaño.
* Tamaño más pequeño: 2\*4\*índice (por ejemplo, índice 5 -> 40)
* Tamaño más grande: 2\*8\*índice (por ejemplo, índice 5 -> 80)
```c
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > CHUNK_HDR_SZ)
#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
#define smallbin_index(sz) \
((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))\
+ SMALLBIN_CORRECTION)
```
Función para elegir entre contenedores pequeños y grandes:
```c
#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
```
<details>
<summary>Agregar un ejemplo de pequeño fragmento</summary>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunks[10];
int i;
// Loop to allocate memory 8 times
for (i = 0; i < 9; i++) {
chunks[i] = malloc(0x100);
if (chunks[i] == NULL) { // Check if malloc failed
fprintf(stderr, "Memory allocation failed at iteration %d\n", i);
return 1;
}
printf("Address of chunk %d: %p\n", i, (void *)chunks[i]);
}
// Loop to free the allocated memory
for (i = 0; i < 8; i++) {
free(chunks[i]);
}
chunks[9] = malloc(0x110);
return 0;
}
```
Observa cómo asignamos y liberamos 9 fragmentos del mismo tamaño para que **llenen la tcache** y el octavo se almacene en el bin no ordenado porque es **demasiado grande para el fastbin** y el noveno no se libera, por lo que el noveno y el octavo **no se fusionan con el fragmento superior**. Luego asignamos un fragmento más grande de 0x110, lo que hace que **el fragmento en el bin no ordenado pase al bin pequeño**.
Compílalo y depúralo con un punto de interrupción en el opcode ret de la función principal. Luego, con gef, puedes ver el llenado del bin tcache y el único fragmento en el bin pequeño:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=15, size=0x110, count=7] ← Chunk(addr=0xaaaaaaac1d10, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1c00, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1af0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac19e0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac18d0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac17c0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac12a0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
─────────────────────────────────────────────────────────────────────── Unsorted Bin for arena at 0xfffff7f90b00 ───────────────────────────────────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
──────────────────────────────────────────────────────────────────────── Small Bins for arena at 0xfffff7f90b00 ────────────────────────────────────────────────────────────────────────
[+] small_bins[16]: fw=0xaaaaaaac1e10, bk=0xaaaaaaac1e10
→ Chunk(addr=0xaaaaaaac1e20, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in 1 small non-empty bins.
```
</details>
### Bins grandes
A diferencia de los bins pequeños, que manejan fragmentos de tamaños fijos, cada **bin grande maneja un rango de tamaños de fragmentos**. Esto es más flexible, permitiendo que el sistema pueda acomodar **varios tamaños** sin necesidad de un bin separado para cada tamaño.
En un asignador de memoria, los bins grandes comienzan donde terminan los bins pequeños. Los rangos para los bins grandes crecen progresivamente, lo que significa que el primer bin podría cubrir fragmentos de 512 a 576 bytes, mientras que el siguiente cubre de 576 a 640 bytes. Este patrón continúa, con el bin más grande que contiene todos los fragmentos por encima de 1MB.
Los bins grandes son más lentos de operar en comparación con los bins pequeños porque deben **ordenar y buscar a través de una lista de tamaños de fragmentos variables para encontrar el mejor ajuste** para una asignación. Cuando se inserta un fragmento en un bin grande, debe ser ordenado, y al asignar memoria, el sistema debe encontrar el fragmento adecuado. Este trabajo adicional los hace **más lentos**, pero como las asignaciones grandes son menos comunes que las pequeñas, es un compromiso aceptable.
Los bins grandes son más lentos de operar en comparación con los bins pequeños porque deben **ordenar y buscar a través de una lista de tamaños de fragmentos variables para encontrar el mejor ajuste** para una asignación. Cuando un fragmento se inserta en un bin grande, debe ser ordenado, y cuando se asigna memoria, el sistema debe encontrar el fragmento adecuado. Este trabajo adicional los hace **más lentos**, pero dado que las asignaciones grandes son menos comunes que las pequeñas, es un compromiso aceptable.
Hay:
* 32 bins de rango de 64B
* 16 bins de rango de 512B
* 8 bins de rango de 4096B
* 32 bins de rango de 64B (colisionan con los bins pequeños)
* 16 bins de rango de 512B (colisionan con los bins pequeños)
* 8 bins de rango de 4096B (parte colisiona con los bins pequeños)
* 4 bins de rango de 32768B
* 2 bins de rango de 262144B
* 1 bin para tamaños restantes
### Bin Desordenado
<details>
El bin desordenado es una **caché rápida** utilizada por el administrador de montón para hacer que la asignación de memoria sea más rápida. Así es como funciona: Cuando un programa libera memoria, el administrador de montón no la coloca inmediatamente en un bin específico. En su lugar, primero intenta **fusionarla con cualquier fragmento libre vecino** para crear un bloque más grande de memoria libre. Luego, coloca este nuevo fragmento en un bin general llamado "bin desordenado".
<summary>Código de tamaños de bins grandes</summary>
```c
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
Cuando un programa **solicita memoria**, el administrador de montón **verifica el bin desordenado** para ver si hay un fragmento de tamaño suficiente. Si encuentra uno, lo utiliza de inmediato. Si no encuentra un fragmento adecuado, mueve los fragmentos liberados a sus bins correspondientes, ya sea pequeños o grandes, según su tamaño.
#define largebin_index_32(sz) \
(((((unsigned long) (sz)) >> 6) <= 38) ? 56 + (((unsigned long) (sz)) >> 6) :\
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\
126)
Por lo tanto, el bin desordenado es una forma de acelerar la asignación de memoria reutilizando rápidamente la memoria liberada recientemente y reduciendo la necesidad de búsquedas y fusiones que consumen tiempo.
#define largebin_index_32_big(sz) \
(((((unsigned long) (sz)) >> 6) <= 45) ? 49 + (((unsigned long) (sz)) >> 6) :\
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\
126)
{% hint style="danger" %}
Ten en cuenta que incluso si los fragmentos son de diferentes categorías, si un fragmento disponible está colisionando con otro fragmento disponible (incluso si son de diferentes categorías), se fusionarán.
{% endhint %}
// XXX It remains to be seen whether it is good to keep the widths of
// XXX the buckets the same or whether it should be scaled by a factor
// XXX of two as well.
#define largebin_index_64(sz) \
(((((unsigned long) (sz)) >> 6) <= 48) ? 48 + (((unsigned long) (sz)) >> 6) :\
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\
126)
### Bins Rápidos
#define largebin_index(sz) \
(SIZE_SZ == 8 ? largebin_index_64 (sz) \
: MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz) \
: largebin_index_32 (sz))
```
</details>
Los bins rápidos están diseñados para **acelerar la asignación de memoria para fragmentos pequeños** manteniendo fragmentos liberados recientemente en una estructura de acceso rápido. Estos bins utilizan un enfoque de Último en Entrar, Primero en Salir (LIFO), lo que significa que el **fragmento liberado más recientemente es el primero** en ser reutilizado cuando hay una nueva solicitud de asignación. Este comportamiento es ventajoso para la velocidad, ya que es más rápido insertar y eliminar desde la parte superior de una pila (LIFO) en comparación con una cola (FIFO).
<details>
Además, **los bins rápidos utilizan listas enlazadas simples**, no dobles, lo que mejora aún más la velocidad. Dado que los fragmentos en los bins rápidos no se fusionan con vecinos, no es necesario una estructura compleja que permita la eliminación desde el medio. Una lista enlazada simple es más simple y rápida para estas operaciones.
<summary>Agregar un ejemplo de un gran fragmento</summary>
```c
#include <stdlib.h>
#include <stdio.h>
Básicamente, lo que sucede aquí es que el encabezado (el puntero al primer fragmento a verificar) siempre apunta al fragmento liberado más reciente de ese tamaño. Entonces:
int main(void)
{
char *chunks[2];
* Cuando se asigna un nuevo fragmento de ese tamaño, el encabezado apunta a un fragmento libre para usar. Como este fragmento libre apunta al siguiente fragmento a usar, esta dirección se almacena en el encabezado para que la próxima asignación sepa dónde obtener un fragmento disponible.
* Cuando se libera un fragmento, el fragmento libre guardará la dirección al fragmento disponible actual y la dirección a este fragmento recién liberado se colocará en el encabezado.
chunks[0] = malloc(0x1500);
chunks[1] = malloc(0x1500);
free(chunks[0]);
chunks[0] = malloc(0x2000);
{% hint style="danger" %}
Los fragmentos en los bins rápidos no se establecen automáticamente como disponibles, por lo que se mantienen como fragmentos de bin rápido durante algún tiempo en lugar de poder fusionarse con otros fragmentos.
{% endhint %}
return 0;
}
```
Dos grandes asignaciones se realizan, luego una se libera (poniéndola en el contenedor no ordenado) y se realiza una asignación más grande (moviendo la liberada del contenedor no ordenado al contenedor grande).
### Bins Tcache (Caché por Hilo)
Compílalo y depúralo con un punto de interrupción en el opcode ret de la función principal. Luego, con gef, puedes ver el llenado del contenedor tcache y el fragmento en el contenedor grande:
```bash
gef➤ heap bin
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
All tcachebins are empty
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
─────────────────────────────────────────────────────────────────────── Unsorted Bin for arena at 0xfffff7f90b00 ───────────────────────────────────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
──────────────────────────────────────────────────────────────────────── Small Bins for arena at 0xfffff7f90b00 ────────────────────────────────────────────────────────────────────────
[+] Found 0 chunks in 0 small non-empty bins.
──────────────────────────────────────────────────────────────────────── Large Bins for arena at 0xfffff7f90b00 ────────────────────────────────────────────────────────────────────────
[+] large_bins[100]: fw=0xaaaaaaac1290, bk=0xaaaaaaac1290
→ Chunk(addr=0xaaaaaaac12a0, size=0x1510, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in 1 large non-empty bins.
```
</details>
Aunque los hilos intentan tener su propio montón (ver [Arenas](bins-and-memory-allocations.md#arenas) y [Submontones](bins-and-memory-allocations.md#subheaps)), existe la posibilidad de que un proceso con muchos hilos (como un servidor web) **termine compartiendo el montón con otros hilos**. En este caso, la solución principal es el uso de **bloqueos**, que podrían **ralentizar significativamente los hilos**.
### Trozo Superior
```c
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
Por lo tanto, un tcache es similar a un bin rápido por hilo en el sentido de que es una **lista enlazada simple** que no fusiona fragmentos. Cada hilo tiene **64 bins tcache enlazados individualmente**. Cada bin puede tener un máximo de [7 fragmentos del mismo tamaño](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l323) que van desde [24 a 1032B en sistemas de 64 bits y de 12 a 516B en sistemas de 32 bits](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l315).
/*
Top
Cuando un hilo **libera** un fragmento, **si no es demasiado grande** para ser asignado en el tcache y el bin tcache **no está lleno** (ya tiene 7 fragmentos), **se asignará allí**. Si no puede ir al tcache, deberá esperar a que el bloqueo del montón esté disponible para poder realizar la operación de liberación global.
The top-most available chunk (i.e., the one bordering the end of
available memory) is treated specially. It is never included in
any bin, is used only if no other chunk is available, and is
released back to the system if it is very large (see
M_TRIM_THRESHOLD). Because top initially
points to its own bin with initial zero size, thus forcing
extension on the first malloc request, we avoid having any special
code in malloc to check whether it even exists yet. But we still
need to do so when getting memory from system, so we make
initial_top treat the bin as a legal but unusable chunk during the
interval between initialization and the first call to
sysmalloc. (This is somewhat delicate, since it relies on
the 2 preceding words to be zero during this interval as well.)
*/
/* Conveniently, the unsorted bin can be used as dummy top on first call */
#define initial_top(M) (unsorted_chunks (M))
```
Básicamente, este es un fragmento que contiene toda la memoria heap disponible actualmente. Cuando se realiza un malloc, si no hay ningún fragmento libre disponible para usar, este fragmento superior reducirá su tamaño proporcionando el espacio necesario.\
El puntero al Fragmento Superior se almacena en la estructura `malloc_state`.
Además, al principio, es posible utilizar el fragmento no ordenado como el fragmento superior.
<details>
<summary>Observa el ejemplo del Fragmento Superior</summary>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunk;
chunk = malloc(24);
printf("Address of the chunk: %p\n", (void *)chunk);
gets(chunk);
return 0;
}
```
Después de compilar y depurarlo con un punto de interrupción en el opcode ret de main, vi que el malloc devolvió la dirección: `0xaaaaaaac12a0` y estos son los fragmentos:
```bash
gef➤ heap chunks
Chunk(addr=0xaaaaaaac1010, size=0x290, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac1010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12a0 41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 00 AAAAAAA.........]
Chunk(addr=0xaaaaaaac12c0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12c0 41 64 64 72 65 73 73 20 6f 66 20 74 68 65 20 63 Address of the c]
Chunk(addr=0xaaaaaaac16d0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac16d0 41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00 00 AAAAAAA.........]
Chunk(addr=0xaaaaaaac1ae0, size=0x20530, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← top chunk
```
Donde se puede ver que el fragmento superior está en la dirección `0xaaaaaaac1ae0`. Esto no es sorprendente porque el fragmento asignado más reciente estaba en `0xaaaaaaac12a0` con un tamaño de `0x410` y `0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0`.\
También es posible ver la longitud del fragmento superior en su encabezado de fragmento:
```bash
gef➤ x/8wx 0xaaaaaaac1ae0 - 16
0xaaaaaaac1ad0: 0x00000000 0x00000000 0x00020531 0x00000000
0xaaaaaaac1ae0: 0x00000000 0x00000000 0x00000000 0x00000000
```
</details>
### Último Recordatorio
Cuando se utiliza malloc y se divide un fragmento (de la lista no enlazada o del fragmento superior, por ejemplo), el fragmento creado a partir del resto del fragmento dividido se llama Último Recordatorio y su puntero se almacena en la estructura `malloc_state`.
Cuando se **asigna un fragmento**, si hay un fragmento libre del tamaño necesario en el **tcache, se utilizará**, de lo contrario, deberá esperar a que el bloqueo del montón esté disponible para encontrar uno en los bins globales o crear uno nuevo.\
También hay una optimización, en este caso, mientras tiene el bloqueo del montón, el hilo **llenará su tcache con fragmentos del montón (7) del tamaño solicitado**, por lo que en caso de que necesite más, los encontrará en el tcache.
## Flujo de Asignación
{% hint style="success" %}
(Esta explicación actual es de [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions). TODO: Verificar la última versión y actualizarla)
{% endhint %}
Consulta:
Las asignaciones se realizan finalmente con la función: `void * _int_malloc (mstate av, size_t bytes)` y siguen este orden:
{% content-ref url="heap-memory-functions/malloc-and-sysmalloc.md" %}
[malloc-and-sysmalloc.md](heap-memory-functions/malloc-and-sysmalloc.md)
{% endcontent-ref %}
1. Actualiza `bytes` para tener en cuenta **alineaciones**, etc.
2. Verifica si `av` es **NULL** o no.
3. En caso de ausencia de **área utilizable** (cuando `av` es NULL), llama a `sysmalloc` para obtener un fragmento usando mmap. Si tiene éxito, llama a `alloc_perturb`. Devuelve el puntero.
4. Dependiendo del tamaño:
* \[Adición a lo original] Usa tcache antes de verificar el siguiente fastbin.
* \[Adición a lo original] Si no hay tcache pero se utiliza un bin diferente (ver paso posterior), intenta llenar tcache desde ese bin.
* Si el tamaño cae en el rango de **fastbin**:
1. Obtiene un índice en el array de fastbin para acceder a un bin apropiado según el tamaño solicitado.
2. Elimina el primer fragmento en ese bin y hace que `víctima` apunte a él.
3. Si `víctima` es NULL, pasa al siguiente caso (smallbin).
4. Si `víctima` no es NULL, verifica el tamaño del fragmento para asegurarse de que pertenezca a ese bin en particular. De lo contrario, se lanza un error ("malloc(): corrupción de memoria (fast)").
5. Llama a `alloc_perturb` y luego devuelve el puntero.
* Si el tamaño cae en el rango de **smallbin**:
1. Obtiene un índice en el array de smallbin para acceder a un bin apropiado según el tamaño solicitado.
2. Si no hay fragmentos en este bin, pasa al siguiente caso. Esto se verifica comparando los punteros `bin` y `bin->bk`.
3. `víctima` se iguala a `bin->bk` (el último fragmento en el bin). Si es NULL (ocurre durante la `inicialización`), llama a `malloc_consolidate` y salta este paso completo de verificación en diferentes bins.
4. De lo contrario, cuando `víctima` no es NULL, verifica si `victim->bk->fd` y `víctima` son iguales o no. Si no son iguales, se lanza un error (`malloc(): lista doblemente enlazada de smallbin corrompida`).
5. Establece el bit PREV\_INSUSE para el siguiente fragmento (en memoria, no en la lista doblemente enlazada) para `víctima`.
6. Elimina este fragmento de la lista del bin.
7. Establece el bit de arena apropiado para este fragmento según `av`.
8. Llama a `alloc_perturb` y luego devuelve el puntero.
* Si el tamaño no cae en el rango de smallbin:
1. Obtiene un índice en el array de largebin para acceder a un bin apropiado según el tamaño solicitado.
2. Verifica si `av` tiene fastchunks o no. Esto se hace verificando el `FASTCHUNKS_BIT` en `av->flags`. Si es así, llama a `malloc_consolidate` en `av`.
5. Si aún no se ha devuelto ningún puntero, esto significa uno o más de los siguientes casos:
1. El tamaño cae en el rango de 'fastbin' pero no hay fastchunk disponible.
2. El tamaño cae en el rango de 'smallbin' pero no hay smallchunk disponible (llama a `malloc_consolidate` durante la inicialización).
3. El tamaño cae en el rango de 'largebin'.
6. A continuación, se verifican los **fragmentos no ordenados** y los fragmentos recorridos se colocan en bins. Este es el único lugar donde se colocan los fragmentos en bins. Itera el bin no ordenado desde la 'COLA'.
1. `víctima` apunta al fragmento actual que se está considerando.
2. Verifica si el tamaño del fragmento de `víctima` está dentro del rango mínimo (`2*SIZE_SZ`) y máximo (`av->system_mem`). De lo contrario, lanza un error (`malloc(): corrupción de memoria`).
3. Si (el tamaño del fragmento solicitado cae en el rango de smallbin) y (`víctima` es el último fragmento restante) y (es el único fragmento en el bin no ordenado) y (el tamaño de los fragmentos >= el solicitado): **Divide el fragmento en 2 fragmentos**:
* El primer fragmento coincide con el tamaño solicitado y se devuelve.
* El fragmento restante se convierte en el nuevo último fragmento restante. Se inserta de nuevo en el bin no ordenado.
1. Establece los campos `chunk_size` y `chunk_prev_size` apropiadamente para ambos fragmentos.
2. Se devuelve el primer fragmento después de llamar a `alloc_perturb`.
3. Si la condición anterior es falsa, el control llega aquí. Elimina `víctima` del bin no ordenado. Si el tamaño de `víctima` coincide exactamente con el tamaño solicitado, devuelve este fragmento después de llamar a `alloc_perturb`.
4. Si el tamaño de `víctima` cae en el rango de smallbin, agrega el fragmento en el smallbin apropiado en la `CABEZA`.
5. De lo contrario, inserta en el largebin apropiado manteniendo el orden ordenado:
6. Primero verifica el último fragmento (más pequeño). Si `víctima` es más pequeña que el último fragmento, lo inserta al final.
7. De lo contrario, bucle para encontrar un fragmento con un tamaño >= al tamaño de `víctima`. Si el tamaño es exactamente el mismo, siempre se inserta en la segunda posición.
8. Repite este paso completo un máximo de `MAX_ITERS` (10000) veces o hasta que se agoten todos los fragmentos en el bin no ordenado.
7. Después de verificar los fragmentos no ordenados, verifica si el tamaño solicitado no cae en el rango de smallbin, si es así, verifica los **largebins**.
1. Obtiene un índice en el array de largebin para acceder a un bin apropiado según el tamaño solicitado.
2. Si el tamaño del fragmento más grande (el primer fragmento en el bin) es mayor que el tamaño solicitado:
1. Itera desde la 'COLA' para encontrar un fragmento (`víctima`) con el tamaño más pequeño >= al tamaño solicitado.
2. Llama a `unlink` para eliminar el fragmento `víctima` del bin.
3. Calcula `remainder_size` para el fragmento de `víctima` (esto será el tamaño del fragmento de `víctima` - tamaño solicitado).
4. Si este `remainder_size` >= `MINSIZE` (el tamaño mínimo del fragmento incluyendo los encabezados), divide el fragmento en dos fragmentos. De lo contrario, se devolverá todo el fragmento de `víctima`. Inserta el fragmento restante en el bin no ordenado (en el extremo 'COLA'). Se realiza una verificación en el bin no ordenado si `unsorted_chunks(av)->fd->bk == unsorted_chunks(av)`. Se lanza un error de lo contrario ("malloc(): fragmentos no ordenados corruptos").
5. Devuelve el fragmento de `víctima` después de llamar a `alloc_perturb`.
8. Hasta ahora, hemos verificado el bin no ordenado y también el respectivo bin fast, small o large. Tenga en cuenta que se verificó un solo bin (fast o small) usando el tamaño **exacto** del fragmento solicitado. Repita los siguientes pasos hasta que se agoten todos los bins:
1. Se incrementa el índice en el array de bin para verificar el siguiente bin.
2. Usa el mapa `av->binmap` para saltar los bins que están vacíos.
3. `víctima` apunta a la 'COLA' del bin actual.
4. El uso del binmap asegura que si se salta un bin (en el paso 2 anterior), definitivamente está vacío. Sin embargo, no garantiza que se salten todos los bins vacíos. Verifica si la víctima está vacía o no. Si está vacía, nuevamente salta el bin y repite el proceso anterior (o 'continúa' este bucle) hasta llegar a un bin no vacío.
5. Divide el fragmento (`víctima` apunta al último fragmento de un bin no vacío) en dos fragmentos. Inserta el fragmento restante en el bin no ordenado (en el extremo 'COLA'). Se realiza una verificación en el bin no ordenado si `unsorted_chunks(av)->fd->bk == unsorted_chunks(av)`. Se lanza un error de lo contrario ("malloc(): fragmentos no ordenados corruptos 2").
6. Devuelve el fragmento de `víctima` después de llamar a `alloc_perturb`.
9. Si aún no se encuentra un bin vacío, se utilizará el fragmento 'top' para atender la solicitud:
1. `víctima` apunta a `av->top`.
2. Si el tamaño del fragmento 'top' >= 'tamaño solicitado' + `MINSIZE`, divídalo en dos fragmentos. En este caso, el fragmento restante se convierte en el nuevo fragmento 'top' y el otro fragmento se devuelve al usuario después de llamar a `alloc_perturb`.
3. Verifica si `av` tiene fastchunks o no. Esto se hace verificando el `FASTCHUNKS_BIT` en `av->flags`. Si es así, llama a `malloc_consolidate` en `av`. Vuelve al paso 6 (donde verificamos el bin no ordenado).
4. Si `av` no tiene fastchunks, llama a `sysmalloc` y devuelve el puntero obtenido después de llamar a `alloc_perturb`.
## Flujo Libre
## Flujo de Liberación
{% hint style="success" %}
(Esta explicación actual es de [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions). TODO: Verificar la última versión y actualizarla)
{% endhint %}
Consulta:
La función final que libera fragmentos de memoria es `_int_free (mstate av, mchunkptr p, int have_lock)`:
{% content-ref url="heap-memory-functions/free.md" %}
[free.md](heap-memory-functions/free.md)
{% endcontent-ref %}
1. Verificar si `p` está antes de `p + chunksize(p)` en la memoria (para evitar desbordamientos). De lo contrario, se lanza un error (`free(): invalid pointer`).
2. Verificar si el fragmento tiene al menos el tamaño `MINSIZE` o es un múltiplo de `MALLOC_ALIGNMENT`. De lo contrario, se lanza un error (`free(): invalid size`).
3. Si el tamaño del fragmento está en la lista de fastbins:
1. Verificar si el tamaño del siguiente fragmento está entre el tamaño mínimo y máximo (`av->system_mem`), lanzar un error (`free(): invalid next size (fast)`) de lo contrario.
2. Llamar a `free_perturb` en el fragmento.
3. Establecer `FASTCHUNKS_BIT` para `av`.
4. Obtener el índice en el array de fastbins según el tamaño del fragmento.
5. Verificar si la parte superior del bin no es el fragmento que vamos a agregar. De lo contrario, lanzar un error (`double free or corruption (fasttop)`).
6. Verificar si el tamaño del fragmento de fastbin en la parte superior es el mismo que el fragmento que estamos agregando. De lo contrario, lanzar un error (`invalid fastbin entry (free)`).
7. Insertar el fragmento en la parte superior de la lista de fastbins y retornar.
4. Si el fragmento no está asignado dinámicamente:
1. Verificar si el fragmento es el fragmento superior o no. Si es así, se lanza un error (`double free or corruption (top)`).
2. Verificar si el siguiente fragmento (por memoria) está dentro de los límites del área. Si no, se lanza un error (`double free or corruption (out)`).
3. Verificar si el bit previo en uso del siguiente fragmento (por memoria) está marcado o no. Si no, se lanza un error (`double free or corruption (!prev)`).
4. Verificar si el tamaño del siguiente fragmento está entre el tamaño mínimo y máximo (`av->system_mem`). Si no, se lanza un error (`free(): invalid next size (normal)`).
5. Llamar a `free_perturb` en el fragmento.
6. Si el fragmento previo (por memoria) no está en uso, llamar a `unlink` en el fragmento previo.
7. Si el siguiente fragmento (por memoria) no es el fragmento superior:
1. Si el siguiente fragmento no está en uso, llamar a `unlink` en el siguiente fragmento.
2. Fusionar el fragmento con el anterior, siguiente (por memoria), si alguno está libre y agregarlo al principio del bin desordenado. Antes de insertar, verificar si `unsorted_chunks(av)->fd->bk == unsorted_chunks(av)` o no. Si no, se lanza un error ("free(): corrupted unsorted chunks").
8. Si el siguiente fragmento (por memoria) era un fragmento superior, fusionar los fragmentos adecuadamente en un único fragmento superior.
5. Si el fragmento estaba asignado dinámicamente, llamar a `munmap_chunk`.
## Verificaciones de Seguridad de Funciones de Montón
## Verificaciones de Seguridad de Funciones de Heap
Verifica las verificaciones de seguridad realizadas por funciones ampliamente utilizadas en el montón en:
Verifique las verificaciones de seguridad realizadas por funciones ampliamente utilizadas en el heap en:
{% content-ref url="heap-functions-security-checks.md" %}
[heap-functions-security-checks.md](heap-functions-security-checks.md)
{% content-ref url="heap-memory-functions/heap-functions-security-checks.md" %}
[heap-functions-security-checks.md](heap-memory-functions/heap-functions-security-checks.md)
{% endcontent-ref %}
## Referencias
@ -194,15 +601,16 @@ Verifique las verificaciones de seguridad realizadas por funciones ampliamente u
* [https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/](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/](https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/)
* [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions)
* [https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/implementation/tcache/](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/implementation/tcache/)
<details>
<summary><strong>Aprende hacking en AWS desde cero hasta experto con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
<summary><strong>Aprende hacking en AWS de cero a héroe con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Otras formas de apoyar a HackTricks:
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Obtén el [**oficial PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).

View file

@ -6,11 +6,11 @@
Otras formas de apoyar a HackTricks:
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén el [**swag oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.
* **Comparte tus trucos de hacking enviando PRs a los repositorios de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
@ -21,7 +21,7 @@ Un desbordamiento de montón es como un [**desbordamiento de pila**](../stack-ov
En los desbordamientos de pila sabemos que algunos registros como el puntero de instrucción o el marco de pila van a ser restaurados desde la pila y podría ser posible abusar de esto. En el caso de los desbordamientos de montón, **no hay ninguna información sensible almacenada por defecto** en el fragmento de montón que puede ser desbordado. Sin embargo, podría ser información sensible o punteros, por lo que la **críticidad** de esta vulnerabilidad **depende** de **qué datos podrían ser sobrescritos** y de cómo un atacante podría abusar de esto.
{% hint style="success" %}
Para encontrar los desbordamientos de montón puedes usar los mismos patrones que en los [**desbordamientos de pila**](../stack-overflow/#finding-stack-overflows-offsets).
Para encontrar los desbordamientos de desbordamiento puedes usar los mismos patrones que en los [**desbordamientos de pila**](../stack-overflow/#finding-stack-overflows-offsets).
{% endhint %}
### Desbordamientos de Pila vs Desbordamientos de Montón
@ -30,23 +30,31 @@ En los desbordamientos de pila, la disposición y los datos que van a estar pres
Sin embargo, en el caso de un desbordamiento de montón, debido a que la memoria utilizada no es lineal sino que **los fragmentos asignados suelen estar en posiciones separadas de la memoria** (no uno al lado del otro) debido a **contenedores y zonas** que separan las asignaciones por tamaño y porque **la memoria previamente liberada se utiliza** antes de asignar nuevos fragmentos. Es **complicado saber el objeto que va a estar colisionando con el vulnerable** a un desbordamiento de montón. Por lo tanto, cuando se encuentra un desbordamiento de montón, es necesario encontrar una **forma confiable de hacer que el objeto deseado esté a continuación en la memoria** del que puede ser desbordado.
Una de las técnicas utilizadas para esto es el **Aseo de Montón** que se utiliza, por ejemplo, [**en este post**](https://azeria-labs.com/grooming-the-ios-kernel-heap/). En el post se explica cómo en el núcleo de iOS cuando una zona se queda sin memoria para almacenar fragmentos de memoria, la expande en una página de kernel, y esta página se divide en fragmentos de los tamaños esperados que se utilizarían en orden (hasta la versión 9.2 de iOS, luego estos fragmentos se utilizan de manera aleatoria para dificultar la explotación de estos ataques).
Una de las técnicas utilizadas para esto es el **Aseo de Montón** que se utiliza, por ejemplo, [**en este post**](https://azeria-labs.com/grooming-the-ios-kernel-heap/). En el post se explica cómo en el núcleo de iOS, cuando una zona se queda sin memoria para almacenar fragmentos de memoria, la expande en una página de núcleo, y esta página se divide en fragmentos de los tamaños esperados que se utilizarían en orden (hasta la versión 9.2 de iOS, luego estos fragmentos se utilizan de manera aleatoria para dificultar la explotación de estos ataques).
Por lo tanto, en el post anterior donde ocurre un desbordamiento de montón, para forzar que el objeto desbordado esté colisionando con un orden víctima, se fuerzan varios **`kallocs` por varios hilos para intentar asegurar que todos los fragmentos libres estén llenos y que se cree una nueva página**.
Por lo tanto, en el post anterior donde ocurre un desbordamiento de montón, para forzar que el objeto desbordado esté colisionando con un orden víctima, varios **`kallocs` son forzados por varios hilos para intentar asegurar que todos los fragmentos libres estén llenos y que se cree una nueva página**.
Para forzar este llenado con objetos de un tamaño específico, la **asignación fuera de línea asociada con un puerto mach de iOS** es un candidato ideal. Al diseñar el tamaño del mensaje, es posible especificar exactamente el tamaño de la asignación de `kalloc` y cuando se destruye el puerto mach correspondiente, la asignación correspondiente se liberará inmediatamente de vuelta a `kfree`.
Para forzar este llenado con objetos de un tamaño específico, la **asignación fuera de línea asociada con un puerto mach iOS** es un candidato ideal. Al diseñar el tamaño del mensaje, es posible especificar exactamente el tamaño de la asignación de `kalloc` y cuando se destruye el puerto mach correspondiente, la asignación correspondiente se liberará inmediatamente a `kfree`.
Luego, algunos de estos marcadores de posición pueden ser **liberados**. La lista de liberación de **`kalloc.4096` libera elementos en orden de último en entrar, primero en salir**, lo que básicamente significa que si algunos marcadores de posición son liberados y el exploit intenta asignar varios objetos víctimas mientras intenta asignar el objeto vulnerable al desbordamiento, es probable que este objeto sea seguido por un objeto víctima.
## Ejemplo ARM64
### Ejemplo libc
En la página [https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/](https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/) puedes encontrar un ejemplo de desbordamiento de montón donde un comando que va a ser ejecutado se almacena en el siguiente fragmento desde el fragmento desbordado. Por lo tanto, es posible modificar el comando ejecutado sobrescribiéndolo con un exploit sencillo como:
[**En esta página**](https://guyinatuxedo.github.io/27-edit\_free\_chunk/heap\_consolidation\_explanation/index.html) es posible encontrar una emulación básica de desbordamiento de montón que muestra cómo sobrescribir el bit previo en uso del siguiente fragmento y la posición del tamaño previo es posible **consolidar un fragmento utilizado** (haciéndolo creer que no está en uso) y **luego asignarlo nuevamente** pudiendo sobrescribir datos que se están utilizando en un puntero diferente también.
Otro ejemplo de [**protostar heap 0**](https://guyinatuxedo.github.io/24-heap\_overflow/protostar\_heap0/index.html) muestra un ejemplo muy básico de un CTF donde un **desbordamiento de montón** puede ser abusado para llamar a la función ganadora y **obtener la bandera**.
En el ejemplo [**protostar heap 1**](https://guyinatuxedo.github.io/24-heap\_overflow/protostar\_heap1/index.html) es posible ver cómo abusando de un desbordamiento de búfer es posible **sobrescribir en un fragmento cercano una dirección** donde se va a escribir **datos arbitrarios del usuario**.
### Ejemplo ARM64
En la página [https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/](https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/) puedes encontrar un ejemplo de desbordamiento de montón donde un comando que va a ser ejecutado se almacena en el siguiente fragmento al fragmento desbordado. Por lo tanto, es posible modificar el comando ejecutado sobrescribiéndolo con un exploit sencillo como:
```bash
python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt
```
<details>
<summary><strong>Aprende hacking en AWS de cero a héroe con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (Experto en Equipo Rojo de AWS de HackTricks)</strong></a><strong>!</strong></summary>
<summary><strong>Aprende hacking en AWS de cero a héroe con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (Experto en Red Team de AWS de HackTricks)</strong></a><strong>!</strong></summary>
Otras formas de apoyar a HackTricks:

View file

@ -6,11 +6,11 @@
Otras formas de apoyar a HackTricks:
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén [**productos oficiales de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.
* **Comparte tus trucos de hacking enviando PRs a los repositorios de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
@ -89,11 +89,11 @@ return 0;
```
</details>
* El ataque no funciona si se utilizan tcaches
* El ataque no funciona si se utilizan tcaches (después de la versión 2.26)
### Objetivo
* Modificar un puntero a un fragmento en la pila para que apunte a la pila y sea posible alterar el contenido de la pila escribiendo en el fragmento
* Modificar un puntero a un fragmento en la pila para que apunte a la pila, de modo que sea posible alterar el contenido de la pila escribiendo en el fragmento
### Requisitos
@ -116,22 +116,27 @@ return 0;
* `fake_chunk->bk->fd` = `fake_chunk->fd`
* Anteriormente se hizo que `fake_chunk->fd->bk` y `fake_chunk->fd->bk` apunten al mismo lugar (la ubicación en la pila donde se almacenaba `fragmento1`, por lo que era una lista enlazada válida). Como **ambos apuntan al mismo lugar**, solo el último (`fake_chunk->bk->fd = fake_chunk->fd`) tendrá **efecto**.
* Esto **sobrescribirá el puntero al fragmento1 en la pila a la dirección (o bytes) almacenados 3 direcciones antes en la pila**.
* Por lo tanto, si un atacante pudiera controlar nuevamente el contenido del fragmento1, podrá **escribir dentro de la pila**, pudiendo potencialmente sobrescribir la dirección de retorno saltando el canary y modificar los valores y puntos de las variables locales. Incluso modificando nuevamente la dirección del fragmento1 almacenada en la pila a una ubicación diferente donde si el atacante pudiera controlar nuevamente el contenido del fragmento1, podrá escribir en cualquier lugar.
* Por lo tanto, si un atacante pudiera controlar nuevamente el contenido del fragmento1, podrá **escribir dentro de la pila**, pudiendo potencialmente sobrescribir la dirección de retorno saltando el canary y modificar los valores y puntos de las variables locales. Incluso modificando nuevamente la dirección del fragmento1 almacenada en la pila a una ubicación diferente donde, si el atacante pudiera controlar nuevamente el contenido del fragmento1, podrá escribir en cualquier lugar.
* Tenga en cuenta que esto fue posible porque las **direcciones se almacenan en la pila**. El riesgo y la explotación pueden depender de **dónde se almacenan las direcciones del fragmento falso**.
<figure><img src="../../.gitbook/assets/image (1246).png" alt=""><figcaption><p><a href="https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit">https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit</a></p></figcaption></figure>
## Referencias
* [https://heap-exploitation.dhavalkapil.com/attacks/unlink\_exploit](https://heap-exploitation.dhavalkapil.com/attacks/unlink\_exploit)
* Aunque sería extraño encontrar un ataque de desvinculación incluso en un CTF, aquí tienes algunos writeups donde se utilizó este ataque:
* Ejemplo de CTF: [https://guyinatuxedo.github.io/30-unlink/hitcon14\_stkof/index.html](https://guyinatuxedo.github.io/30-unlink/hitcon14\_stkof/index.html)
* En este ejemplo, en lugar de la pila, hay una matriz de direcciones malloc'ed. El ataque de desvinculación se realiza para poder asignar un fragmento aquí, pudiendo así controlar los punteros de la matriz de direcciones malloc'ed. Luego, hay otra funcionalidad que permite modificar el contenido de los fragmentos en estas direcciones, lo que permite apuntar direcciones a la GOT, modificar direcciones de funciones para obtener fugas y RCE.
*
<details>
<summary><strong>Aprende hacking en AWS desde cero hasta experto con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
<summary><strong>Aprende hacking en AWS de cero a héroe con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Otras formas de apoyar a HackTricks:
* Si quieres ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén el [**oficial PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.

View file

@ -6,8 +6,8 @@
Otras formas de apoyar a HackTricks:
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén el [**swag oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
@ -29,16 +29,14 @@ Consulta más información en:
[first-fit.md](first-fit.md)
{% endcontent-ref %}
##
<details>
<summary><strong>Aprende hacking en AWS desde cero hasta experto con</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (Experto en Equipos Rojos de AWS de HackTricks)</strong></a><strong>!</strong></summary>
Otras formas de apoyar a HackTricks:
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén el [**swag oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Si deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos** en **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).