CVE-2017-7269—IIS6.0WebDAV远程代码执⾏漏洞分析
漏洞描述:
3⽉27⽇,在Windows 2003 R2上使⽤IIS 6.0 爆出了0Day漏洞(CVE-2017-7269),漏洞利⽤PoC开始流传,但糟糕的是这产品已经停⽌更新了。⽹上流传的poc下载链接如下。
结合上⾯的POC,我们对漏洞的成因及利⽤过程进⾏了详细的分析。在分析过程中,对poc的exploit利⽤技巧感到惊叹,多次使⽤同⼀个漏洞函数触发,⽽同⼀个漏洞同⼀段漏洞利⽤代码却实现不同的⽬的,最终通过ROP⽅式绕过GS的保护,执⾏shellcode。
调试环境:
虚拟机中安装Windows Server 2003企业版,安装iss6.0后,设置允许WebDAV扩展。使⽤的调试器为:windbg:6.7.0005.1
远程代码执⾏效果如下:
由上图可到,漏洞利⽤成功后可以network services权限执⾏任意代码。
漏洞分析:
漏洞函数
漏洞位于ScStoragePathFromUrl函数中,通过代码可以看到,在函数尾部调⽤memcpy函数时,对于拷贝的⽬的地址来⾃于函数的参数,⽽函数的参数为上层函数的局部变量,保存在上层函数的栈空间中。在调⽤memcpy时,没有判断要拷贝的源字符长度,从⽽导致了栈溢出。
通过伪代码更容易看出:
漏洞利⽤:
在POC中,可以看到发送的header中包含两部分<>标签,这会使上⾯的每个循环体都会运⾏两次,为了下⾯的描述⽅便,我们对这两个header的标签部分分别定义为HEAD_A与HEAD_B。
cve漏洞库漏洞利⽤流程:
1. 在HrCheckIfHeader函数中,通过使⽤HEAD_A溢出,使⽤HEAD_B被分配到堆空间地址中。
2. 在HrGetLockIdForPath函数中,再次通过使⽤HEAD_A溢出,使HEAD_B所在的堆地址赋值给局部对象的虚表指针,在该对象在调⽤
函数时,控制EIP。
3. 最终调⽤IEcb类的对象偏移0x24处的函数指针,控制EIP
漏洞利⽤主要在于HrCheckIfHeader函数与函数HrGetLockIdForPath中。
函数HrCheckIfHeader主要功能是对⽤户传递来的Header头进⾏有效性的判定。在函数中HrCheckIfHeader通过了while循环来遍历⽤户输⼊的Header头中的数据。
HrGetLockIdForPath主要功能是对传递来的路径信息进⾏加锁操作。在HrGetLockIdForPath函数中,也是通过while循环来遍历路径信息,同样也对应着两次调⽤漏洞函数。
调试过程:
两次溢出控制EIP
对这4个调⽤漏洞函数的地⽅分别下断:
bp httpext!CParseLockTokenHeader::HrGetLockIdForPath+0x114 ".echo HrGetLockIdForPath_FIRST";
bp httpext!CParseLockTokenHeader::HrGetLockIdForPath+0x14f ".echo HrGetLockIdForPath_SECOND";
bp httpext!HrCheckIfHeader+0x11f ".echo HrCheckIfHeader_FIRST";
bp httpext!HrCheckIfHeader+0x159 ".echo HrCheckIfHeader_SECOND";
调试程序,共会断下6次,我们对这6次断点处漏洞函数在利⽤时的功能进⾏归纳:
第⼀次:
暂停在HrCheckIfHeader _FIRST,对漏洞利⽤没有影响
第⼆次:
断在HrCheckIfHeader _SECOND,此处调⽤漏洞函数的⽬的是为了使⽤HEAD_A标签,来溢出漏洞函数,⽬的是使⽤HEAD_A标签中的堆地址覆盖栈中的地址,此堆地址会在随后使⽤。
运⾏漏洞函数前,
运⾏过漏洞函数后,可以看到栈空间中的0108f90c位置处的内容已经被覆盖成了680312c0,680312c0正是⼀个堆中的地址。
第三次:
暂停在HrCheckIfHeader_FIRST,此时漏洞函数的作⽤是,将HEAD_B标签拷贝到上⾯的堆地址中。本来正常的程序在这⾥会将⽤户传递进来的HEADER拷贝到栈空间中,但在上⾯因为溢出,将HEAD_B标签拷贝到了堆中。可以看到使⽤的堆地址680312c0。
第四次:
暂停到HrCheckIfHeader_FIRST,对漏洞利⽤没有影响
第五次:
HrCheckIfHeader_SECOND,此处调⽤漏洞函数的⽬的是为了使⽤HEAD_A标签,来溢出漏洞函数,⽬的是使⽤HEAD_A标签中的堆地址覆盖栈中的地址,此堆地址会在随后使⽤。溢出AAA db ebp-14 将栈中的地址改成了与堆中的地址 680312c0,在这⾥ebp-14的地址也被覆盖,这个地址在下⾯第六次的溢出时,会赋值给对象指针,在这⾥就控制了ebp_14的值,也就可以控制下⼀步中的对象指针。
第六次:
HrCheckIfHeader_FIRST在这个函数下⾯的⼦⼦函数中会调⽤虚函数,从⽽控制EIP。
总结⼀下,在上⾯六次调⽤处,需要关注的利⽤过程是:
1)第⼆次与第三次处是必须的,因为没有第⼆次处的利⽤,就不会有第三次处的把HEAD_B拷贝到堆中。没有堆中的地址在第六次调⽤时就没法控制虚表指针。所以没有第⼆次的溢出调⽤,就不会有堆中的HEAD_B内存。(本来HEAD_B的归宿是栈空间,就是因为溢出了才把HEAD_B放到了堆空间中)
2)第五次再次把栈溢出,把堆的地址写到了局部变量中,才导致第六次能成功调⽤虚函数。因为第六次调⽤虚函数时,是调⽤的局部变量的虚函数。如果没有第五次断点处的溢出,就⽆法把堆中地址成功的写⼊到局部变量的虚函数中,也就⽆法控制虚函数指针。
由此可以看出,两次对漏洞函数溢出操作,其中⼀次溢出操作(第⼆次断点处)将栈地址改写为堆地址,保证了HEAD_B被写⼊到堆中,另外⼀次溢出操作(第五次断点处)将局部变量对象的指针指向堆。两次溢出代码相同,实现的⽬的却不同,双剑合壁,⿁斧神⼯,巧妙结合实现对EIP的控制。
ROP
控制EIP后,使⽤ROP技术绕过GS的保护。
使⽤SharedUserData的⽅法执⾏⾃定义的函数
来到shellcode处:
Shellcode进⾏⼀次循环解码:
解码完成后,就是长得⽐较漂亮的shellcode了
缓解⽅案:
l 禁⽤ IIS 的 WebDAV 服务
l 使⽤ WAF相关防护设备
l 建议⽤户升级到最新系统 Windows Server 2016。
总结
通过分析可以看到,漏洞原理只是因为没有对拷贝函数的长度做判断,⽽导致了栈溢出。这也提醒⼴⼤程序员们,慎⽤不安全的内存操作函数,在编译代码时开启所有保护。从漏洞利⽤⾓度分析,对于栈溢出,喜闻乐见的利⽤⼿法为修改返回地址,覆盖虚表指针等⽅法,但这种利⽤栈溢出把指针引向堆空间中,在需要的时候,再通过溢出将堆空间中的地址引回到栈空间中的利⽤⼿法确实也是标新⽴异、与众不同,同⼀个漏洞代码处使⽤多次溢出最终实现exploit,即使在分析完成后也对利⽤⼿法回味悠长。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论