网页***正是由于漏洞的存在,才得以不断繁衍。现在网页***所利用的漏洞主要有两类:一类是逻辑型漏洞,另一类则是溢出型漏洞。逻辑型漏洞的数量比较少,通常都是由利用系统本身提供的一些功能来完成***的下载和执行的;而溢出型漏洞则是利用程序编写中的一些漏洞来获得浏览器的控制权。两者在利用手段上有本质的不同。在本章中,笔者将对两种漏洞分别举例进行说明。
2.2.1 逻辑型漏洞
    BMP网页***是早期比较典型的网页***之一,利用的就是逻辑方面的漏洞。它先把一个EXE文件伪装成一个BMP图片文件,欺骗IE自动下载,再利用网页中的JAVASCRIPT脚本查找客户端的Internet临时文件夹,找到下载后的BMP文件,把它复制到TEMP目录,再编写一个脚本把找到的BMP文件用DEBUG还原成EXE,并把它放到注册表启动项中,在下一次开机时自启动。我们可以看到,在这个过程中,全都是利用Windows系统提供的函数和接口,漏洞就在于早期版本的IE浏览器在Internet域下仍允许Javascript脚本读写本地文件和修改注册表,被恶意的***利用制作网页***。新版本的IE便修补了这个逻辑漏洞,只有在我的电脑域下IE浏览器才有权限执行上述恶意脚本代码。
    2001年,再次发现微软的IE浏览器存在重大的安全缺陷。简单地说,IE在处理MIME头中"Content-Type:"处指定的某些类型时,存在问题,***者可以利用这类缺陷在IE客户端执行任意命令。这个漏洞也是一个典型的逻辑方面的漏洞,IE浏览器在遇到如下EML文件时:
Content-Type: audio/x-wav;
name="hello.bat"
Content-Transfer-Encoding: quoted-printable
Content-ID: <THE-CID>
echo OFF
dir *.*
    尽管该附近被声明为audio/x-wav类型,但是IE或者Outlook浏览器会执行hello.bat这个脚本。这两个漏洞是比较早期的漏洞,都是针对IE5.5、5.0的,感兴趣的读者可以找一台Windows98或者Windows 2000sp0的机器测试一下,自从IE6推出以后这样的漏洞几乎就绝迹了。
    百度是我国最大的搜索引擎供应商,2007年7月份爆出的百度超级搜霸BaiduBar.dll ActiveX控件远程代码执行漏洞也是一个逻辑漏洞,同样是利用了IE这个***平台,不过漏洞不是IE或者Windows系统造成的,而是百度搜霸造成的。漏洞存在于由ActiveX控件"BaiduBar.dll"导出的"DloadDS()"函数中,相关信息如下:
InprocServer32: C:\Program Files\baidu\bar\BaiduBar.dll
ClassID : A7F05EE4-0426-454F-8013-C41E3596E9E9
[id(0x0000001d), helpstring("method DloadDS")]
void DloadDS(
[in] BSTR bstrUrl,
[in] BSTR bstrName,
[in] long lShow);
    如果把bstrUrl设置成某个服务器上的CAB文件,DloadDS()函数会尝试将这个文件下载到TEMP目录并解压,然后执行其中以bstrName命名的文件,由于在这以前没有任何有效的检查措施,因此***者可构造包含***或者间谍程序的CAB文件,然后用DloadDS()函数下载并运行它。因此,有漏洞的百度搜霸相当于一个完美的后门。
同样爆出漏洞的还有Web迅雷,Web讯雷组件的名称:ThunderServer.webThunder.1,可以采用JS代码new ActiveXObject("ThunderServer.webThunder.1");来激活讯雷的组件。其中的关键函数包括:
SetBrowserWindowData:新建浏览器窗口;
SetConfig:设置Web讯雷;
HideBrowserWindow:隐藏浏览器;
AddTask:添加下载任务;
SearchTask:搜索任务,得到任务ID,文件下载状态等详情;
OpenFile:根据任务ID,打开文件。
    ***者利用这一系列的函数,现已经能完成从下载到运行***程序的完整过程,实现了一个完整的网页***功能。这个Web迅雷漏洞的曝光时间是2007年6月。
    类似的逻辑漏洞还不少,这里就不一一列举了,逻辑漏洞主要是由于程序员毫无安全意识造成的,属于很低级的错误。尽管IE浏览器本身逐渐已经没有这样的漏洞了,但很多第三方的ActiveX控件还存在很多的安全隐患。由于决定木桶盛水量多少的是最短的那块木板,而不是最长的那块;故信息安全也应遵循这个木桶原理。
