Skip to content

DEF CON Quals 2023 - nlinks

This is a script that solves the challenge nlinks from DEF CON Quals 2023. Please find the binary executables here.

def get_passsphrase_from_class_1_binaries(previous_flag):
    flag = b""

    d = debugger("CTF/1")
    r = d.run()

    bp = d.breakpoint(0x7EF1, hardware=True, file="binary")

    d.cont()

    r.recvuntil(b"Passphrase:\n")

    # We send a fake flag after the valid password
    r.send(previous_flag + b"a" * 8)

    for _ in range(8):
        # Here we reached the breakpoint
        if not bp.hit_on(d):
            print("Here we should have hit the breakpoint")

        offset = ord("a") ^ d.regs.rbp
        d.regs.rbp = d.regs.r13

        # We calculate the correct character value and append it to the flag
        flag += (offset ^ d.regs.r13).to_bytes(1, "little")

        d.cont()

    r.recvline()

    d.kill()

    # Here the value of flag is b"\x00\x006\x00\x00\x00(\x00"
    return flag

def get_passsphrase_from_class_2_binaries(previous_flag):
    bitmap = {}
    lastpos = 0
    flag = b""

    d = debugger("CTF/2")
    r = d.run()

    bp1 = d.breakpoint(0xD8C1, hardware=True, file="binary")
    bp2 = d.breakpoint(0x1858, hardware=True, file="binary")
    bp3 = d.breakpoint(0xDBA1, hardware=True, file="binary")

    d.cont()

    r.recvuntil(b"Passphrase:\n")
    r.send(previous_flag + b"a" * 8)

    while True:
        if d.regs.rip == bp1.address:
            # Prepare for the next element in the bitmap
            lastpos = d.regs.rbp
            d.regs.rbp = d.regs.r13 + 1
        elif d.regs.rip == bp2.address:
            # Update the bitmap
            bitmap[d.regs.r12 & 0xFF] = lastpos & 0xFF
        elif d.regs.rip == bp3.address:
            # Use the bitmap to calculate the expected character
            d.regs.rbp = d.regs.r13
            wanted = d.regs.rbp
            needed = 0
            for i in range(8):
                if wanted & (2**i):
                    needed |= bitmap[2**i]
            flag += chr(needed).encode()

            if bp3.hit_count == 8:
                # We have found all the characters
                d.cont()
                break

        d.cont()

    d.kill()

    # Here the value of flag is b"\x00\x00\x00\x01\x00\x00a\x00"
    return flag

def get_passsphrase_from_class_3_binaries():
    flag = b""

    d = debugger("CTF/0")
    r = d.run()

    bp = d.breakpoint(0x91A1, hardware=True, file="binary")

    d.cont()

    r.send(b"a" * 8)

    for _ in range(8):

        # Here we reached the breakpoint
        if not bp.hit_on(d):
            print("Here we should have hit the breakpoint")

        offset = ord("a") - d.regs.rbp
        d.regs.rbp = d.regs.r13

        # We calculate the correct character value and append it to the flag
        flag += chr((d.regs.r13 + offset) % 256).encode("latin-1")

        d.cont()

    r.recvline()

    d.kill()

    # Here the value of flag is b"BM8\xd3\x02\x00\x00\x00"
    return flag

def run_nlinks():
    flag0 = get_passsphrase_from_class_3_binaries()
    flag1 = get_passsphrase_from_class_1_binaries(flag0)
    flag2 = get_passsphrase_from_class_2_binaries(flag1)

    print(flag0, flag1, flag2)