free
Reading time: 12 minutes
tip
Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
Free Order Summary
(Hakuna ukaguzi unaoelezewa katika muhtasari huu na baadhi ya kesi zimeachwa nje kwa ufupisho)
- Ikiwa anwani ni null usifanye chochote
- Ikiwa chunk ilikuwa mmaped, fanya munmap na maliza
- Ita
_int_free
: - Ikiwa inawezekana, ongeza chunk kwenye tcache
- Ikiwa inawezekana, ongeza chunk kwenye fast bin
- Ita
_int_free_merge_chunk
ili kuunganisha chunk ikiwa inahitajika na kuiweka kwenye unsorted list
Kumbuka: Kuanzia glibc 2.42, hatua ya tcache inaweza pia kushikilia chunks hadi ukubwa mkubwa zaidi wa threshold (angalia “Recent glibc changes” hapo chini). Hii inabadilisha ni lini free inaingia katika tcache dhidi ya unsorted/small/large bins.
__libc_free
Free
huitwa __libc_free
.
- Ikiwa anwani iliyopita ni Null (0) usifanye chochote.
- Angalia pointer tag
- Ikiwa chunk ni
mmaped
, fanyamunmap
na hiyo tu - Vinginevyo, ongeza color kisha ita
_int_free
juu yake
__lib_free code
void
__libc_free (void *mem)
{
mstate ar_ptr;
mchunkptr p; /* chunk corresponding to mem */
if (mem == 0) /* free(0) has no effect */
return;
/* Quickly check that the freed pointer matches the tag for the memory.
This gives a useful double-free detection. */
if (__glibc_unlikely (mtag_enabled))
*(volatile char *)mem;
int err = errno;
p = mem2chunk (mem);
if (chunk_is_mmapped (p)) /* release mmapped memory. */
{
/* See if the dynamic brk/mmap threshold needs adjusting.
Dumped fake mmapped chunks do not affect the threshold. */
if (!mp_.no_dyn_threshold
&& chunksize_nomask (p) > mp_.mmap_threshold
&& chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX)
{
mp_.mmap_threshold = chunksize (p);
mp_.trim_threshold = 2 * mp_.mmap_threshold;
LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
mp_.mmap_threshold, mp_.trim_threshold);
}
munmap_chunk (p);
}
else
{
MAYBE_INIT_TCACHE ();
/* Mark the chunk as belonging to the library again. */
(void)tag_region (chunk2mem (p), memsize (p));
ar_ptr = arena_for_chunk (p);
_int_free (ar_ptr, p, 0);
}
__set_errno (err);
}
libc_hidden_def (__libc_free)
_int_free
_int_free kuanza
Inaanzia na ukaguzi kadhaa kuhakikisha:
- kiashiria imepangiliwa, au kusababisha kosa
free(): invalid pointer
- ukubwa sio chini ya kiwango cha chini na pia ukubwa umepangiliwa, au kusababisha kosa:
free(): invalid size
_int_free kuanza
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4493C1-L4513C28
#define aligned_OK(m) (((unsigned long) (m) &MALLOC_ALIGN_MASK) == 0)
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
INTERNAL_SIZE_T size; /* its size */
mfastbinptr *fb; /* associated fastbin */
size = chunksize (p);
/* Little security check which won't hurt performance: the
allocator never wraps around at the end of the address space.
Therefore we can exclude some size values which might appear
here by accident or by "design" from some intruder. */
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
malloc_printerr ("free(): invalid pointer");
/* We know that each chunk is at least MINSIZE bytes in size or a
multiple of MALLOC_ALIGNMENT. */
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
malloc_printerr ("free(): invalid size");
check_inuse_chunk(av, p);
_int_free tcache
Kwanza itajaribu allocate chunk hii katika tcache husika. Hata hivyo, ukaguzi kadhaa hufanywa kabla. Itapitia chunks zote za tcache kwenye index ile ile kama chunk iliyofutwa na:
- If there are more entries than
mp_.tcache_count
:free(): too many chunks detected in tcache
- If the entry is not aligned:
free(): unaligned chunk detected in tcache 2
- if the freed chunk was already freed and is present as chunk in the tcache:
free(): double free detected in tcache 2
Ikiwa yote yatakwenda sawa, chunk inaongezwa kwenye tcache na function inarudi.
_int_free tcache
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4515C1-L4554C7
#if USE_TCACHE
{
size_t tc_idx = csize2tidx (size);
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
/* Check to see if it's already in the tcache. */
tcache_entry *e = (tcache_entry *) chunk2mem (p);
/* This test succeeds on double free. However, we don't 100%
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
if (__glibc_unlikely (e->key == tcache_key))
{
tcache_entry *tmp;
size_t cnt = 0;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = REVEAL_PTR (tmp->next), ++cnt)
{
if (cnt >= mp_.tcache_count)
malloc_printerr ("free(): too many chunks detected in tcache");
if (__glibc_unlikely (!aligned_OK (tmp)))
malloc_printerr ("free(): unaligned chunk detected in tcache 2");
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}
}
if (tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (p, tc_idx);
return;
}
}
}
#endif
_int_free fast bin
Anza kwa kukagua kwamba ukubwa unafaa kwa fast bin na kuangalia kama inawezekana kuuweka karibu na top chunk.
Kisha, ongeza chunk iliyofutwa juu ya fast bin huku ukifanya ukaguzi ufuatao:
- Ikiwa ukubwa wa chunk ni batili (mkuu sana au mdogo sana) itasababisha:
free(): invalid next size (fast)
- Ikiwa chunk iliyoongezwa ilikuwa tayari top ya fast bin:
double free or corruption (fasttop)
- Ikiwa ukubwa wa chunk iliyo juu ni tofauti na ukubwa wa chunk tunayoongeza:
invalid fastbin entry (free)
_int_free Fast Bin
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4556C2-L4631C4
/*
If eligible, place chunk on a fastbin so it can be found
and used quickly in malloc.
*/
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
/*
If TRIM_FASTBINS set, don't place chunks
bordering top into fastbins
*/
&& (chunk_at_offset(p, size) != av->top)
#endif
) {
if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
<= CHUNK_HDR_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av->system_mem, 0))
{
bool fail = true;
/* We might not have a lock at this point and concurrent modifications
of system_mem might result in a false positive. Redo the test after
getting the lock. */
if (!have_lock)
{
__libc_lock_lock (av->mutex);
fail = (chunksize_nomask (chunk_at_offset (p, size)) <= CHUNK_HDR_SZ
|| chunksize (chunk_at_offset (p, size)) >= av->system_mem);
__libc_lock_unlock (av->mutex);
}
if (fail)
malloc_printerr ("free(): invalid next size (fast)");
}
free_perturb (chunk2mem(p), size - CHUNK_HDR_SZ);
atomic_store_relaxed (&av->have_fastchunks, true);
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);
/* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */
mchunkptr old = *fb, old2;
if (SINGLE_THREAD_P)
{
/* Check that the top of the bin is not the record we are going to
add (i.e., double free). */
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
p->fd = PROTECT_PTR (&p->fd, old);
*fb = p;
}
else
do
{
/* Check that the top of the bin is not the record we are going to
add (i.e., double free). */
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
old2 = old;
p->fd = PROTECT_PTR (&p->fd, old);
}
while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2))
!= old2);
/* Check that size of fastbin chunk at the top is the same as
size of the chunk that we are adding. We can dereference OLD
only if we have the lock, otherwise it might have already been
allocated again. */
if (have_lock && old != NULL
&& __builtin_expect (fastbin_index (chunksize (old)) != idx, 0))
malloc_printerr ("invalid fastbin entry (free)");
}
_int_free hitimisho
Ikiwa chunk bado haijawekwa kwenye bin yoyote, ita _int_free_merge_chunk
_int_free hitimisho
/*
Consolidate other non-mmapped chunks as they arrive.
*/
else if (!chunk_is_mmapped(p)) {
/* If we're single-threaded, don't lock the arena. */
if (SINGLE_THREAD_P)
have_lock = true;
if (!have_lock)
__libc_lock_lock (av->mutex);
_int_free_merge_chunk (av, p, size);
if (!have_lock)
__libc_lock_unlock (av->mutex);
}
/*
If the chunk was allocated via mmap, release via munmap().
*/
else {
munmap_chunk (p);
}
}
_int_free_merge_chunk
Kazi hii itajaribu kuunganisha chunk P ya SIZE bytes na majirani zake. Itaweka chunk iliyotokana kwenye unsorted bin list.
Baadhi ya ukaguzi hufanywa:
- Ikiwa chunk ni top chunk:
double free or corruption (top)
- Ikiwa next chunk iko nje ya mipaka ya arena:
double free or corruption (out)
- Ikiwa chunk haisemwi kuwa imetumiwa (katika
prev_inuse
kutoka kwa next chunk):double free or corruption (!prev)
- Ikiwa next chunk ina size ndogo sana au kubwa kupita kiasi:
free(): invalid next size (normal)
- Ikiwa previous chunk haijatumiwa, itajaribu consolidate. Lakini, ikiwa
prev_size
inatofautiana na size iliyotajwa katika previous chunk:corrupted size vs. prev_size while consolidating
_int_free_merge_chunk code
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4660C1-L4702C2
/* Try to merge chunk P of SIZE bytes with its neighbors. Put the
resulting chunk on the appropriate bin list. P must not be on a
bin list yet, and it can be in use. */
static void
_int_free_merge_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T size)
{
mchunkptr nextchunk = chunk_at_offset(p, size);
/* Lightweight tests: check whether the block is already the
top block. */
if (__glibc_unlikely (p == av->top))
malloc_printerr ("double free or corruption (top)");
/* Or whether the next chunk is beyond the boundaries of the arena. */
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av->top + chunksize(av->top)), 0))
malloc_printerr ("double free or corruption (out)");
/* Or whether the block is actually not marked used. */
if (__glibc_unlikely (!prev_inuse(nextchunk)))
malloc_printerr ("double free or corruption (!prev)");
INTERNAL_SIZE_T nextsize = chunksize(nextchunk);
if (__builtin_expect (chunksize_nomask (nextchunk) <= CHUNK_HDR_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
malloc_printerr ("free(): invalid next size (normal)");
free_perturb (chunk2mem(p), size - CHUNK_HDR_SZ);
/* Consolidate backward. */
if (!prev_inuse(p))
{
INTERNAL_SIZE_T prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
}
/* Write the chunk header, maybe after merging with the following chunk. */
size = _int_free_create_chunk (av, p, size, nextchunk, nextsize);
_int_free_maybe_consolidate (av, size);
}
Vidokezo vya Mshambuliaji na mabadiliko ya hivi karibuni (2023–2025)
- Safe-Linking in tcache/fastbins:
free()
inahifadhifd
pointer ya singly-linked lists kwa kutumia macroPROTECT_PTR(pos, ptr) = ((size_t)pos >> 12) ^ (size_t)ptr
. Hii inamaanisha kwamba kutengeneza fake next pointer kwa tcache poisoning kunahitaji mshambuliaji kujua anwani ya heap (mfano, leakchunk_addr
, kisha tumiachunk_addr >> 12
kama ufunguo wa XOR). Tazama maelezo zaidi na PoCs kwenye ukurasa wa tcache hapa chini. - Tcache double-free detection: Kabla ya kusukuma chunk kwenye tcache,
free()
inakagua per-entrye->key
dhidi ya per-threadtcache_key
na inapitia bin hadimp_.tcache_count
kutafuta duplicates, ikasitisha kwafree(): double free detected in tcache 2
inapopatikana. - Recent glibc change (2.42): tcache imekua ikiruhusu chunks kubwa zaidi, zinadhibitiwa na tunable mpya
glibc.malloc.tcache_max_bytes
.free()
sasa itajaribu kuhifadhi kwenye cache freed chunks hadi kikomo hicho cha byte (mmapped chunks hazihifadhiwi kwenye cache). Hii inapunguza mara ngapi frees zinaangukia katika unsorted/small/large bins kwenye mifumo ya kisasa.
Quick crafting of a safe-linked fd (for tcache poisoning)
# Given a leaked heap pointer to an entry located at &entry->next == POS
# compute the protected fd that points to TARGET
protected_fd = TARGET ^ (POS >> 12)
- Kwa mwendo wa kina wa tcache poisoning (na mipaka yake chini ya safe-linking), tazama:
Kufanya frees ziguse unsorted/small bins wakati wa utafiti
Wakati mwingine unataka kuepuka tcache kabisa katika maabara ya ndani ili kuona tabia ya klasiki ya _int_free
(unsorted bin consolidation, n.k.). Unaweza kufanya hivyo kwa GLIBC_TUNABLES:
# Disable tcache completely
GLIBC_TUNABLES=glibc.malloc.tcache_count=0 ./vuln
# Pre-2.42: shrink the maximum cached request size to 0
GLIBC_TUNABLES=glibc.malloc.tcache_max=0 ./vuln
# 2.42+: cap the new large-cache threshold (bytes)
GLIBC_TUNABLES=glibc.malloc.tcache_max_bytes=0 ./vuln
Usomaji unaohusiana ndani ya HackTricks:
- Tabia ya First-fit/unsorted na mbinu za overlap:
- Double-free primitives na ukaguzi wa kisasa:
Onyo kuhusu hooks: Mbinu za jadi za ku-overwrite
__malloc_hook
/__free_hook
hazifanyi kazi kwenye glibc ya kisasa (≥ 2.34). Ikiwa bado unaziona katika write-ups za zamani, badilisha kwa malengo mbadala (IO_FILE, exit handlers, vtables, etc.). Kwa maelezo ya msingi, angalia ukurasa kuhusu hooks katika HackTricks.
WWW2Exec - __malloc_hook & __free_hook
Marejeleo
- GNU C Library – NEWS ya 2.42 (allocator: larger tcache via tcache_max_bytes, mmapped chunks are not cached) https://www.gnu.org/software/libc/NEWS.html#2.42
- Safe-Linking explanation and internals (Red Hat Developer, 2020) https://developers.redhat.com/articles/2020/05/13/new-security-hardening-gnu-c-library
tip
Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.