
###############

##
#         Name: Exploit.pm
#       Author: spoonm <ninjatools [at] hush.com>
#      Version: $Revision: 1.39 $
#  Description: Parent class for exploit Modules, inherits from Module.
#      License:
#
#      This file is part of the Metasploit Exploit Framework
#      and is subject to the same licenses and copyrights as
#      the rest of this package.
#
##

package Msf::Exploit;
use strict;
use base 'Msf::Module';

my $defaults =
{
  'Payload'     => undef,
  'Nop'         =>
    {
      'SaveRegs'  => ['esp', 'ebp'],
    },
  'AutoOpts'    => { },
};

my $payloadDefaults =
{
  'Keys' => [ ],
  'KeysDefaults' => ['noconn', 'reverse', 'bind'],
  'KeysType' => 'or',
};
my $nopDefaults =
{
  'Keys' => [ ],
  'KeysDefaults' => [ ],
  'KeysType' => 'or',
};
my $encoderDefaults =
{
  'Keys' => [ ],
  'KeysDefaults' => [ ],
  'KeysType' => 'or',
};

sub new {
  my $class = shift;
  my $hash = @_ ? shift : { };
  $hash->{'_InfoDefaults'} = $defaults;
  my $self = $class->SUPER::new($hash);
#  $self->SetDefaults($defaults);
  return($self);
}

sub SetDefaults {
  my $self = shift;
  my $defaults = shift;
  $self->SUPER::SetDefaults($defaults);
  if($self->Payload) {
    $self->_Info->{'Payload'} = $self->MergeHashRec($self->Payload, $payloadDefaults);
    $self->_Info->{'Nop'} = $self->MergeHashRec($self->Nop, $nopDefaults);
    $self->_Info->{'Encoder'} = $self->MergeHashRec($self->Encoder, $encoderDefaults);
  }
}

# Exploit Specific Accessors
sub Payload       { my $self = shift; return($self->Info->{'Payload'}); }
sub Nop           { my $self = shift; return($self->Info->{'Nop'}); }
sub Encoder       { my $self = shift; return($self->Info->{'Encoder'}); }
sub Targets       { my $self = shift; return($self->Info->{'Targets'}); }
sub AutoOpts      { my $self = shift; return $self->Info->{'AutoOpts'}; }
sub UseMessage    { my $self = shift; return $self->Info->{'UseMessage'}; }

sub DefaultTarget {
  my $self = shift;
  my $target = $self->Info->{'DefaultTarget'};
  $target = -1 if(!defined($target));
  return($target);
}

sub Prepare {
  my $self = shift;
  $self->ApplyAutoOpts;
}

sub ApplyAutoOpts {
  my $self = shift;
  my $autoOpts = $self->AutoOpts;
  if($autoOpts) {
    foreach my $key (keys(%{$autoOpts})) {
      if(!defined($self->GetVar($key))) {
        $self->PrintDebugLine(5, "AutoOpts setting $key -> " . $autoOpts->{$key});
        $self->SetTempEnv($key, $autoOpts->{$key});
      }
    }
  }
}

# Default fallthrough methods
sub Exploit {
  my $self = shift;
  $self->PrintLine('[*] No exploit method has been defined for this module.');
}

# Return true if no check has been defined
sub Check {
  my $self = shift;
  $self->PrintLine('[*] No check has been implemented for this module');
  return($self->CheckCode('Safe'));
}

sub TargetsList {
  my $self = shift;
  my $targets = $self->Targets;
  my @targets;

  return if(ref($targets) ne 'ARRAY');

  my $i = 0;
  foreach my $target (@{$targets}) {
    $targets[$i++] = $target->[0];
  }
  return(@targets);
}

sub Handler {
  my $self = shift;
  my $sock = shift;
  my $payload = $self->GetLocal('_Payload');
  if($payload) {
    $sock = $sock->Socket if(ref($sock) =~ /(?:Msf|Pex)::Socket/);
    $payload->ChildHandler($sock);
  }
}

# We have this so the module can override it, and make decisions based on arch
# etc (access _Payload)
sub PayloadPrependEncoder {
  my $self = shift;
  return($self->Payload->{'PrependEncoder'});
}
sub PayloadPrepend {
  my $self = shift;
  return($self->Payload->{'Prepend'});
}
sub PayloadAppend {
  my $self = shift;
  return($self->Payload->{'Append'});
}
sub PayloadSpace {
  my $self = shift;
  return($self->Payload->{'Space'});
}
sub PayloadBadChars {
  my $self = shift;
  return($self->Payload->{'BadChars'});
}
sub PayloadMinNops {
  my $self = shift;
  return($self->Payload->{'MinNops'});
}
sub PayloadMaxNops {
  my $self = shift;
  return($self->Payload->{'MaxNops'});
}

sub PayloadKeysParsed {
  my $self = shift;
  return([ Pex::Utils::ParseKeys(
    $self->PayloadKeysDefaults,
    $self->PayloadKeys,
    $self->PayloadKeysType,
  ) ]);
}
sub PayloadKeys {
  my $self = shift;
  return($self->Payload->{'Keys'});
}
sub PayloadKeysType {
  my $self = shift;
  return($self->Payload->{'KeysType'});
}
sub PayloadKeysDefaults {
  my $self = shift;
  return($self->Payload->{'KeysDefaults'});
}

