CVE-2013-4743漏洞是针对StaticHTTPServer1.0版的本地缓冲区溢出漏洞。由于以后可能还会分享更多带编号的漏洞分析,为免产生困惑,所以先在这里说明漏洞的编号规律。
以CVE-2013-4743为例,CVE意味着CommonVulnerabilitiesandExposures,是一个非常知名的漏洞数据库,中间的2013表示年份,4743表示该漏洞是当年的第几号漏洞。当然,除了CVE漏洞数据库,还有其它的漏洞数据库,譬如OSVDB(OpenSourcedVulnerabilityDatabase)。同一个漏洞在不同数据库中的编号不同,譬如CVE-2013-4743在OSVDB中的编号就为OSVDB-94717。可以看出,常见的漏洞编号均以所属的漏洞数据库的英文开头。如果是微软发布的产品漏洞,则是以“MS”开头,形如MS13-056,意思是2013年的第56号漏洞。
说完了编号,我们再更具体地了解CVE-2013-4743。OSVDB对它的描述原文为“StaticHTTPServercontainsanoverflowconditionthatistriggeredasuser-suppliedinputisnotproperlyvalidatedwhenhandlingmultipleHTTPcommandsorheaders.Thismayallowaremoteattackertocauseabufferoverflow,resultinginadenialofserviceorpotentiallyallowingtheexecutionofarbitrarycode”。简而言之,OSVDB认为它是一个远程(Remote)缓冲区溢出漏洞,可在处理Http请求时触发。这里我要说,虽然OSVDB的名气远比我大,不过对这个漏洞的理解是值得商榷的。
OSVDB在漏洞介绍里引用了EDB-26520,EDB全名ExploitDatabase,是一个面向Exploit的漏洞数据库。EDB-26520对漏洞的描述为“SEHOverflow”,所给出的Exploit也与OSVDB描述相矛盾。我认为EDB的描述是准确的,原因请听我细细道来。
配置文件引发的腥风血雨形如*.ini的配置文件常见得有点不起眼,可谁能想到配置文件也能引发缓冲区溢出?
StaticHTTPServer的作者肯定是没想到,所以在读取配置文件http.ini时,完全没做防护措施。
首先看看读取配置文件时的堆栈截图,如图1所示。读取文件的动作只需要下断ReadFile就能看到,不过断下来要看哪里呢?不同目标的看点不同,既然这里是缓冲区溢出,我们关注一下缓冲区好了。看清楚缓冲区的位置是0x13B0050。缓冲区溢出通常说明缓冲区的大小超过了合法的大小,所以我们再看一眼缓冲区的大小,是0x200。
根据惯例,接下来我们是不是要翻一翻,从缓冲区的开头(0x11B0050)到缓冲区的结尾(注意是0x11B024F),中间有没有函数返回地址?答:不可能有。这个大小为0x200的缓冲区是系统主动分配的堆地址,不会有任何我们想要的内容。
柳暗花明的二次复制
每一个成功的缓冲区溢出,背后往往站着一个顾此失彼的二次复制。有时候我们甚至奇怪,每每就当我们绝望,准备承认缓冲区内没有可以利用的地方时,作者突然来了个二次复制,像是特地在暗夜里点亮一盏明灯,让我们重拾希望。为什么作者要对缓冲区做二次复制呢?难道真的是为缓冲区溢出留后路?其实问题反过来想,就不难理解了。作者需要缓冲区来读取数据,但接下来往往还需要解析读取的数据。就以配置文件http.ini为例,里面有http_utip、http_ubip、max_http_connections等各种各样的配置项,不但需要读取这些配置项,还需要使用些配置项,既然要使用,自然就得进行二次复制,也就是用某个数据结构保存某个需要的配置项的值。这里我们暂且管它叫赋值。
Http.ini配置文件的赋值工作,由以下代码完成:
0040B2B5|/8B06 /mov |mov |ov eax,dwordptr[esi] ex,dwordptr[eax+4] cx,dwordptr[eax+esi+4] 0040B93F 0040B2B7|.|8B4004 0040B2BA|.|8B4C3004 0040B2BE|.|E87C060000|call ; 读取 0040B2C3|.|83F8FF 0040B2C6|.|7422 0040B2C8|.|3B4510 0040B2CB|.|743F 0040B2CD|.|85DB 0040B2CF|.|7403 0040B2D1|.|88041F 000B2D4||8B06 0040B2D6|.|8B4004 0040B2D9|.|8B4C3004 |cmp |je eax,-1 short0040B2EA ;0040B2EA ;0040B30C ;0040B2D4 |mp |je eax,dwordptr[ebp+10] short0040B30C |test |je ebx,ebx short0040B2D4 |mov |mov |mov |mov byteptr[edi+ebx],al eax,dwordptr[esi] eax,dwordptr[eax+4] ex,dwordptr[eax+esi+4] 0040B2DD|.|E824060000|call 0040B906 ; 移动读取指 针 0040B2E2|.|47 |inc |cmp \jb edi 0040B2E3|.|3B7D0C 0040B2E6|.^\72CD e,dwordptr[ebp+C] short0040B2B5 ;0040B2B5
赋值的实际操作发生在0x0040B2D1,通过mov指令循环向以EBX为基地址,以EDI为偏移的内存地址填写1B数据,数据的内容保存在AL中。AL的值由分别位于0x0040B906和0x0040B93F的两个函数,前者负责移动读取指针,后者负责读取内存并将值保存至AL。要找到赋值数据的源头,就需要分析移动指针的方法,代码如下:
0040B939|.40 inc mov eax 0040B93A|.894628 dwordptr[esi+28],eax
指针地址在esi+28,指向内存地址0x011B288,值为0x11B0050,正是刚才ReadFile的缓冲区起始地址,如图2所示。
既然我们知道了赋值数据的来龙,现在只需要搞清去脉,也就是赋值的目标地址。刚才已经分析了,目标地址保存在EBX中,值为0x0012EC54。也就是说,从0x0012EC54开始的内存地址,因为二次复制而成为我们可以利用的第二片缓冲区。还有第三片缓冲区,复制代码如下:
004072D5|/80FA09 004072D8|.|7417 004072DA|.|80FA3D 004072DD|.|7412 004072DF|.|3BC1 004072E1|.|7D0E 004072E|.|88141F 00072E6|.|8A543001 004072EA|.|47 /cmp |je dl,9 sort004072F1 dl,3D ;004072F1 ;004072F1 ;004072F1 |cmp |je short004072F1 eax,ecx |cmp |je |mov |mov |inc |inc |cmp \jnz hort004072F1 byteptr[edi+ebx],dl dl,byteptr[eax+esi+1] edi 004072EB|.|40 eax 004072EC|.|80FA20 004072EF|.^\75E4 dl,20 short004072D5 ;004072D5
这里直接使用了两个mov来执行读取原始地址的数据和填写目标地址数据的复制操作。在这一段代码中,原始地址起始为0x0012EC54,目标地址起始为0x0012FDDC,最大可以覆盖至0x12FF42。
无需返回地址的栈溢出
一般的栈溢出,通常是靠保存在栈中的缓冲区数据淹没同样保存在栈中的函数返回地址,从而让Shellcode获得控制权。栈中还有另一样可以起同样效果的好东西,那就是SEH(结构异常处理)。SEH有点类似我们的扁桃体,设计初衷本来是处理异常,结果某些时候反而会导致异常。
SEH机制简单来说,就是用一条链表将所有的异常处理函数串起来,链表的每个节点包含两个地址,一个是下一个SEH节点的地址,也即链表的Next指针,另一个则是本节点保存的异常处理函数的地址。当发生异常的时候,系统会遍历这条链表,直到找到某一个函数可以处理当前的异常。既然包含有“函数地址”,那么不难猜到利用原理和返回地址类似。我们能够控制的栈地址中是否存在某个SEH节点呢?有一个SEH节点在0x12FF3C,正好落到我们第三片缓冲区的辖区里,如图3所示。
到这里CVE-2013-4743漏洞就分析完了,从过程不难看出,这并不是一个远程溢出漏洞,而是利用本地的配置文件完成栈溢出,利用难度偏大,需要配合一个文件上传漏洞,完成替换配置文件的工作才能生效。
本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法行为。