Auxiliary
auxiliary は ペイロードを実行しない モジュールです。列挙、ログイン試行、設定変更、DoS、server 待ち受けなどがここに入ります。参照は packages/metasploit-framework/modules/auxiliary/ と同梱 wiki How-to-get-started-with-writing-an-auxiliary-module.md です。
共通手順は 共通手順 です。このページは auxiliary 特有の型、操作例、書き方の参考モジュールをまとめます。
いつ auxiliary にするか
Section titled “いつ auxiliary にするか”| 条件 | 種別 |
|---|---|
| ペイロードセッションを開く | exploit |
| 情報取得・認証確認・管理操作だけで終わる | auxiliary |
| OS コマンドは流せるがペイロードはまだ載せない | 当面は auxiliary。後で exploit 化できる |
| ディレクトリ | 用途 |
|---|---|
scanner/ | Msf::Auxiliary::Scanner を使う多ホスト走査 |
gather/ | 単一対象からの情報収集 |
admin/ | 設定変更、アカウント作成などの管理操作 |
dos/ | DoS |
fuzzers/ | fuzzer |
server/ | 待ち受けサーバ |
sniffer/ | sniffer |
書き方の参考になるモジュール
Section titled “書き方の参考になるモジュール”| 参考モジュール | 学べること |
|---|---|
auxiliary/example | Actions / PassivePassiveActions / 最小 run |
auxiliary/scanner/http/http_version | HttpClient + Scanner + report_service の短い実例 |
auxiliary/scanner/http/title | HTTP 走査の別例 |
auxiliary/scanner/ssh/ssh_login | AuthBrute + SSH + 資格情報保存 + セッション作成 |
auxiliary/scanner/smb/smb_version | SMB mixin + Scanner + 指紋 |
auxiliary/scanner/smb/smb_login | SMB 認証試行 |
auxiliary/scanner/smb/smb_enumshares | 認証後の列挙 |
auxiliary/scanner/winrm/winrm_login | WinRM ログイン / CreateSession |
auxiliary/admin/dcerpc/icpr_cert | admin 系・特化 mixin(MsIcpr)の実例 |
auxiliary/scanner/mssql/mssql_login | DB ログイン scanner |
auxiliary/server/socks_proxy | server 型(待ち受け)の使い方 |
同じ scanner / gather でも、保存先・認証・リクエスト形で骨格が分かれます。
1. 最小・Actions
Section titled “1. 最小・Actions”1a. Actions で処理分岐(auxiliary/example)
Section titled “1a. Actions で処理分岐(auxiliary/example)”class MetasploitModule < Msf::Auxiliary def initialize(info = {}) super( update_info( info, 'Name' => 'Sample Auxiliary Module', 'Author' => ['you'], 'License' => MSF_LICENSE, 'Actions' => [ ['List', { 'Description' => 'Enumerate' }], ['Dump', { 'Description' => 'Dump config' }] ], 'DefaultAction' => 'List' ) ) end
def run case action.name when 'List' then do_list when 'Dump' then do_dump end endend1b. Action が 1 つだけの gather(qnap_lfi 系)
Section titled “1b. Action が 1 つだけの gather(qnap_lfi 系)”'Actions' => [ ['Download', { 'Description' => 'Download FILEPATH' }]],'DefaultAction' => 'Download'
def run # FILEPATH を読んで store_loot / create_credentialend2. HTTP scanner
Section titled “2. HTTP scanner”2a. fingerprint + report_service(http_version)
Section titled “2a. fingerprint + report_service(http_version)”def run_host(ip) connect res = send_request_raw('uri' => '/', 'method' => 'GET') fp = http_fingerprint(response: res) print_good("#{ip}:#{rport} #{fp}") if fp report_service(host: rhost, port: rport, name: (ssl ? 'https' : 'http'), info: fp)ensure disconnectend2b. title 取得 + notes + リダイレクト追従(title 系)
Section titled “2b. title 取得 + notes + リダイレクト追従(title 系)”def run_host(target_host) http_opts = { 'uri' => normalize_uri(target_uri.path), 'method' => 'GET', 'query' => datastore['HttpQueryString'] } res = if datastore['FollowRedirect'] send_request_cgi!(http_opts, datastore['HttpClientTimeout'] || 20, datastore['FollowRedirectDepth']) else send_request_cgi(http_opts) end return if res.nil?
title = res.body[/<title>(?<t>[^<]+)/i, :t] print_good("#{target_host} [#{title}]") if datastore['SHOW_TITLES'] && title report_note(host: target_host, port: rport, type: 'http.title', data: { title: title }, update: :unique_data) if datastore['STORE_NOTES']end2c. パス存在確認だけの軽い scanner
Section titled “2c. パス存在確認だけの軽い scanner”def run_host(ip) %w[/admin /login /api/v1].each do |path| res = send_request_cgi('method' => 'GET', 'uri' => path) next unless res && res.code == 200 print_good("#{ip}#{path} => #{res.code}") report_note(host: ip, type: 'http.path', data: { path: path, code: res.code }, update: :unique_data) endend3. 認証ブルート
Section titled “3. 認証ブルート”3a. SSH(ssh_login 系)
Section titled “3a. SSH(ssh_login 系)”include Msf::Auxiliary::AuthBruteinclude Msf::Auxiliary::Scannerinclude Msf::Auxiliary::Reportinclude Msf::Exploit::Remote::SSH::Options
def run_host(ip) cred_collection = build_credential_collection( username: datastore['USERNAME'], password: datastore['PASSWORD'] ) # LoginScanner::SSH#scan! → create_credential_and_login / CreateSessionend3b. SMB(smb_login 系)
Section titled “3b. SMB(smb_login 系)”AuthBrute + SMB Client。成功時に create_credential と create_credential_login を分けて書く例もあります。
def report_creds(ip, port, result) report_service(host: ip, port: port, name: 'smb', proto: 'tcp') credential_core = create_credential(result.to_h.merge( workspace_id: myworkspace_id, module_fullname: fullname )) create_credential_login( core: credential_core, status: Metasploit::Model::Login::Status::SUCCESSFUL, address: ip, port: port, protocol: 'tcp', service_name: 'smb' )end3c. 単一クレデンシャル確認だけ(辞書なし)
Section titled “3c. 単一クレデンシャル確認だけ(辞書なし)”def run_host(ip) # USERNAME/PASSWORD のみ。USER_FILE は使わない ok = try_login(ip, datastore['USERNAME'], datastore['PASSWORD']) if ok create_credential_and_login(...) print_good("#{ip} login ok") else invalidate_login(...) endend4. HTTP / ファイル gather(単一ホスト)
Section titled “4. HTTP / ファイル gather(単一ホスト)”4a. フォームログイン後に config を loot
Section titled “4a. フォームログイン後に config を loot”def run 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: #{res&.code}") unless res && res.code == 302
res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'config.php') ) path = store_loot('example.config', 'text/plain', rhost, res.body, 'config.php') print_good("Loot: #{path}")end4b. 未認証 LFI / 任意ファイル読取(qnap_lfi 系)
Section titled “4b. 未認証 LFI / 任意ファイル読取(qnap_lfi 系)”def run traversal = '../' * datastore['DEPTH'] res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'read'), 'vars_get' => { 'file' => traversal + datastore['FILEPATH'] } ) fail_with(Failure::UnexpectedReply, 'read failed') unless res && res.code == 200 print_line(res.body) if datastore['PRINT'] store_loot('qnap.file', 'text/plain', rhost, res.body, File.basename(datastore['FILEPATH'])) # /etc/shadow なら create_credential も検討end4c. Basic 認証付きでページを取る
Section titled “4c. Basic 認証付きでページを取る”def run res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'secret'), 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) ) fail_with(Failure::NoAccess, 'auth failed') unless res && res.code == 200 store_loot('example.secret', 'text/html', rhost, res.body)end4d. check で機種判定してから gather
Section titled “4d. check で機種判定してから gather”def check res = send_request_cgi('method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'authLogin.cgi')) return CheckCode::Safe unless res && res.code == 200 CheckCode::Appears('QNAP-like device')end
def run fail_with(Failure::NotVulnerable, 'not target') unless check == CheckCode::Appears # 取得処理end5. TCP / SMB scanner
Section titled “5. TCP / SMB scanner”5a. TCP banner
Section titled “5a. TCP banner”include Msf::Exploit::Remote::Tcpinclude Msf::Auxiliary::Scannerinclude Msf::Auxiliary::Report
def run_host(ip) connect banner = sock.get_once(1024, 5).to_s print_good("#{ip}:#{rport} #{banner.strip}") unless banner.empty? report_service(host: ip, port: rport, name: 'unknown', info: banner.strip)ensure disconnectend5b. SMB バージョン(smb_version 系)
Section titled “5b. SMB バージョン(smb_version 系)”include Msf::Exploit::Remote::SMB::Clientinclude Msf::Auxiliary::Scannerinclude Msf::Auxiliary::Report
def run_host(ip) connect # 方言ネゴシエーション / OS 情報 report_service(host: ip, port: rport, name: 'smb', info: info_string)ensure disconnectend6. server 型
Section titled “6. server 型”6a. SOCKS(socks_proxy)
Section titled “6a. SOCKS(socks_proxy)”利用者は run -j で待ち受けます。自作する場合は TcpServer や専用 mixin を使い、PassiveActions を検討します。
6b. 簡易キャプチャ HTTP サーバ
Section titled “6b. 簡易キャプチャ HTTP サーバ”include Msf::Exploit::Remote::HttpServer
def run # start_service 相当は親に任せる / exploit 型と似た on_request_uriend
def on_request_uri(cli, request) print_status("#{cli.peerhost} #{request.method} #{request.uri}") store_loot('http.capture', 'text/plain', cli.peerhost, request.to_s) send_response(cli, 'ok')endHTTP バージョン走査
Section titled “HTTP バージョン走査”use auxiliary/scanner/http/http_versionset RHOSTS 10.10.11.0/24set THREADS 10set RPORT 80runservices -p 80HTTPS の場合です。
set RPORT 443set SSL truerunSSH ログイン
Section titled “SSH ログイン”use auxiliary/scanner/ssh/ssh_loginset RHOSTS 10.10.11.123set USERNAME svc-userset PASSWORD '...'set STOP_ON_SUCCESS trueset CreateSession truerunsessions -lcreds辞書の場合です。
unset PASSWORDset PASS_FILE /usr/share/wordlists/rockyou.txtset BRUTEFORCE_SPEED 3runSMB バージョン / 共有列挙
Section titled “SMB バージョン / 共有列挙”use auxiliary/scanner/smb/smb_versionset RHOSTS 10.10.11.123run
use auxiliary/scanner/smb/smb_enumsharesset RHOSTS 10.10.11.123set SMBUser userset SMBPass '...'set SMBDomain .runWinRM ログイン
Section titled “WinRM ログイン”use auxiliary/scanner/winrm/winrm_loginset RHOSTS 10.10.11.123set USERNAME svcset PASSWORD '...'set DOMAIN .set RPORT 5985set CreateSession truerunsessions -lSOCKS proxy(server 型)
Section titled “SOCKS proxy(server 型)”use auxiliary/server/socks_proxyset SRVHOST 127.0.0.1set SRVPORT 1080run -jjobs -l自作 scanner の読み込み
Section titled “自作 scanner の読み込み”reload_lib -ause auxiliary/scanner/http/my_bannerset RHOSTS 10.10.11.123run型別の組み立て方
Section titled “型別の組み立て方”| 型 | 基底 + mixin | 入口 | 参考 |
|---|---|---|---|
| 単発 gather | Auxiliary + HttpClient + Report | run | 上記ログイン dump |
| 多ホスト scanner | + Scanner | run_host | http_version |
| 辞書ログイン | + AuthBrute (+ Scanner) | run_host | ssh_login |
| TCP / SMB banner | + Tcp / SMB + Scanner | run_host | smb_version |
| 待ち受け server | + HttpServer など | run / PassiveActions | socks_proxy |
共通手順との対応
Section titled “共通手順との対応”- 種別と配置 →
auxiliary/<category>/... - 基底と mixin
- update_info
- register_options
- 入口メソッド →
run/run_host - 成果の保存
- 後片付け
実装時の注意
Section titled “実装時の注意”- Scanner なら
run_host。runを二重定義しない。 - mixin が持つ
RHOSTS/THREADSを再登録しない。 res.nil?を常に見る。- gather と admin はモジュールを分ける。
- セッションを開きたい段階になったら Exploit へ移す。