2.2.2 溢出型漏洞
    相对于逻辑型的漏洞,溢出型漏洞的数量就要多得多。这里所说的溢出型漏洞是指利用缓冲区溢出来传播***病毒的网页***,鉴于目前国内网络安全的严峻现状,由于微软对IE浏览器的漏洞修补都比较及时,故大部分网页***都已经开始转而利用一些国产软件的漏洞来***用户。关于缓冲区溢出,笔者在第4章中将会详细介绍。这里先介绍溢出型网马所要用到的一些技术,主要有:ActiveX技术、Shellcode技术、HeapSpray技术。
2.2.3 关于ActiveX
    ActiveX是Microsoft公司提出的一组使用COM(Component Object Model,部件对象模型)使得软件部件在网络环境中进行交互的技术集。它与具体的编程语言无关。作为针对Internet应用开发的技术,ActiveX被广泛应用于Web服务器以及客户端的各个方面。同时,ActiveX技术也被用于创建普通的桌面应用程序。ActiveX控制是OLE控制的更新版本。控制(Control)是建立可编程部件(Component)的主要元素,ActiveX控制可以用于所有支持COM规范的容器中,或者作为Internet控制嵌入到Web页面中。用户访问该页面时,将下载该控制并自动地在本地注册。利用脚本描述语言(Script)可以在控制之间以及客户与服务器之间通过设置属性(Property)、调用方法(Method)和激活事件(Event)进行通信。ActiveX控制与以前的OLE控制相比,具有更少的接口,并且可以没有窗口。
    所有的ActiveX控制都支持IUnknown接口。目前,很多第三方开发商都编制了各式各样的ActiveX控制。在Internet上,有超过1000个ActiveX控制供用户下载使用。在Windows的SYSTEM目录下,保存有很多Windows提供的ActiveX控制。Microsoft Visual C++(以下简称VC)提供的MFC(Microsoft Foundation Classes)控制都是ActiveX控制。利用VBScript或者Microsoft JScript,可以向Web页面中加入可用于交互的ActiveX控制,将数据预处理或者检验过程放在客户端进行,然后将结果传往Web服务器。在一个Web页面中引入ActiveX控件的方法主要有两种:
    *通过Object标签:<object id="gl" classid="clsid:F3E70CEA-956E-49CC-B444-
73AFE593AD7F"></object>
    *利用JavaScript中的New方法:new ActiveXObject("ThunderServer.webThunder.1")
2.2.4 关于Shellcode
    Shellcode这个概念是随着缓冲区溢出而产生的。从概念上来说,Shellcode是一段用来***软件Bug的可重定向的机器码,之所以被叫做Shellcode是因为在早期这一小段机器码都用于启动一个命令行的Shell以便控制肉鸡,但是后来由于Shellcode并不仅仅用于得到一个Shell,还能完成很多其他的功能,例如:下载并执行***、添加一个用户等,所以有人觉得Shellcode这个名词的范围有点小,但是其他名词也得不到大众的普遍认可。
    Shellcode存在于一个进程的内存中,在***者通过栈溢出、堆溢出、或格式化字符串等漏洞获得执行权以后用来完成某一特定功能的一段代码,获得执行权的方法随着系统架构的不同而有所不同,在我们所熟悉的Windows系统下,通常是通过覆盖栈中的返回地址或异常处理例程的地址来获得EIP的控制权的。关于缓冲区溢出,笔者在第4章中将会进行详细介绍。
2.2.5 关于Heap Spray
    这种方法最早是由荷兰***Berend-Jan Wever创造的,究竟什么是Heap Spray呢?仅仅从字面上来理解,就是“堆的扩展”。这一技术用JavaScript脚本创建了很多个String对象,在String对象中写入一个长长的NOP链以及紧接着NOP链的一小段Shellcode。JavaScript的动态运行库会把这些String对象都存储在堆中。由于堆中的数据是不断向内存高址增长的,在大约分配了200MB的内存给这些string对象之后,在50MB~200MB这段内存中,随意取出一个地址,便极有可能落在nop+Shellcode的中间。把某个返回地址覆盖掉,变成这个随意取出的地址之后,我们就能跳到这个NOP链上,最终执行Shellcode。
    使用这种方法有两方面的优点,由于新版本IE浏览器的许多Dll都是使用VS7以上版本编译的,VS7提供了一个新的编译选项/GS用于防范缓冲区溢出问题,如图2.7所示。
图2.7  Visual Studio中的/GS选项
    /GS选项检测改写返回地址的某些缓冲区溢出,这是一种利用不强制缓冲区大小限制的代码的常用技术。这是通过将安全检查插入到已编译代码中完成的。/GS只尝试检测进入返回地址的直接缓冲区溢出。
    通过将函数调用的返回地址传递到堆栈上的调用约定,可以很容易地在计算机上利用缓冲区溢出。例如,x86使用将函数调用的返回地址传递到堆栈上的调用约定。
    对于编译器认为容易出现缓冲区溢出问题的函数,编译器将在堆栈上返回地址之前分配空间。在进入函数时,用安全Cookie(它在模块加载时计算一次)加载分配的空间。然后在退出函数时,调用编译器帮助器以确保Cookie值仍然不变。如果该值发生变化,则可能已经改写返回地址,因此将报告错误并且终止此进程。除非已用_set_security_error_handler提供了备用处理程序,否则将出现警告用户存在潜在安全问题的消息框并调用ExitProcess。
    当静态链接到CRT时,每个程序映像将具有自己的处理程序。当使用动态链接时,每个组件将共享公共处理程序。/GS需要CRT启动代码,故当用/GS编译DLL时将引出问题。安全Cookie的预期值由CRT在CRT_INIT函数中重置。如果函数是用/GS编译(因此具有安全Cookie)的,而它又调用CRT_INIT,则预期的安全Cookie值将更改,并且程序将认为已出现缓冲区溢出。解决方案有如下两个:
    1.不要在调用(或结束调用)CRT_INIT的任何函数中使用数组,例如,改用_alloca;
    2.使CRT正常初始化。不要指定自己的入口点,请改用DllMain(并且不要调用CRT_INIT)。/clr不支持/GS。
    /GS不能抵御所有缓冲区溢出安全***。例如,通过改写到参数区域来进行缓冲区溢出***仍然是有可能的。因此,即使您使用/GS,也应尽量编写安全的代码;也就是说,确保代码没有任何缓冲区溢出。/GS可以保护您的应用程序,不会发生代码中确实存在的缓冲区溢出。
    对于栈溢出,/GS选项的保护非常有效,它能够有效检测返回地址是不是被覆盖。但是对于IE浏览器里发生的溢出,就有可能被绕过,用的正是Heap spray技术。***者首先执行的就是以下这段javascript脚本,将堆填充成大量NOP+Shellcode的形式到0x05050505这个地址。然后在存在栈溢出的函数中构造0x05050505形成的超长字符串覆盖整个堆栈段,在往外覆盖的时候由于触发了写入异常,流程转入了SEH处理。由于整个堆栈都被0x05050505覆盖,而恰恰异常处理的地址就是存放在堆栈中的,所以EIP就会转向0x05050505,在执行了一系列nop以后,最终Shellcode将获得执行权。
<SCRIPT language="javascript">
var heapSprayToAddress = 0x05050505;
//填充最简单的死循环Shellcode
var payLoadCode = unescape("%uFEEB%uFEEB%uFEEB");
//申请每个堆块的大小,确保块和块直接无缝链接
var heapBlockSize = 0x400000;
var payLoadSize = payLoadCode.length * 2;
var spraySlideSize = heapBlockSize - (payLoadSize+0x38);
var spraySlide = unescape("%u9090%u9090");
spraySlide = getSpraySlide(spraySlide,spraySlideSize);
heapBlocks = (heapSprayToAddress - 0x400000)/heapBlockSize;
//利用new方法从堆中分配内存
memory = new Array();
for (i=0;i<heapBlocks;i++)
{
memory[i] = spraySlide + payLoadCode;
}
function getSpraySlide(spraySlide, spraySlideSize)
{
while (spraySlide.length*2<spraySlideSize)
{
spraySlide += spraySlide;
}
spraySlide = spraySlide.substring(0,spraySlideSize/2);
return spraySlide;
}
</script>
    将上述脚本保存为HTML执行以后,笔者的计算机的PF使用率迅速升高,使用调试器OD附加到IE浏览器的进程,可以发现0x05050505地址已经被NOP+Shellcode形式的长字符串覆盖,如图2.8和图2.9所示。
图2.8 堆扩展会大量消耗内存
图2.9  JS已经成功进行堆扩展
    使用堆扩展技术的exploit有一个非常显著的缺点,就是会耗费大量内存;在配置稍低的计算机上将使IE和整个系统的速度显著变慢,甚至使IE浏览器处于假死状态。在这个例子中,扩展到0x05050505就耗费了近200MB内存,假如要扩展到0x0F0F0F0F,那么吃掉的内存会更加的惊人!恐怕还没有等到内存分配完,浏览器已经被等得不耐烦的用户给关闭了。
    堆扩展技术的另外一个缺点就是成功率不稳定。在本例中,由于整个过程我们只打开了一个网页,所以能够成功地将堆扩展到0x05050505。但是如果用户打开过数个大型Web页,里面有很多不同类型的对象,也许堆地址早已分配到0x06060606了,那么这次***肯定失败。
    堆扩展技术的这个缺点同时也是一个优点,因为只要把地址设得高一点,就几乎不会已经被占用了。而这种方法恰恰可以弥补通用性不强的缺点,本来覆盖返回地址针对不同的IE版本、不同的Windows系统需要使用不同的跳转地址;但是堆扩展技术弥补了这个缺点,一个地址就能做到通用。
 
本文节选自电子工业出版社2009年5月出版的 《网页******实战》一书。