-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
Add generic memory search post/multi module #18713
Add generic memory search post/multi module #18713
Conversation
cd2c867
to
6d14a24
Compare
20c948c
to
6ca4c11
Compare
results | ||
end | ||
|
||
def memory_search(pid, needle, min_search_len, match_len) |
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.
Maybe have this upstream in a library?
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.
Probably makes sense to move this into the Meterpreter API for sure; Not a blocker for me - but we should do it 👍
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.
For reference, we already have Post methods for Linux memory read and ASCII search (by regex):
metasploit-framework/lib/msf/core/post/linux/process.rb
Lines 29 to 37 in d60e382
def mem_search_ascii(min_search_len, max_search_len, needles, pid: 0) | |
proc_id = session.sys.process.open(pid, PROCESS_READ) | |
matches = proc_id.memory.search(needles, min_search_len, max_search_len) | |
end | |
def mem_read(base_address, length, pid: 0) | |
proc_id = session.sys.process.open(pid, PROCESS_READ) | |
data = proc_id.memory.read(base_address, length) | |
end |
session_processes = session.sys.process.get_processes | ||
session_processes.each do |session_process| | ||
pid, _ppid, name, _path, _session, _user, _arch = *session_process.values | ||
if (::File.fnmatch(process_names_glob, name, ::File::FNM_EXTGLOB) unless process_names_glob.empty?) || (target_pids.include? pid) |
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.
Is there a way to tidy this predicate up to be more human readable? I don't think I've ever seen an if
and an unless
combined together before 😄
end | ||
end | ||
|
||
target_processes |
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 we dump out the processes for the user to see what they could've matched against? 🤔
diff --git a/modules/post/multi/gather/memory_search.rb b/modules/post/multi/gather/memory_search.rb
index 7f3832fd32..ae39ebc1d9 100644
--- a/modules/post/multi/gather/memory_search.rb
+++ b/modules/post/multi/gather/memory_search.rb
@@ -82,13 +82,27 @@ class MetasploitModule < Msf::Post
target_processes = []
session_processes = session.sys.process.get_processes
- session_processes.each do |session_process|
+ session_table = session_processes.to_table
+ session_table.columns.unshift "Matched?"
+ session_table.colprops.unshift(
+ {
+ 'Formatters' => [],
+ 'Stylers' => [::Msf::Ui::Console::TablePrint::CustomColorStyler.new( 'true' => '%grn', 'false' => '%red' )],
+ 'ColumnStylers' => []
+ }
+ )
+ session_table.sort_index += 1
+ session_processes.each.with_index do |session_process, index|
pid, _ppid, name, _path, _session, _user, _arch = *session_process.values
if (::File.fnmatch(process_names_glob, name, ::File::FNM_EXTGLOB) unless process_names_glob.empty?) || (target_pids.include? pid)
target_processes.append session_process
+ session_table.rows[index].unshift "true"
+ else
+ session_table.rows[index].unshift "false"
end
end
+ vprint_status(session_table.to_s)
target_processes
end
end | ||
|
||
result_group_tlvs = result[:response].get_tlvs(::Rex::Post::Meterpreter::Extensions::Stdapi::TLV_TYPE_MEMORY_SEARCH_RESULTS) | ||
if result_group_tlvs.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.
Not a blocker; There's either a bug fix or UX improvements needed for trying to search 64 process memory from a 32 bit process
After migrating to 64 bit process it works fine
[*] Running against the following processes:
notepad.exe (pid: 4748)
[*] Memory Matches for notepad.exe (pid: 4748)
==========================================
Match Address Match Length Match Buffer Memory Region Start Memory Region Size
------------- ------------ ------------ ------------------- ------------------
0x00007FFF200AADE9 127 "password.......Printing........Print background colors and images......Play system sounds......Play so 0x00007FFF1FFB8000 0x00000000001E7000
unds in web pages........"
0x00007FFF200AC47B 127 "password.....Automatic logon only in Intranet zone...Arial...........AppleWebKit/537.36 (KHTML, like G 0x00007FFF1FFB8000 0x00000000001E7000
ecko) Chrome/51.0.2704.79"
[+] Loot stored to: /Users/adfoster/.msf4/loot/20240123124227_default_192.168.123.147_memory.dmp_479367.bin
[+] Loot stored to: /Users/adfoster/.msf4/loot/20240123124227_default_192.168.123.147_memory.dmp_173286.bin
[*] Post module execution completed
Release NotesAdds a new |
We'll circle back to patch up the nits, just wanting to unblock the Meterpreter payload bump merge queue |
This PR addresses comments present here: #18597 (comment)
This PR adds a
multi/post
module that can run against Meterpreter sessions that support thestdapi_sys_process_memory_search
command. The module will search for needles provided by the user in processes that match the provided PID or name glob.Verification
msfconsole
use post/multi/gather/memory_search
set SESSION <Session ID>
set PROCESS_NAMES_GLOB <process_names_regex>
set PROCESS_IDS <Process ID>
set REGEX <regex>
run
Scenarios
Session Setup
I am using the following sessions:
Windows 10 (non-admin) - OpenSSH_9.4p1, OpenSSL 3.1.2 1 Aug 2023
In this scenario, the Windows target is connected to a different host using
ssh.exe
using the passwordmyverysecretpassword
:Windows 10 (non-admin) - Python3 HTTP Server
In this scenario, the Windows target is running the
http.server
module in Python inside Windows Terminal:Ubuntu (root) - Python3 HTTP Server
In this scenario, Meterpreter is ran as root and we are targeting Python's HTTP server module: