6.5K Views
May 08, 23
スライド概要
本資料はslideshareに掲載していたスライドを移行したものです。
SSRFについて社内で発表した際の資料です。
本資料は、作成者の勉強の一環として調査・検証したものになります。間違った解釈をしている場合は、ご指摘いただければ幸いです。
都内で働くセキュリティエンジニア
SSRF 基礎 2020/11/4 (社内公開) 2021/5/24 (外部公開) iwama
目次 ● ● ● ● ● はじめに SSRFとは SSRFの脅威 SSRFの対策 ハンズオン (CTF) 2
はじめに この資料で取り扱うのはSSRFの基礎です。 業務的観点、AWS以外のクラウド、WAFバイパスには触れませ ん。 ハンズオンにはDocker+Burp(or Fiddler)が必要です。 是非TRYしてみてね! 3
SSRFとは Server-Side-Request-Forgery(SSRF)とは、 標的サーバから、攻撃者の指定した宛先にリクエスト を送信させることを可能にする脆弱性。 4
OWASP Night SSRF基礎より https://speakerdeck.com/hasegawayosuke/ssrfji-chu?slide=6 5
OWASP Night SSRF基礎より https://speakerdeck.com/hasegawayosuke/ssrfji-chu?slide=7 6
SSRFの脅威 ● 本来外部からアクセスできない内部ネットワークに 対するアクセス ● 内部ネットワークに存在するサーバやクライアント への攻撃 ● (環境がクラウドにある場合)クラウドインスタンス への不正ログイン 7
クラウドメタデータが特に狙われる OWASP Night SSRF基礎より https://speakerdeck.com/hasegawayosuke/ssrfji-chu?slide=7 8
SSRFの攻撃事例(多数あり) Capital One 個人情報漏えい - https://piyolog.hatenadiary.jp/entry/2019/08/06/062154 WYSIWYG型エディタのビデオ挿入でSSRF - https://hackerone.com/reports/643622 oEmbedの機能からSSRF - https://hackerone.com/reports/793704 shopify SSRFからインスタンスのルート取得 - https://hackerone.com/reports/341876 米国国防総省 ポートスキャニング - https://hackerone.com/reports/326040 shopify 細工したSVGで内部サーバに保存されている画像ファイルをリーク - https://hackerone.com/reports/223203 9
SSRFに発展する主な脆弱性 ● LFI,RFI ○ 外部ファイルをインクルード出来る場合、SSRFの脆弱性がある。 ● パストラバーサル ○ ファイルパスだけでなくURLもパース出来る場合、SSRFに発展する。 ● XXE ○ 外部実体参照を利用してSSRFに発展する。 10
コラム: RFIとSSRFの違い ● RFIは外部ファイルをプログラムコードとしてインクルー ドできてしまう。 ● RFIはSSRFの脆弱性を悪用した結果だが、SSRFがRFIになる わけではない。 ● SSRFはサーバにリクエストを送信させることが可能だが、 リクエストによってコード実行させるという意味ではな い。 11
脆弱なコード例(PHP)
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET[“url”]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$res = curl_exec($ch);
curl_close($ch);
$doc = new DOMDocument();
$doc->loadHTML($res);
$anchors = $doc->getElementsByTagName('a');
foreach($anchors as $anchor) {
echo "[-] ".$anchor->nodeValue, PHP_EOL;
}
?>
12
脆弱なコード例(PHP)
<?php
URLを外部から受け取っている。
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET[“url”]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$res = curl_exec($ch);
curl_close($ch);
$doc = new DOMDocument();
$doc->loadHTML($res);
$anchors = $doc->getElementsByTagName('a');
foreach($anchors as $anchor) {
url=http://127.0.0.1を送信する
とサーバ自身にリクエストを送
る。
※ 後続のloadHTML関数により
HTMLコンテンツを返さない場合は
エラーになる
echo "[-] ".$anchor->nodeValue, PHP_EOL;
}
?>
13
脆弱なコード例(Python) import urllib3 from lxml import etree from flask import Flask, request # 省略 @app.route("/", methods=["GET"]) def index(): url = request.args.get("url", type=str) or None if url: http = urllib3.PoolManager() res = http.request('GET', url) root = etree.HTML(res.data) anchors = root.findall(".//a") for anchor in anchors: print("[-] %s" % anchor.text) 14
脆弱なコード例(Python) import urllib3 PHPと同じ from lxml import etree from flask import Flask, request # 省略 @app.route("/", methods=["GET"]) def index(): url = request.args.get("url", type=str) or None if url: http = urllib3.PoolManager() res = http.request('GET', url) root = etree.HTML(res.data) anchors = root.findall(".//a") for anchor in anchors: print("[-] %s" % anchor.text) 15
SSRFの間違った対策
<?php
function validate_url($url) {
$parse_url = parse_url($url);
if ($parse_url["host"] !== '169.254.169.254') {
print("pass");
return $url;
} else {
print("block");
}
}
内部IPアドレスをブロックリス
トに指定する
$url = validate_url($_GET["url"]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
#...
16
SSRFの間違った対策
<?php
function validate_url($url) {
$parse_url = parse_url($url);
if ($parse_url["host"] !== '169.254.169.254') {
print("pass");
return $url;
} else {
print("block");
}
}
簡単に回避される
# validate_url("http://169.254.169.254");
# block
# validate_url("http://2852039166");
# pass
$url = validate_url($_GET["url"]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
#...
17
SSRFの間違った対策
<?php
function validate_url($url) {
$parse_url = parse_url($url);
if ($parse_url["host"] !== '169.254.169.254') {
print("pass");
return $url;
} else {
print("block");
}
}
ワイルドカードDNSサービス
nip.io など
#
validate_url("http://magic-169-254-169-254.
nip.io");
# pass
$url = validate_url($_GET["url"]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
#...
18
SSRFの間違った対策
<?php
function validate_url($url) {
$parse_url = parse_url($url);
if ($parse_url["host"] !== '169.254.169.254') {
print("pass");
return $url;
} else {
print("block");
}
}
$url = validate_url($_GET["url"]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
#...
リダイレクトさせる
※ リダイレクト追従する場合のみ
# validate_url("https://evil.example.com");
# pass
HTTP/2 302
…
location: http://169.254.169.254
他にもDNS Rebindingなどがある
19
SSRFの対策 ● (SSRFに限った話じゃないけど・・・)SSRFに発展する脆 弱性はちゃんと対策する ○ XXE, パストラ―バサル, LFI, RFIなど ● 外部からURLを受け取らない ○ URLに対応するIDの組み合わせをサーバ側にて保持し、パラメー タに渡される値はIDのみを受け付けるようにする 20
任意のURLを受け取る場合 根本的な対策は難しい。以下の方法が緩和策 ● (ネットワークレベルで)ネットワーク保護 ○ 内部ネットワークやホストは参照できないようにする ● AWSならIMDSv2を利用する ○ ○ https://dev.classmethod.jp/articles/ec2-imdsv2-release/ 任意のHTTPヘッダーやリクエストメソッドが送れる状況では根本的 対策にはならない。 ■ gopher プロトコル使ったバイパス ■ https://speakerdeck.com/hasegawayosuke/ssrfji-chu?slide= 18 21
SSRFの対策はなぜ難しい? ● URLのパースは難しい。 ○ ○ ○ 過去にバイパスできるケースが報告されている。 A New Era of SSRF - Exploiting URL Parser in Trending Programming Languages! https://gist.github.com/mala/f86a849d15b2d60d7119e05f33b4 b885 22
もっとSSRFを知る ● ● ● ● AWS以外のクラウドメタデータの取得方法を知る blind SSRFの手法を知る フィルターバイパスの手法を知る URLやファイルパスなどのパス構造やパス解析処理 (パーサー)を知る 23
参考リンク ● payload チートシート ○ https://github.com/swisskyrepo/PayloadsAllTheThings/ tree/master/Server%20Side%20Request%20Forgery ● Cloud metadata URL リスト ○ https://gist.github.com/mrtc0/60ca6ba0fdfb4be0ba499c 65932ab42e 24
ハンズオン環境を用意しました https://github.com/wild0ni0n/ssrf-practice 必要環境 ● ● Docker docker-compose 動かし方 $ git clone https://github.com/wild0ni0n/ssrf-practice.git $ cd ssrf-practice $ docker-compose up -d 25
ハンズオン F&Q Q なにするの? A Aboutを見てください。FLAGを5つ探してください。 Q FLAGが分からない・・・。 A Hintを用意してます Q クラウドメタデータを窃取してもいいの? A コンテナ内に同じ構成を作ってるだけだから問題ないです! 26