码迷,mamicode.com
首页 > 其他好文 > 详细

CVE-2011-2140分析 Adobe Flash Player - MP4 SequenceParameterSetNALUnit Remote Code Execution

时间:2017-07-22 16:52:43      阅读:244      评论:0      收藏:0      [点我收藏+]

标签:adobe   lin   日志   long   写入   根据   获取   count   了解   

相关链接:

    exploit-db:https://www.exploit-db.com/exploits/18437/

    adobe安全公告:http://www.adobe.com/support/security/bulletins/apsb11-21.html

    flashplayer全版本:https://helpx.adobe.com/flash-player/kb/archived-flash-player-versions.html#playerglobal

    mp4格式解析:http://blog.csdn.net/chenchong_219/article/details/44263691

             http://blog.csdn.net/zhuweigangzwg/article/details/17223355

    AVCC:http://blog.csdn.net/romantic_energy/article/details/50508332

    H264:http://blog.csdn.net/xiaojun111111/article/details/40107559

    指数哥伦布编码:http://blog.csdn.net/u012188065/article/details/53590641

    SPS参数解析:http://blog.csdn.net/heanyu/article/details/6205390

漏洞介绍:

    根据安全公告的说明,漏洞影响 10.3.181.36 及之前的版本。结合exploit-db的文章,从上面的链接中下载稍微早一点的版本。

环境介绍:

    (1) XP Professional sp 3    (2) Flash Player 10.3.181.34

 漏洞重现:

    下载后发现多个版本的FlashPlayer,了解后 sa是独立的player,不运行在浏览器上。winax代表ActiveX用于IE浏览器。技术分享

    于是选择安装IE浏览器的FlashPlayer。安装后下载exploit-db上提供的 python 脚本及文章中提到的zip。执行后生成exploit.html 和 exploit.mp4,还需要压缩包中的mediaplayer.swf。

技术分享

    打开exploit.html,用 windbg 附加,允许运行ActiveX控件后,发生内存访问异常在Flash10u.ocx模块如下:

技术分享

技术分享

    在IDA中定位该异常点,发现是一个循环。先输出日志了解循环流程。执行 bp Flash10u+0x5b4e2 ".printf \"eax=%08x and ecx=%08x\\n\",eax,ecx;g",一开始该模块并未加载所以要先执行sxe ld Flash10u ,等到断下后再下断。

技术分享

    日志中eax一直为0,ecx循环递增4。所以定位一下第一个ecx的值和循环变量。技术分享

    首次断下时,ebp为0,循环的条件是 ebp < [esi+4Ch]  ,[esi+4Ch] 为 0x0FFFFFFF。技术分享

技术分享

    ecx是一个栈地址,由于[esi+4Ch]的值过大,导致内存访问异常。怀疑是一个和文件格式有关的漏洞。

技术分享

 

    在网上了解mp4的格式后,用010Editor打开mp4文件,并用 mp4 的模板解析。

技术分享

    定位到脚本中的特殊数据,这一块数据位于moov box -> trak box -> mdia box(模板未分析其中的内容)-> minf box -> stsd box ->avc1 box ->avcC box

技术分享

    在avcC box的数据中,首先是一个 AVC sequence header 结构如下:

技术分享

技术分享

    从偏移0x213开始该结构体,偏移 0x219 开始就是脚本中的异常数据,也是文章标题提到的 SequenceParameterSetNALUnit,这和H264编码有关,一个高度压缩数字视频编解码器标准。

    SequenceParameterSetNALUnit 它是(sps_size +sps)结构,

    # SPSUnit = SPSUnit Len (2 bytes) + NAL Header (1 byte) + profile_idc (1 byte) + Flags and Reserved (1 byte) + levelidc (1 byte) + 
    #            seq_parameter_set_id (variable) + log2_max_frame_num_minus4 (variable) + pic_order_cnt_type = 1 (variable) +
    #           delta_pic_order_always_zero_flag (1 bit) + offset_for_non_ref_pic (num_ref_frames_in_pic_order_cnt_cycle) + offset_for_top_to_bottom_field (variable) + 
    #           num_ref_frames_in_pic_order_cnt_cycle (num_ref_frames_in_pic_order_cnt_cycle) + other bytes

     这里的NAL Header,占一个字节,由三部分组成forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)。0x67  表示序列参数集(SPS)。

技术分享

    

   继续回去调试,定位到循环值 0x0FFFFFFF 的来源[esi+4Ch],对这个函数调用进行下断调试。

技术分享

   edi看起来有点像一个类假设为 classA,第一个成员指向了 SPSUnit,

技术分享

   由于 H264 解码的过程比较复杂,找到错误的代码位置,也不是很确定错误的数据,尝试对当前函数逆向了解流程。

byte g_byteArr[12] = {0,1,3,7,15,31,63,127,255,0,0,0};
class classA
{
    private byte* pUnit;//0x0  指向SPSUnit
    private unsigned long unitMaxIndex; //0x4  SPSUnit的最大索引
    private unsigned long nowIndex;//0x0C 表示待读取字节的偏移
    private unsigned long waitBitCount;//0x10 表示当前字节剩余的待读取的位数
    private unsigned short nowByte;//0x14 当前读取的字节

    public void sub_1005B396(LPVOID arg_0)
    {
        arg_0->member0 = this->getByte();    //获得profile_idc 0x70
        //0x34  flag and reserved
        //001 flag(3bit) 
        this->getBit();
        this->getBit();
        this->getBit();
        //10100 reserved(5bit)
        arg_0->member4 = this->getBitByCount(5);

        arg_0->member8 = this->getByte(); //level_idc   0x32

        //0x74  011  10100     根据前3位解码后的值为2
        this->memberC = this->getUe(); //seq_parameter_set_id  2
        
        arg_0->member10 = 1;    //byte
        arg_0->member14 = 0;
        arg_0->member18 = 0;
        arg_0->member1C = 0;    //byte
        arg_0->member1D = 0;    //byte

        BYTE profile_idc = arg_0->member0;

        if(profile_idc != 0x42 && profile_idc !=0x4D && profile_idc !=0x58)
        {
            //1 0100
            arg_0->member10 = this->getUe(); //chroma_format_idc      0
            if(arg_0->member10 == 3)
                    this->getBit();
            //010 0
            arg_0->member14 = this->getUe(); //bit_depth_luma_minus8         1

            //0x70 0+01110000   00111 0000
            arg_0->member18 = this->getUe();//bit_depth_chroma_minus8        6
            //0 000
            arg_0->member1C = this->getBit(); //lossless_qpprime_flag      0
            //0 00
            arg_0->member1D = this->getBit(); //seq_scaling_matrix_present_flag   0 
            if(arg_0->member1D != 0)
            {
                loc_1005B441
            }
        }
        loc_1005B464
        if(profile_idc != 0x53 && profile_idc != 0x56)
        {
            //00+0x0000AF8888=00+00000000000000001010111110001000100 01000    连续18个0
            //1010111110001000100 -1 = 1010111110001000011 = 0x57c43
            arg_0->member20 = this->getUe();//log2_max_frame_num_minus4   0x00057c43
            //01000    010 00
            arg_0->member10 = this->getUe();//pic_order_cnt_type  0x1

            if(arg_0->member10 == 0)
            {

            }elseif(arg_0->member10 == 1){
                loc_1005B49D
                //0 0
                arg_0->member48 = this->getBit(); //delta_pic_order_always_zero_flag     0
                //0+0x84   010 000100   
                arg_0->member54 = this->getSe(); //offset_for_non_ref_pic       010
                //000100+0x00 =  00010000000000  = 0001000 0000000
                arg_0->member50 = this->getSe(); //offset_for_top_to_bottom_field   000100
                //由于0x0003是防竞争机制所以直接跳过0x03
                //0000000 +0x000004 = 0000000 + 00000000 + 00000000+00000100
                //这个值过大导致后面的循环超出写入范围。
                arg_0->member4C = this->getUe(); //num_ref_frames_in_pic_order_cnt_cycle    0x0FFFFFFF

                int i =0;//ebp
                unsigned long* p = &arg_0;
                while(arg_0->member4C > i)
                {
                    *p = this->getSe(); //offset_for_ref_frame[i]
                    p++;
                    i++;
                }
                //省略........
            }

        }
    }

