15.5K Views
December 12, 19
スライド概要
LINE DEVELOPER DAY 2019における講演資料
#linedevday
XXE、SSRF、安全でないデシリアライゼーション入門 EGセキュアソリューションズ株式会社 代表取締役 徳丸 浩
アジェンダ • XML外部実体参照(XXE)攻撃 • サーバーサイド・リクエスト・フォージェリ(SSRF) • 安全でないデシリアライゼーション © 2016-2019 Hiroshi Tokumaru 2
本日取り上げる脆弱性について • 今日は以下を紹介します – XML外部実体参照(XXE)攻撃 – サーバーサイド・リクエスト・フォージェリ(SSRF) – 安全でないデシリアライゼーション • これらを取り上げる理由 – XXEと安全でないデシリアライゼーションは、 OWASP Top 10 - 2017 で新たにランクインした – SSRFはセキュリティ界隈で非常に熱い – Capital Oneの事件 © 2016-2019 Hiroshi Tokumaru 3
徳丸浩の自己紹介 • 経歴 – 1985年 京セラ株式会社入社 – 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍 – 2008年 KCCS退職、HASHコンサルティング株式会社(現社名:EGセキュアソリューションズ株式会社)設立 • 経験したこと – 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当 – その後、企業向けパッケージソフトの企画・開発・事業化を担当 – 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当 Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始 – 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立ち上げ • 現在 – EGセキュアソリューションズ株式会社 代表 https://www.eg-secure.co.jp/ – 独立行政法人情報処理推進機構 非常勤研究員 https://www.ipa.go.jp/security/ – 著書「体系的に学ぶ 安全なWebアプリケーションの作り方」(2011年3月) 同 第2版が 2018年6月21日発売 「徳丸浩のWebセキュリティ教室 」(2015年10月) – 技術士(情報工学部門) © 2016-2019 Hiroshi Tokumaru 4
LINEに関して私からも発表があります © 2016-2019 Hiroshi Tokumaru 5
https://store.line.me/stickershop/product/9861689 6
XML外部実体参照(XXE; XML External Entity) © 2016-2019 Hiroshi Tokumaru 7
外部実体参照とは
XMLの実体(entity)はXMLの文書型定義中で以下のように宣言する。
宣言した実体は、XML文書内で、&greeting; &external-file;という形(実体参
照)で参照できる。
<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE foo [
<!ENTITY greeting "こんにちは">
<!ENTITY externalfile SYSTEM "external.txt">
]>
<foo>
<hello>&greeting;</hello>
<ext>&externalfile;</ext>
</foo>
<foo>
<hello>こんにちは</hello>
<ext>Hello World</ext>
</foo>
© 2016-2019 Hiroshi Tokumaru
8
攻撃手法と影響 サンプルスクリプトの説明 ・XMLデータをアップロードするフォーム: 4e-024.html <body> XMLファイルを指定してください<br> <form action="/4e3/C4e_023a" method="post"> <textarea name="xml" cols="40" rows="5"> </textarea> <input type="submit"/> </form> </body> © 2016-2019 Hiroshi Tokumaru 9
攻撃手法と影響
サンプルスクリプトの説明
・XMLデータを受け取りXML形式を解析して表示するスクリプト:C4e_023.java
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
String xml = request.getParameter("xml");
Document doc = builder.parse(new InputSource(new StringReader(xml)));
String name = doc.getElementsByTagName("name").item(0).getTextContent();
String address = doc.getElementsByTagName("address").item(0).getTextContent();
© 2016-2019 Hiroshi Tokumaru
10
攻撃手法と影響(正常系) 正常なXMLファイルをアップロード <?xml version="1.0" encoding="utf8"?> <user> <name>安全太郎</name> <address>東京都港区</address> </user> © 2016-2019 Hiroshi Tokumaru 11
外部実体参照によるファイルアクセス
/etc /hostsを外部参照で指定したXMLファイルをアップロード
<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE foo [
<!ENTITY host SYSTEM "/etc/hosts">
]>
<user>
<name>安全太郎</name>
<address>&host;</address>
</user>
© 2016-2019 Hiroshi Tokumaru
12
URL指定のHTTPアクセスによる攻撃(SSRF攻撃)
内部サーバーのURLを外部参照で指定したXMLファイルをアップロード
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE name [
<!ENTITY schedule SYSTEM "http://internal.example.jp/">
]>
<user><name>安全太郎</name>
<address>&schedule;</address></user>
© 2016-2019 Hiroshi Tokumaru
13
脆弱性が生まれる原因と対策 原因 XMLの外部実体参照を使うと、XML中に外部のファイルを流し込むことができるこ とはXMLがもともと持つ機能。従って、XXEはXMLの機能を悪用するものであり、 プログラムにコーディング上のバグがあるというものではない。 各言語共通対策 • XMLの代わりにJSONを用いる 外部から与えられる信頼できないXMLを解析しないことが重要。外部とのデータ交 換にはXMLではなくJSONを使用する事で脆弱性が発生しないようにできる。ただし、 SOAPのようにプロトコルでXMLを使用している場合はこの対策は使えない。 © 2016-2019 Hiroshi Tokumaru 14
脆弱性が生まれる原因と対策(1) Java言語におけるXXE対策 • DTDあるいは外部実体参照を禁止する Javaの場合は多くのXMLパーサにおいて外部実体参照はデフォルトで有 効になっているため、アプリケーション側で対策する必要がある。対策 の1つとして、DTD(Document Type Definition)を禁止する事が挙げら れる。 あるいは、共通対策に挙げたとおり、XMLの代わりにJSONを用いる事 で対策が可能である。ただし、XMLとJSON両方に対応したライブラリ を使う場合は、受け取ったデータがJSONである事の確認を、ContentTypeのチェック等で行う必要がある。 © 2016-2019 Hiroshi Tokumaru 15
JavaによるXXE対策版
public class C4e_023a extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl",
true);
// DTDを禁止する設定
DocumentBuilder builder = factory.newDocumentBuilder();
String xml = request.getParameter("xml");
Document doc = builder.parse(new InputSource(new StringReader(xml)));
// 以下略
© 2016-2019 Hiroshi Tokumaru
16
脆弱性が生まれる原因と対策(2) PHPにおけるXXE対策 ・libxml2のバージョン2.9以降を用いる libxml2のバージョン2.9以降はデフォルトで外部実体参照を停止する設定になっているため、XXE に対して脆弱ではない。ただし、PHP側で外部実体参照を許可する設定にしている場合は例外であ る。下記にPHP側で外部実体参照を許可するスクリプトを示す。 $doc = new DOMDocument(); $doc->substituteRntities = true; // 外部実体参照を許可…XXE脆弱となる $doc->load($_FILES['user']['tmp_name']); ・libxml_disable_entity_loader(true)を呼び出す PHPバージョン5.2.11以降にはlibxml_disable_entity_loaderという関数が用意されており、下記 のように呼び出す事で、libxml2やPHP側の処理内容にかかわらず、外部実体参照を禁止できる。 libxml_disable_entity_loader(true); © 2016-2019 Hiroshi Tokumaru // 外部実体の読み込みを禁止 17
脆弱性が生まれる原因と対策(3) Ruby on RailsにおけるXXE対策 • REXMLを用いてXMLをパースする(詳しくはマニュアル等を参照) • Nokogiriの場合、NOENTオプションを指定しない • ライブラリにより挙動が異なるのでテストしてから使用することを推奨 def xxedo2 xml = params[:xml] doc = REXML::Document.new(xml) @name = doc.elements['//user/name'].text @address = doc.elements['//user/address'].text render 'xxe/xxedo' end © 2016-2019 Hiroshi Tokumaru 18
サーバーサイド・リクエスト・フォージェリ(SSRF) © 2016-2019 Hiroshi Tokumaru 19
SSRFのイメージ 攻撃者からは、公開サーバー(203.0.113.2)にはアクセスできますが、内部のサーバー (192.168.0.5)はファイアウォールで隔離されているため外部から直接アクセスできない。し かし、公開サーバーから内部のサーバーにはアクセスできる想定。 攻撃者は *何らかの方法で* 公開サーバーから内部のサーバーにリクエストを送信することによ り、内部のサーバーを攻撃できる場合がある。これがSSRF攻撃。 © 2016-2019 Hiroshi Tokumaru 20
プレビュー機能でのSSRF攻撃
これは、はてなブックマークのようなソーシャルブックマークの機能のうち、URLを指定してプレ
ビューを表示するというもの(Ruby on Rails)。
class SsrfController < ApplicationController
def index
url = params[:url]
c = Curl::Easy.new(url) # curb (cURLのRubyラッパー)を使用
c.http_get
s = c.body_str
s.force_encoding("UTF-8"); # curbのバグ対策
render html: Sanitize.clean(s, Sanitize::Config::RELAXED).html_safe
# sanitizeは<script>など攻撃可能なタグを取り除くために使用
end
end
本サンプルはスキームやURLのチェックを一切行っていないため、SSRFに対して脆弱になります。
© 2016-2019 Hiroshi Tokumaru
21
プレビュー機能でのSSRF攻撃 下図はAWS EC2上にサンプルを設置して、徳丸のブログ記事を表示した例。 http://ssrf.wasbook.org:3000/ssrf?url=https://blog.tokumaru.org/... © 2016-2019 Hiroshi Tokumaru 22
プレビュー機能でのSSRF攻撃 次に攻撃例。EC2のインスタンスからhttp://169.254.169.254/ にアクセスすると、そのインスタン スの設定情報が読み込めるという機能がEC2にある。 この機能を悪用して、EC2のクレデンシャルを読み込んでみる。 まずは、以下のURLにアクセスしてみる。 /ssrf?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ © 2016-2019 Hiroshi Tokumaru 23
プレビュー機能でのSSRF攻撃 次に、先程のURLの末尾に test-role を追加した以下のURLでアクセスする。 /ssrf?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/test-role © 2016-2019 Hiroshi Tokumaru 24
SSRF脆弱性(CWE-918)とSSRF攻撃 一般的に脆弱性と攻撃手法は1対1の関係になっている。 例)SQLインジェクション攻撃が可能なSQLインジェクションの脆弱性 しかし、SSRF攻撃とSSRF攻撃の原因となる脆弱性は1対多の関係。 例1:SQLインジェクション脆弱性を悪用した任意コマンド実行によるwget、curlを利用したSSRF攻撃 例2:ディレクトリトラバーサル脆弱性の混入箇所に、SSRFの脆弱性も含まれていた © 2016-2019 Hiroshi Tokumaru 25
SSRF脆弱性(CWE-918)とは 以下は、SSRF脆弱性の分類を定義しているCWE-918の冒頭の引用。 The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. 参考訳 Webサーバーは、上流のコンポーネントからURLまたは類似のリクエストを受け取 り、このURLの内容を取得するが、リクエストが想定される送信先に送られること を十分確実にしていない。 要はURLのチェックが不十分なために読まれてはいけないコンテンツを読まれてし まった、ということで、先の例はまさにそのような例になっている。 © 2016-2019 Hiroshi Tokumaru 26
Capital Oneの例 • 独自運用のWAFの設定ミスを悪用したSSRF攻撃 HostヘッダにEC2インスタンスを指定することによる攻撃。 設定ミスの詳細は明らかにされていない。 1億人を超える被害者が出た。 GET / HTTP/1.1 Host: 169.254.169.254 WAF © 2016-2019 Hiroshi Tokumaru EC2インスタンス 27
対策 SSRF(CVE-918)以外によるSSRF攻撃が可能な場合 • SSRF攻撃の原因となる脆弱性の対策 SSRF(CVE-918)によるSSRF攻撃が可能な場合 • スキーム(プロトコル)のチェック • parse_urlによって得られたホスト名やパス名からURLを組み立て直す • 仕様的に可能であれば、外部からURLを受け取らないようにするか、許可された URLの一覧表(ホワイトリスト)によるチェック • ネットワーク的な保護 © 2016-2019 Hiroshi Tokumaru 28
対策 • スキーム(プロトコル)のチェック これがないと、/etc/passwdのようなパス指定でディレクトリトラバーサル攻撃ができる。 url=/etc/passwd © 2016-2019 Hiroshi Tokumaru 29
対策 • 仕様的に可能であれば、外部からURLを受け取らないようにするか、許可された URLの一覧表(ホワイトリスト)によるチェック ホスト名からIPアドレスを求める際にも以下の問題が発生する。 #DNSサーバーが複数のIPアドレスを返す場合の処理の漏れ #IPアドレスの表記の多様性 #IPアドレスチェックとHTTPリクエストのタイミングの差を悪用した攻撃 (TOCTOU脆弱性=DNS Rebinding) #リクエスト先のWebサーバーが、攻撃対象サーバーにリダイレクトする これらに注意しつつIPアドレスチェックを実装する必要がある。 © 2016-2019 Hiroshi Tokumaru 30
参考: DNS Rebinding攻撃: 環境の説明 © 2016-2019 Hiroshi Tokumaru 31
参考: DNS Rebinding攻撃: 正常系 © 2016-2019 Hiroshi Tokumaru 32
参考: DNS Rebinding攻撃: 攻撃の様子 別のIPアドレス © 2016-2019 Hiroshi Tokumaru 33
対策 • ネットワーク的な保護 URLの完全な検証は難しいので、ネットワーク的な保護も有効です。以下は、AWS のドキュメントで推奨されている iptables の設定例。 これにより、「docker0 ブリッジのコンテナがコンテナインスタンスのロールに指 定されている権限にアクセスするのを防止できます」としている。iptablesによる設 定は環境依存なのでご注意を。 sudo iptables --insert FORWARD 1 --in-interface docker+ -destination 169.254.169.254/32 --jump DROP Amazon ECS コンテナインスタンスの IAM ロール - Amazon Elastic Container Service より引用 34
参考: cURLの resolve オプションを用いた対策
url = params[:url]
uri = URI.parse(url)
host = uri.host
ip = TCPSocket.gethostbyname(host)[3] # URLからホスト名を取り出し
if ip == '169.254.169.254'
# ブラックリストチェック
render html: 'Invalid Host'
return
end
c = Curl::Easy.new(params[:url])
c.resolve = ["%s:%d:%s" % [uri.host, uri.port, ip]] # resolve によりIPアドレスを指定
c.http_get
s = c.body_str
s.force_encoding("UTF-8");
render html: Sanitize.clean(s, Sanitize::Config::RELAXED).html_safe
検証済みのIPアドレスで接続を行うことから、DNSリバインディング攻撃を防ぐことができる…はず
© 2016-2019 Hiroshi Tokumaru
35
速報: EC2にてSSRF多層防御が実装された What’s new in IMDSv2 With IMDSv2, every request is now protected by session authentication. A session begins and ends a series of requests that software running on an EC2 instance uses to access the locally-stored EC2 instance metadata and credentials. The software starts a session with a simple HTTP PUT request to IMDSv2. IMDSv2 returns a secret token to the software running on the EC2 instance, which will use the token as a password to make requests to IMDSv2 for metadata and credentials. Unlike traditional passwords, you don’t need to worry about getting the token to the software, because the software gets it for itself with the PUT request. The token is never stored by IMDSv2 and can never be retrieved by subsequent calls, so a session and its token are effectively destroyed when the process using the token terminates. There’s no limit on the number of requests within a single session, and there’s no limit on the number of IMDSv2 sessions. Sessions can last up to six hours and, for added security, a session token can only be used directly from the EC2 instance where that session began. https://aws.amazon.com/jp/blogs/security/defense-in-depth-open-firewalls-reverseproxies-ssrf-vulnerabilities-ec2-instance-metadata-service/ 36
IMDSv2については以下のブログ記事を参照ください https://blog.tokumaru.org/2019/12/defense-ssrf-amazon-ec2-imdsv2.html より引用 © 2016-2019 Hiroshi Tokumaru 37
安全でないデシリアライゼーション(CWE-502) © 2016-2019 Hiroshi Tokumaru 38
安全でないデシリアライゼーション(CWE-502)とは • クッキー等からシリアライズデータを送り込み、任意のオブジェクトを メモリ内に生成 • オブジェクトが破棄されるタイミングでデストラクタが実行される • オブジェクトを巧妙に組み合わせることにより、攻撃を実行 © 2016-2019 Hiroshi Tokumaru 39
安全でないデシリアライゼーションによる任意コード実行(PHP) Foo Bar class Foo { private $func; public function __destruct() { call_user_func($this->func); } } class Bar { private $func; private $args; public function exec() { call_user_func_array($this->func, $this->args); } } © 2016-2019 Hiroshi Tokumaru 40
安全でないデシリアライゼーションによる任意コード実行
:Foo
func:
:配列
0:
1: 'exec'
class Foo {
private $func;
public function __destruct() {
call_user_func($this->func);
}
}
1.
2.
3.
4.
5.
:Bar
func = 'system'
args = ['whoami']
class Bar {
private $func;
private $args;
public function exec() {
call_user_func_array(
$this->func, $this->args);
}
}
Fooオブジェクトのデストラクタが呼ばれる
call_user_func($this->func) が呼ばれる
Barオブジェクトのexecメソッドが呼ばれる
Barオブジェクトのexecメソッド内で、call_user_func_array('system', ['whoami']) が呼ばれる
system('whoami') が呼ばれる
© 2016-2019 Hiroshi Tokumaru
41
Welcart 1.9.3 のオブジェクトインジェクション脆弱性 © 2016-2019 Hiroshi Tokumaru 42
Welcart 1.9.4 をリリースしました【脆弱性の修正】 Welcart 1.9.4 をリリースしました。オブジェクトインジェクション脆弱性の修正などを行い ました。詳細は以下の通りです。 アップグレードを行う場合は、Welcartを停止してからアップグレードを行ってください。 【変更点】 • オブジェクトインジェクション脆弱性の修正 フロントにて、オブジェクトインジェクションと思われる脆弱性が認められました。 過去のすべてのバージョンが対象となります。1.9.4にアップグレードしてください。 放置しますと、サイトに任意のファイルの埋め込まれる可能性があります。 脆弱性に関する修正の差分はこちら https://www.welcart.com/community/archives/83947より引用 43
Welcart 1.9.3 と 1.9.4の差分 典型的な「安全でないデシリアライゼーション」 https://plugins.trac.wordpress.org/changeset?sfp_email=&sfph_mail=&reponame=&new=1728429%40usc-eshop&old=1728428%40usc-e-shop&sfp_email=&sfph_mail= より引用 44
攻撃に利用できる別プラグインのクラス: All-in-one Event Calendar より class Ai1ec_Shutdown_Controller { protected $_preserve; protected $_restorables; protected $_callbacks; public function __destruct() { // replace globals from our internal store $restore = array(); foreach ( $this->_preserve as $name => $class ) { // 略 } // execute callbacks foreach ( $this->_callbacks as $callback ) { call_user_func( $callback ); } // 後略 攻撃に利用可能 } // 後略 } © 2016-2019 Hiroshi Tokumaru 45
攻撃に利用できる別プラグインのクラス: ManageWP Worker より
class MWP_WordPress_HookProxy
{
private $callback;
private $args;
// 省略
public function hook()
{
call_user_func_array($this->callback, $this->args);
}
}
攻撃に利用可能
Demo
© 2016-2019 Hiroshi Tokumaru
46
安全でないデシリアライゼーションの対策 • シリアライズ形式ではなくJSON 形式によりデータを受け渡す • クッキーやhidden パラメータではなくセッション変数など書き換えで きない形でシリアライズ形式のデータを受け渡す • HMACなどの改ざん検知の仕組みを導入してデータが改ざんされてい ないことを確認する © 2016-2019 Hiroshi Tokumaru 47
Thank you © 2016-2019 Hiroshi Tokumaru 48