CVE-2013-0077 堆溢出分析

    找了好久才发现这个环境比较容易搭建分析...html

  • 环境
    • 系统---Win XP SP3
    • 漏洞程序:QQPlayer 3.7.892.400
    • 出错DLL:quartz.dll 6.5.2600.5512
    • 调试工具:x32db+gflag.exe
  • 过程
    • 首先gflag设置QQPlayer.exe的堆调试属性,"gflag.exe -i QQPlayer.exe +hpa",此处添加堆页检查堆复制中的溢出。
    • 启动QQPlayer.exe,而后将x32dbg附加到QQPlayer进程上调试,使得被调试程序的堆分配机制正常执行,再将poc.m2p文件拖入QQPlayer中,由此x32dbg捕获到错误(EXCEPTION_ACCESS_VIOLATION),并停在rep movsd指令处。
    • 此时查看到当前出错指令位于quartz.dll中,用IDA Pro分析quartz.dll分析,得以下结果。
    • signed int __stdcall sub_7D0706CC(int src_addr, unsigned int count, int a3)
      {
        signed int result; // eax@2
        int v4; // eax@3
        unsigned __int8 v5; // al@3
        unsigned int v6; // ebx@7
        int v7; // ecx@7
        int v8; // ST00_4@7
        unsigned int v9; // eax@7
        signed int v10; // eax@10
      
        if ( *(_BYTE *)(src_addr + 10) & 0x20 )
        {
          v4 = (*(_BYTE *)(src_addr + 6) + ((*(_BYTE *)(src_addr + 5) + (*(_BYTE *)(src_addr + 4) << 8)) << 8)) & 0xFFF;
          *(_DWORD *)a3 = (*(_BYTE *)(src_addr + 6)
                         + ((*(_BYTE *)(src_addr + 5) + ((unsigned int)*(_BYTE *)(src_addr + 4) << 8)) << 8)) >> 12;
          *(_DWORD *)(a3 + 4) = v4;
          v5 = *(_BYTE *)(src_addr + 7);
          if ( (*(_BYTE *)(src_addr + 7) & 0xFu) > 8 )
            v5 &= 0xF7u;
          if ( v5 & 0xF0 && v5 & 0xF )
          {
            v6 = v5;
            v7 = v5 & 0xF;
            *(_QWORD *)(a3 + 16) = dword_7D0707F8[v7];
            v8 = *(_DWORD *)(a3 + 16);
            *(_DWORD *)(a3 + 24) = dword_7D070838[v7];
            *(_DWORD *)(a3 + 28) = MulDiv(v8, 9, 1000);
            v9 = (*(_BYTE *)(src_addr + 10) + ((*(_BYTE *)(src_addr + 9) + ((unsigned int)*(_BYTE *)(src_addr + 8) << 8)) << 8)) >> 6;
            *(_DWORD *)(a3 + 32) = v9;
            if ( v9 == 0x3FFFF )
              *(_DWORD *)(a3 + 32) = 0;
            else
              *(_DWORD *)(a3 + 32) = 400 * v9;
            *(_DWORD *)(a3 + 40) = 2000;
            *(_DWORD *)(a3 + 36) = dword_7D070860[v6 >> 4];
            v10 = (((unsigned int)*(_BYTE *)(src_addr + 11) >> 3) | 32 * (*(_BYTE *)(src_addr + 10) & 0x1F)) << 11;
            *(_DWORD *)(a3 + 8) = v10;
            if ( *(_BYTE *)(src_addr + 11) & 4 )
            {
              if ( v10 > 40960 )
                *(_DWORD *)(a3 + 8) = 40960;
            }
            *(_DWORD *)(a3 + 48) = count;
         //此处qmemcpy为出错指令,将src_addr处的内容复制count字节到堆中,觉得count未检查,致使溢出
         //查看到此处src_addr的起始内容为0x000001B3
            qmemcpy((void *)(a3 + 52), (const void *)src_addr, count);
            result = 1;
          }
          else
          {
            result = 0;
          }
        }
        else
        {
          result = 0;
        }
        return result;
      }

      返回到x32dbg中调试到此函数上,发现src_addr处起始内容为0x000001B3,count为0x000000C3。数据结构

    • 在poc.m2p文件中查找到“0x000001B3”,而且在“0x000001B3”偏移0xC3处后,内容为“0x00000100”,此时咱们并不知道这个十六进制序列有什么特殊含义,暂且看作一个开端标志。以后Google MPEG-2数据结构发现“0x000001B3”为Video Sequence的起始标志,“0x00000100”为Picture Header的起始标志,因此此函数的功能就是将Video Sequence到Picture Header之间的内容复制到堆中,由于堆中空间不够,而致使的溢出错误。ide

    • 最后咱们探究一下溢出的精确长度复制前的堆起始地址为0x027A6F64,出错时为0x027A7000,0x027A7000-0x027A6F64=0x9C,因此此处分配的堆空间大小为0x9C字节,而咱们实际复制的内容有0xC3字节,超出全部空间,致使溢出错误。函数

  • 总结:此处的堆溢出与栈溢出其实基本相近,都是由于复制数目未检查而致使的溢出,只是位于不一样属性地址空间上的不一样溢出而已,因地址空间所具备的特性而不一样;x32dbg等调试器对于堆调试很不友好,没有windbg对堆的调试支持强,是一个后续须要解决的问题,同时此时找到了堆的溢出错误,但是要想达到exp,还有很长一段路...
  • 待续:期待续篇EXP...
相关文章
相关标签/搜索