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

##
#         Name: Exploit.pm
#       Author: spoonm <ninjatools [at] hush.com>
#      Version: $Revision: 1.44 $
#  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', 'tunnel' ],
  'KeysType' => 'or',
};
my $nopDefaults =
{
  'Keys' => [ ],
  'KeysDefaults' => [ ],
  'KeysType' => 'or',
};
my $encoderDefaults =
{
  'Keys' => [ ],
  'KeysDefaults' => [ ],
  'KeysType' => 'or',
};

sub new {
    my $class = shift;
    my $hash = @_ ? shift : { };
    if ($class ne 'Msf::Exploit') {
        no strict "refs";
        if (defined ${$class . '::info'}) {
            $hash->{'Info'} = ${$class . '::info'};
        }
        if (defined ${$class . '::advanced'}) {
            $hash->{'Advanced'} = ${$class . '::advanced'};
        }
    }
    $hash->{'_InfoDefaults'} = $defaults;

    # $advanced isn't required, but suggested?
    # die "$class didn't provide an 'our \$advanced' structure!" if (!defined $hash->{'Advanced'});
    die "$class didn't provide an 'our \$info' structure!" if (!defined $hash->{'Info'});
  

    my $self = $class->SUPER::new($hash);
    $self->_init_warn();
    return($self);
}

=head1 _init_warn

For the life of this module, add a warn signal handler that prints the warning via the Error mechanism

=cut

sub _init_warn {
    my ($self) = @_;

    my $ref = ref $SIG{'__WARN__'};
    if ($ref) {
        $self->{'__prior_warn'} = $ref;
    }

    $SIG{'__WARN__'} = sub { $self->Error('[*] WARNING: ' . $_[0]); }
}

=head1 DESTROY

Undo the warn signal handler, since our object is going away

=cut

sub DESTROY {
    my ($self) = @_;
    if ($self->{'__prior_warn'}) {
        $SIG{'__WARN__'} = $self->{'__prior_warn'};
    } else {
        delete $SIG{'__WARN__'};
    }
}

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 DisclosureDate { my $self = shift; return $self->Info->{'DisclosureDate'}; }
sub ModuleClass   { my $self = shift; return 'remote'; }

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;