sub NopSaveRegs {
  my $self = shift;
  return($self->Nop->{'SaveRegs'});
}
sub NopKeysParsed {
  my $self = shift;
  return([ Pex::Utils::ParseKeys(
    $self->NopKeysDefaults,
    $self->NopKeys,
    $self->NopKeysType,
  ) ]);
}
sub NopKeys {
  my $self = shift;
  return($self->Nop->{'Keys'});
}
sub NopKeysType {
  my $self = shift;
  return($self->Nop->{'KeysType'});
}
sub NopKeysDefaults {
  my $self = shift;
  return($self->Nop->{'KeysDefaults'});
}

sub EncoderKeysParsed {
  my $self = shift;
  return([ Pex::Utils::ParseKeys(
    $self->EncoderKeysDefaults,
    $self->EncoderKeys,
    $self->EncoderKeysType,
  ) ]);
}
sub EncoderKeys {
  my $self = shift;
  return($self->Encoder->{'Keys'});
}
sub EncoderKeysType {
  my $self = shift;
  return($self->Encoder->{'KeysType'});
}
sub EncoderKeysDefaults {
  my $self = shift;
  return($self->Encoder->{'KeysDefaults'});
}


sub CheckCode {
  my $self = shift;
  my $code = shift;
  my $codes = {
    'Generic'   => -2,  # Generic error encountered during test
    'Connect'   => -1,  # Connection or socket error during test
    'Safe'      =>  0,  # Confirmed to be non-exploitable
    'Detected'  =>  1,  # Service is detected but unable to confirm
    'Appears'   =>  2,  # Version or response indicates vulnerability
    'Confirmed' =>  3,  # Verified that service is indeed vulnerable
  };

  foreach (keys(%{$codes})) {
    return($codes->{$_}) if($_ eq lc($code));
    return($_) if($codes->{$_} eq $code);
  }
  return;
}

# This function does not check StackBottom, so it is your responsibility
# to make sure that it didn't possible increment too far.
# my name is spoon, my code sucks, here is some below
sub StepAddress {
  my $self = shift;
  my $o = $self->MergeHashRec({ @_ }, {
    'StepSize' => $self->GetLocal('StackStep'),
    'Direction' => -1,
    'StepIncrement' => 4,
    'BadChars' => $self->PayloadBadChars,
    'PackData' => 'V',
    'Address' => '', # Pass Me!
  });

  my $ret = $o->{'Address'};
  my $stepSize = $o->{'StepSize'};
  my $prevRet = $ret;
  $ret += $stepSize * $o->{'Direction'};

  while($o->{'Direction'} * ($ret - $prevRet) > 0 && Pex::Text::BadCharCheck($o->{'BadChars'}, pack($o->{'PackData'}, $ret))) {
    $ret -= $o->{'StepIncrement'} * $o->{'Direction'};
  }

  # We went past prevRet
  if($o->{'Direction'} * ($ret - $prevRet) <= 0) {
    $ret += $stepSize * $o->{'Direction'};
    while(Pex::Text::BadCharCheck($o->{'BadChars'}, pack($o->{'PackData'}, $ret))) {
      $ret += $o->{'StepIncrement'} * $o->{'Direction'};
    }
    $self->PrintDebugLine(1, '[!] Warning, had to step ' .
      (abs($prevRet - $ret) - $stepSize) . ' more than StepSize, could miss payload');
  }
  return($ret);
}

sub InitNops {
  my $self = shift;
  my $size = shift;
  my $nops = $self->_MakeNops($size);
  
  if (length($nops) != $size) {
    $self->PrintDebugLine(1, '[*] Failed to initialize nops');
    return;
  }
  
  $self->{'_InitNops'} = $nops;
  return $size;
}

sub _MakeNops {
  my $self = shift;
  my $size = shift;
  my $badChars = @_ ? shift : $self->PayloadBadChars;

  return if($size <= 0);

  my $ui = $self->GetTempEnv('_UI');
  if(!$ui) {
    $self->PrintDebugLine(1, '[*] UI object not in env');
    return;
  }

  my $payload = $self->GetTempEnv('_Payload');
  if(!$ui) {
    $self->PrintDebugLine(1, '[*] Payload object not in env');
    return;
  }

  my ($rcode, $nops) = $ui->MakeNopSled($size, $payload, $badChars);

  if($rcode <= 0) {
    $self->PrintDebugLine(1, '[*] MakeNopSled errored.');
    return;
  }

  # we should now be guaranteed the length($nops) == size, and all is well
  return($nops);
}

sub MakeNops {
  my $self = shift;
  my $size = shift;
  my $nops = $self->_MakeNops($size, @_);
  
  if (length($nops) != $size) {
    $self->PrintDebugLine(1, '[*] MakeNops failed to generate new nops');
    $nops = $self->{'_InitNops'};
	
	while (length($nops) < $size) {
      $nops .= $self->{'_InitNops'};
	}
    
	return substr($nops, length($nops) - $size - 1, $size);
  }
  return $nops;
}
1;
