Skip to content

Hrúguárásir (e. Heap Exploitation)

Kynning

Hrúguárásir (e. heap exploitation) eru flóknari en stack overflow, en mjög algengar í CTF og raunverulegum veikleikum (e. vulnerabilities).

Þegar malloc() er kallað, gefur libc þér blokk (e. chunk) úr hrúgunni. Þessar blokkur eru raktar í gagnaskipan (e. data structure) sem libc heldur utan um.

Forsendir

Gott er að þekkja Stack/heap minnislíkanið og C forritun áður en þú heldur áfram.


Uppbygging malloc chunk

Hverri malloc blokk fylgir hausgögn (e. metadata):

┌─────────────────┐
│   prev_size     │  8 bæti — stærð fyrri blokkar (ef laus)
├─────────────────┤
│   size | flags  │  8 bæti — stærð + flaggar (P/M/A bits)
├─────────────────┤  ← malloc() skilar bendi HINGAÐ
│                 │
│   notendagögn  │  ← Þetta er það sem þú notar
│                 │
└─────────────────┘

; Næsta blokk byrjar beint á eftir

Flaggar í size-reitnum: - P (PREV_INUSE): fyrri blokk er í notkun - M (IS_MMAP): úthlutað með mmap - A (NON_MAIN_ARENA): notað í þráðum (e. threads)


Laus blokka (e. Free Lists)

Þegar free(p) er kallað, fer blokkin í lista yfir lausar blokkur (e. free list / bin).

libc hefur nokkrar tegundir af bins:

Bin Stærð Lýsing
tcache < ~1032 bæti Hraðinn cache (e. per-thread cache), per-thread
fastbin 16–80 bæti Hraðinn listi, LIFO
smallbin 16–1008 bæti Tvítengdur listi (e. doubly linked list)
largebin > 1008 bæti Stærri blokkir
unsorted bin Allt Tímabundin geymsla áður en flokkun

Hrúguyfirflæði (e. Heap Overflow)

Svipað og stack overflow, en hér skrifum við yfir hausgögn eða notendagögn næstu blokkar á hrúgunni.

char *a = malloc(32);
char *b = malloc(32);  // Beint á eftir a í minni

strcpy(a, long_input);  // Skrifar yfir í b!
┌──────────┐
│  chunk a │  ← Við skrifum hér
│  gögn    │
├──────────┤
│ chunk b  │  ← En við yfirskrifum hingað
│  gögn    │  ← Við getum breytt gögnum eða size/flags
└──────────┘

Dæmi: Breyting á stærð

Ef við yfirskrifum size reit næstu blokkar, getur libc misskilið minnisuppbygginguna og við getum nýtt okkur til að fá blokkur sem skarast (e. overlapping chunks).


Use-After-Free (UAF)

Use-After-Free (notkun eftir losun) á sér stað þegar forrit notar bendi á minni sem hefur þegar verið losað með free().

char *p = malloc(64);
free(p);
// p bendir enn á sama vistfang!
printf("%s\n", p);  // Óskilgreind hegðun (e. undefined behavior)
p[0] = 'A';         // Skrifar á losað minni, hættulegt!

Nýting UAF

  1. Losa blokk A, fer í tcache/bin
  2. Úthluta nýja blokk B af sömu stærð → B fær sama minnið og A hafði
  3. Skrifa í B → þar sem gamli bendirinn A bendir enn á sama stað, getum við stjórnað hvað A les/skrifar
struct User {
    char name[32];
    void (*print_fn)(char*);  // Fallbendi (e. function pointer)
};

User *u = malloc(sizeof(User));
u->print_fn = safe_print;
free(u);

// Árásarmaður fær úthlutað yfir sama minni og skrifar sinn fallbenda
// Þegar kóðinn kallar u->print_fn(u->name) → keyrðu kóði árásarmanns

Double Free

Double free (tvöföld losun) á sér stað þegar free() er kallað tvisvar á sama bendi.

char *p = malloc(64);
free(p);
free(p);  // VILLA! p er þegar laus

Í eldri útgáfum af libc var þetta nýtt til að fá tvær malloc úthlutanir á sama minni, þ.e. tvær blokkur sem sýnast ólíkar en eru á sama vistfangi.

tcache poisoning

Í nýlegri libc (tcache, frá glibc 2.26) geymir laus blokk next bendi (á næstu lausa blokk). Double free leyfir okkur að:

  1. Losa chunk A tvisvar → A er tvisvar í tcache-listanum
  2. Úthluta A, við getum breytt next bendinum
  3. Næsta malloc skilar A aftur, en síðan skilar það hvað sem við skrifuðum í next
# Þetta er mjög einfaldað
# Eftir double free og poisoning:
# malloc() → skilar chunk A
# skrifa: fake_chunk_addr í A->next
# malloc() → skilar chunk A aftur
# malloc() → skilar fake_chunk_addr  ← við stjórnum þessu!

Fastbin dup

Svipað og tcache poisoning en notar fastbin lista:

void *a = malloc(40);
void *b = malloc(40);

free(a);
free(b);
free(a);  // fastbin: a -> b -> a (hringtenging!)

void *p1 = malloc(40);  // = a
void *p2 = malloc(40);  // = b
// Skrifa á p1 → breytir 'a->next'
void *p3 = malloc(40);  // = a aftur
void *p4 = malloc(40);  // = hvað sem við skrifuðum í a->next!

Tcache

Frá glibc 2.26 hefur hvert þráð (e. thread) sitt eigið tcache (per-thread cache). Það geymir allt að 7 lausar blokkur af hverri stærð.

tcache hefur færri öryggisathuganir (e. security checks) en fastbin/smallbin, þetta gerir það auðveldara í nýtingu.

tcache poisoning (nútímalegt)

Frá glibc 2.32 var bætt við safe-linking, þar sem next bendi eru dulkóðuð (e. obfuscated):

stored = (pos >> 12) XOR next

Til að afkóða:

# Ef við getum lesið gildi úr tcache
leak = leaked_next_ptr
heap_key = leak >> 12
real_next = leak ^ heap_key  # Ef heap_key er þekkt


Gagnleg tól (e. Tooling)

# í pwndbg:
heap             # Yfirlit yfir hrúguna
bins             # Sýnir alla bins (tcache, fastbin, osfrv.)
vis_heap_chunks  # Fallegt myndrænt yfirlit
malloc_chunk 0x... # Skoða tiltekna blokk
# pwntools heap hjálparföll
from pwn import *
p = process('./challenge')
# ...
gdb.attach(p, 'heap\nbins\nvis_heap_chunks\nc')

Gátlisti fyrir heap verkefni

  • [ ] Greina minnisstjórnun forritsins: hvaða föll eru notuð?
  • [ ] Leita að UAF, double free, eða heap overflow
  • [ ] Athuga glibc útgáfu: ./libc.so.6strings libc.so.6 | grep "GNU C Library"
  • [ ] Ákveða markmiðið: skrifa á __free_hook, __malloc_hook, GOT, eða return address
  • [ ] Nota heap debug tól í pwndbg (heap, bins, vis_heap_chunks)