コンテンツにスキップ

Auxiliary

auxiliary は ペイロードを実行しない モジュールです。列挙、ログイン試行、設定変更、DoS、server 待ち受けなどがここに入ります。参照は packages/metasploit-framework/modules/auxiliary/ と同梱 wiki How-to-get-started-with-writing-an-auxiliary-module.md です。

共通手順は 共通手順 です。このページは auxiliary 特有の型、操作例、書き方の参考モジュールをまとめます。

条件種別
ペイロードセッションを開くexploit
情報取得・認証確認・管理操作だけで終わるauxiliary
OS コマンドは流せるがペイロードはまだ載せない当面は auxiliary。後で exploit 化できる
ディレクトリ用途
scanner/Msf::Auxiliary::Scanner を使う多ホスト走査
gather/単一対象からの情報収集
admin/設定変更、アカウント作成などの管理操作
dos/DoS
fuzzers/fuzzer
server/待ち受けサーバ
sniffer/sniffer

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

Section titled “書き方の参考になるモジュール”
参考モジュール学べること
auxiliary/exampleActions / PassivePassiveActions / 最小 run
auxiliary/scanner/http/http_versionHttpClient + Scanner + report_service の短い実例
auxiliary/scanner/http/titleHTTP 走査の別例
auxiliary/scanner/ssh/ssh_loginAuthBrute + SSH + 資格情報保存 + セッション作成
auxiliary/scanner/smb/smb_versionSMB mixin + Scanner + 指紋
auxiliary/scanner/smb/smb_loginSMB 認証試行
auxiliary/scanner/smb/smb_enumshares認証後の列挙
auxiliary/scanner/winrm/winrm_loginWinRM ログイン / CreateSession
auxiliary/admin/dcerpc/icpr_certadmin 系・特化 mixin(MsIcpr)の実例
auxiliary/scanner/mssql/mssql_loginDB ログイン scanner
auxiliary/server/socks_proxyserver 型(待ち受け)の使い方

同じ scanner / gather でも、保存先・認証・リクエスト形で骨格が分かれます。

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
end
end

1b. 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_credential
end

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
disconnect
end

2b. 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']
end

2c. パス存在確認だけの軽い 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)
end
end
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include 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 / CreateSession
end

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'
)
end

3c. 単一クレデンシャル確認だけ(辞書なし)

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(...)
end
end

4. 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}")
end

4b. 未認証 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 も検討
end

4c. 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)
end

4d. 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
# 取得処理
end
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include 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
disconnect
end

5b. SMB バージョン(smb_version 系)

Section titled “5b. SMB バージョン(smb_version 系)”
include Msf::Exploit::Remote::SMB::Client
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def run_host(ip)
connect
# 方言ネゴシエーション / OS 情報
report_service(host: ip, port: rport, name: 'smb', info: info_string)
ensure
disconnect
end

利用者は run -j で待ち受けます。自作する場合は TcpServer や専用 mixin を使い、PassiveActions を検討します。

include Msf::Exploit::Remote::HttpServer
def run
# start_service 相当は親に任せる / exploit 型と似た on_request_uri
end
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')
end
use auxiliary/scanner/http/http_version
set RHOSTS 10.10.11.0/24
set THREADS 10
set RPORT 80
run
services -p 80

HTTPS の場合です。

set RPORT 443
set SSL true
run
use auxiliary/scanner/ssh/ssh_login
set RHOSTS 10.10.11.123
set USERNAME svc-user
set PASSWORD '...'
set STOP_ON_SUCCESS true
set CreateSession true
run
sessions -l
creds

辞書の場合です。

unset PASSWORD
set PASS_FILE /usr/share/wordlists/rockyou.txt
set BRUTEFORCE_SPEED 3
run
use auxiliary/scanner/smb/smb_version
set RHOSTS 10.10.11.123
run
use auxiliary/scanner/smb/smb_enumshares
set RHOSTS 10.10.11.123
set SMBUser user
set SMBPass '...'
set SMBDomain .
run
use auxiliary/scanner/winrm/winrm_login
set RHOSTS 10.10.11.123
set USERNAME svc
set PASSWORD '...'
set DOMAIN .
set RPORT 5985
set CreateSession true
run
sessions -l
use auxiliary/server/socks_proxy
set SRVHOST 127.0.0.1
set SRVPORT 1080
run -j
jobs -l
~/.msf4/modules/auxiliary/scanner/http/my_banner.rb
reload_lib -a
use auxiliary/scanner/http/my_banner
set RHOSTS 10.10.11.123
run
型基底 + mixin入口参考
単発 gatherAuxiliary + HttpClient + Reportrun上記ログイン dump
多ホスト scanner+ Scannerrun_hosthttp_version
辞書ログイン+ AuthBrute (+ Scanner)run_hostssh_login
TCP / SMB banner+ Tcp / SMB + Scannerrun_hostsmb_version
待ち受け server+ HttpServer などrun / PassiveActionssocks_proxy
  1. 種別と配置 → auxiliary/<category>/...
  2. 基底と mixin
  3. update_info
  4. register_options
  5. 入口メソッド → run / run_host
  6. 成果の保存
  7. 後片付け
  • Scanner なら run_host。run を二重定義しない。
  • mixin が持つ RHOSTS / THREADS を再登録しない。
  • res.nil? を常に見る。
  • gather と admin はモジュールを分ける。
  • セッションを開きたい段階になったら Exploit へ移す。