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

相关链接:

    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 的大小,导致数组访问越界。

时间: 2024-11-05 12:15:41

CVE-2011-2140分析 Adobe Flash Player - MP4 SequenceParameterSetNALUnit Remote Code Execution的相关文章

Install Adobe Flash Player 11.2 on CentOS/RHEL 7/6/5, Fedora 20/19

Adobe Flash Player are very useful for watching videos in web browser online. Without flash player most of the videos will not play in your browser. This article will help you to install Adobe flash player plugin for your browsers in CentOS 6/5, Redh

解决浏览器Adobe Flash Player不是最新版本问题

方法/步骤 首先打开浏览器,登录网址[http://www.adobe.com/cn/],步骤截图如下所示:   点击[菜单],下拉打开的列表,找到并点击[Adobe Flash Player],步骤截图如下所示:   点击页面上的[您是否有其他操作系统或浏览器?]按钮,步骤截图如下所示:   根据自己的情况,先选择操作系统,需要注意的是,在选择Adobe Flash Player的版本时,一定要看准,要选择PPAPI版本的,现在谷歌浏览器已经停止支持NPAPI版本的插件了. 特别提醒,注意把那

安装Ubuntu双系统系列——为Firefox安装Adobe Flash Player

使用环境:OS:Ubuntu 12.04 LTSBrowser: Firefox 12.0Adobe Flash Player: install_flash_player_11_linux.x86_64.tar.gz 安装方法:1. 下载Adobe Flash Player:请从Adobe官方下载,也可以从这个URL下载,这个URL也是官方的链接:http://fpdownload.macromedia.com/get/flashplayer/pdc/11.2.202.235/install_f

Adobe Flash Player - imsoft.cnblogs

Adobe Flash Player是一个跨平台.基于浏览器的应用程序.运行时,它可以跨屏幕和浏览器原汁原味地查看具有表现力的应用程序.内容和视频.Flash Player实现了移动屏幕上的高性能优化,设计为充分利用本机设备能力,从而实现更丰富.更引人入胜的用户体验. Adobe Flash Player 16.0 两大新功能: 1.Windows/Mac PPAPI安装程序 PPAPI也就是Pepper Plugin API,是在原有网景NPAPI(Netscape Plugin API)基础

chrome://plugins 无法打开的解决方法,同时解决“该网页已屏蔽插件-adobe flash player”

chrome打开想要看视频时提示该网页已屏蔽插件-adobe flash player,在网上查了半天说在chrome plugins里面打开就可以了.可是chrome://plugins 无法打开,在chrome标签页输入chrome://plugins提示如下页面:其次chrome://plugins 无法打开已经有一段时间了.在http://www.cnplugins.com/tool/chrome-plugins.html找到答案. 因为从谷歌官方的博客上看,chrome://plugi

谷歌浏览器提示Adobe Flash Player因过期而遭到阻止

解决方法: 1.下载最新版本chrome://plugins/ 到官网Adobe Flash Player 下载最新版本,目前20 https://get.adobe.com/cn/flashplayer/?no_redirect 2.在谷歌浏览器地址输入chrome://plugins/  进入插件管理,可以看到 Adobe Flash Player 并且出现过期提示,点击该行最右边的查看详情,出现多个版本,把旧的版本点停用,然后下面最新的版本就会被启用 3.完成

Ubuntu 14.04 下 Chromium 出现 未安装Adobe Flash Player 问题解决

Ubuntu 14.04 中,其他浏览器在安装Adobe Flash插件后可以播放视频及音乐,但是Chromium浏览器则会提示缺少Adobe Flash 插件. 原因:之前Chromium使用Netscape Plugin API构架来支持Flash,从Ubuntu 14.04开始,Chromium将会停止使用Netscape Plugin API.因为其他浏览器使用的是Flash Player 11.2,所以不会出现这个问题. 解决:为了解决这个问题,我们可以使用Pepper Flash P

ubuntu中 给chrome安装adobe flash player

这个问题也是会普遍遇到的,每次度娘大把信息,各种方法,可视真正能用的却少之又少,就像给chrome安装adobe flash player  只需要下面一条命令即可,无需换源,无需下载及复制. sudo apt-get install pepperflashplugin-nonfree

如何将chrome浏览器中出现的Adobe flash player版本旧无法更新的问题

1. 下载adobe flash player 1) 下载链接:http://www.adobe.com/support/flashplayer/debug_downloads.html 2)  选择带有PPAPI的链接地址进行下载 3)  可右击选择复制链接,然后将其复制到迅雷中,进行迅雷下载(可选) 2. 安装即可