コンテンツにスキップ

認証と Cookie

ログインが必要なケースのパターンです。

res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'admin'),
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
)

HttpUsername / HttpPassword を set しておくと、401 時の再送で Basic / Digest / NTLM などが選ばれる経路もあります。明示したいときは authorization か preferred_auth を使います。

res = send_request_cgi(
'method' => 'GET',
'uri' => '/',
'username' => datastore['HttpUsername'],
'password' => datastore['HttpPassword'],
'preferred_auth' => 'Digest'
)

preferred_auth の例: Basic、Digest、NTLM、Kerberos、Negotiate、None。

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::Unreachable, "#{peer} - no response") if res.nil?
fail_with(Failure::NoAccess, "login failed: #{res.code}") unless res.code == 302
# 以降は cookie_jar が自動添付される
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'dashboard')
)

example_webapp.rb も同じ流れです。Basic とフォームを同時に載せる例もあります。

'authorization' => basic_auth(user, pass),
'keep_cookies' => true,
'vars_post' => { 'username' => user, 'password' => pass }

公開登録があるアプリでは、新しいアカウントを作成してからログインします。登録応答にセッション Cookie が含まれない場合、keep_cookies はログイン時だけで足ります。保存された Cookie は後続のリクエストに自動で添付されます。

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 }
)
fail_with(Failure::NoAccess, 'login failed') if res.nil?
fail_with(Failure::NoAccess, "login failed: HTTP #{res.code}") unless res.code == 200 || res.redirect?
@authenticated = true
true
end
  • 登録応答に Set-Cookie が含まれるアプリでは、登録 POST にも keep_cookies を付けます。
  • 登録・ログインの成否は応答コードや遷移先で確認します。
  • CSRF が要る場合は次節と同じく、先に GET してトークンを取ります。
  • AutoCheck を使う場合は check と exploit の入口でこの関数を呼びます。@authenticated によって、同じ実行中の登録とログインは繰り返しません。実装例は Exploit の「自己登録 → ログイン → JSON API RCE」を参照してください。

参考モジュール: modules/exploits/unix/webapp/byob_unauth_rce.rb(register_user と login_user を分離)、modules/exploits/linux/http/xplico_exec.rb。

res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'users', 'sign_in')
)
fail_with(Failure::Unreachable, 'no response') if res.nil?
token = res.body[%r{name="authenticity_token" value="([^"]+)"}, 1]
fail_with(Failure::UnexpectedReply, 'CSRF token missing') if token.blank?
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'users', 'sign_in'),
'keep_cookies' => true,
'vars_post' => {
'authenticity_token' => token,
'user[login]' => datastore['USERNAME'],
'user[password]' => datastore['PASSWORD'],
'user[remember_me]' => '0'
}
)

jar を使わず、そのリクエストだけ Cookie を固定します。

res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'admin'),
'cookie' => "PHPSESSID=#{@phpsessid}; role=admin"
)

cookie を渡したリクエストでは、その回に限って jar の自動添付より指定が使われます。

cj = Msf::Exploit::Remote::HTTP::HttpCookieJar.new
host = datastore['RHOST']
cj.add(Msf::Exploit::Remote::HTTP::HttpCookie.new('PHPSESSID', @phpsessid, domain: host, path: '/'))
cj.add(Msf::Exploit::Remote::HTTP::HttpCookie.new('role', 'admin', domain: host, path: '/'))
send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'admin'),
'cookie' => cj
)

domain と path は必須です。値は String です。

cookie_jar.clear
send_request_cgi(
'method' => 'GET',
'uri' => '/',
'expire_cookies' => false
)

別アカウントで再ログインする前に cookie_jar.clear します。

Section titled “手動で Set-Cookie を拾う古典パターン”
res = send_request_cgi(...)
cookie = res.get_cookies
send_request_cgi(
'method' => 'GET',
'uri' => '/next',
'cookie' => cookie
)

新規モジュールでは keep_cookies の方が短くて済みます。