    /*
        以有符号指数哥伦布熵解码下个值   IDA:sub_1005AA93
    */
    public int getSe()
    {
        //000011111  
        int result = this->getUe();//eax     11110
        int tmp = result;  //ecx
        result++;  //恢复Ue最后一步的减1     11111
        result>>1; //去掉符号位               1111

        if(!(tmp & 0x1))//之前最低位符号位为1
        {
            result = ~result;                
        }
        return result;
    }
    /*
        以无符号指数哥伦布熵算法解码下一个值   IDA:sub_1005AA64
    */
    public unsignd long getUe()
    {
        int i = 0;        //esi
        //得到连续的i个0
        while(getBit() == 0 && i < 32)
        {
            i++;
        }
        //读取之后的N+1位的值,已知下一位是1,再获取后面的N位      
        int r = getBitByCount(i);//再读取
        
        //X-1获取解码后的值
        return (r + (1<<(i&0xFF))-1);
    }
    /*
        得到指定位数的数据   IDA:sub_1005A9C9
        count - 需要读取的位数  
    */
    public unsigned long getBitByCount(int count)
    {
        unsigned long waitBitCount = this->waitBitCount;  //eax
        unsigned long data = 0;          //eax
        unsigned long tmpCount = count;//ebx
        unsigned long result = 0;//edi

        if(tmpCount >= waitBitCount)//需要的位数 >= 当前字节剩余的待读取的位数
        {
            result = g_byteArr[waitBitCount];//  假设waitBitCount = 5 取出0x1f 00011111
            result &= this->nowByte; //edi   得到当前字节未读取的位数
            tmpCount -= waitBitCount;  //tmpCount用于保存还需要读取的位数

            //剩余的待读取位数>=8
            if(tmpCount >= 8)
            {
                count = tmpCount >> 3;  //count用于保存还需要读取的字节数

                do{
                    data = this->getByte();
                    result = (result << 8)|data;
                    tmpCount -= 8;
                    count--;
                }while(count != 0);
            }
            
            if(tmpCount != 0)
            {
                data = this->getByte();
                this->nowByte = data;
                waitBitCount = (8-tmpCount);
                data >> waitBitCount;
                this->waitBitCount = waitBitCount;
                data &= g_byteArr[tmpCount];
                result << waitBitCount;
                result |=data;
            }else{
                this->waitBitCount = 0;
            }
        }else{
            result = this->nowByte;
            waitBitCount -= tmpCount;
            this->waitBitCount = waitBitCount;
            data = g_byteArr[tmpCount];
            result >> waitBitCount;
            result &= data;
        }
        return result;
    }
    /*
        读取下一个bit    IDA:sub_1005A99A
    */
    public int getBit()
    {
        if(this->waitBitCount == 0)
        {
            this->nowByte = getByte() & 0x000000FF;
            this->waitBitCount = 8;
        }
        this->waitBitCount--;
        return (this->nowByte >> this->waitBitCount) & 0x00000001;
    }
    /*
        读取一个字节    IDA:sub_1005A95B
    */
    public BYTE getByte()
    {
        DWORD nowIndex = this->nowIndex;
        
        if(nowIndex >= this->unitMaxIndex)  //判断是否大于最大索引
            return 0;
        BYTE b = *((LPBYTE)(this->pUnit + nowIndex)); //读取一个字节
        nowIndex++;        
        this->nowIndex = nowIndex;   //索引加1 指向下一个字节
        if(b == 0)              //如果读取出来的是0
        {
            this->member8++;
            //判断是否需要跳过下个字节
            if(nowIndex >= this->unitMaxIndex 
            || this->member8 !=2 
            || *((LPBYTE)(this->pUnit + nowIndex)) != 3)//0x0300为NAL语法的防竞争机制
                goto end;
            this->nowIndex++;
        }
        this->member8 = 0;

    end:
        return b;
    }
}

 

     由于未限制 num_ref_frames_in_pic_order_cnt_cycle 的大小,导致数组访问越界。

CVE-2011-2140分析 Adobe Flash Player - MP4 SequenceParameterSetNALUnit Remote Code Execution

标签:adobe   lin   日志   long   写入   根据   获取   count   了解   

原文地址:http://www.cnblogs.com/poc-/p/7211827.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!