12

PHP で XML API を使用する Web アプリケーションを作成していますが、インライン DOCTYPE 定義に関連する 3 つの特定の脆弱性 (ローカル ファイル インクルージョン、2 次エンティティ ブローアップ、指数エンティティ ブローアップ) が心配です。PHP (5.3) のビルトイン ライブラリを使用したいのですが、これらの影響を受けないようにしたいのです。

libxml_disable_entity_loader を使用して LFI を排除できることがわかりましたが、これは他のエンティティを参照するエンティティを含むインライン ENTITY 宣言には役立ちません。

SimpleXML ライブラリ (SimpleXMLElement、simplexml_load_string など) は、DOM パーサーであり、すべての入力がかなり小さいため、優れています。これにより、xpath を使用して DOM を非常に簡単に操作できるようになります。ENTITY 宣言を停止する方法がわかりません。(可能であれば、すべてのインライン DOCTYPE 定義を無効にできれば幸いです。)

XML パーサー ライブラリ (xml_parser_create、xml_set_element_handler など) を使用すると、エンティティを含む既定のハンドラーを xml_set_default_handler で設定できます。私はそれをハックして、認識されないエンティティに対して単に元の文字列 (つまり、"&ent;") を返すようにすることができます。ただし、このライブラリはイライラさせられます。これは SAX パーサーであるため、多数のハンドラー (最大 9..) を作成する必要があります。

では、組み込みライブラリを使用して DOM のようなオブジェクトを取り出し、これらのさまざまな DoS 脆弱性から身を守ることは可能でしょうか? ありがとう

このページでは、3 つの脆弱性について説明し、.NET を使用していた場合の解決策を提供します: http://msdn.microsoft.com/en-us/magazine/ee335713.aspx

アップデート:

<?php
$s = <<<EOF
<?xml version="1.0?>
<!DOCTYPE data [
<!ENTITY en "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa....">
]>
<data>&en;&en;&en;&en;&en;&en;&en;&en;&en;&en;&en;&en;.....</data>
EOF;
$doc = new DOMDocument();
$doc->loadXML($s);
var_dump($d->lastChild->nodeValue);
?>

私もやってみloadXML($s, LIBXML_NOENT);ました。どちらの場合も、最終的に 300 MB 以上をダンプします。まだ足りないものはありますか?

4

2 に答える 2

14

注:以下の XML チャンクを含むファイルを使用してテスト ケースを作成する場合、編集者もこれらの攻撃を受けやすく、フリーズ/クラッシュする可能性があることを想定してください。

億の笑い

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
  <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
  <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
  <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
  <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
  <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

ロード時:

致命的: #89: エンティティ参照ループを検出しました 1:7
... (プラス 6 回同じ = 上記の合計 7 回)
致命的: #89: エンティティ参照ループを検出しました 14:13

結果:

<?xml version="1.0"?>

メモリ使用量は軽く、ピークはDOMDocument. この例では 7 つの致命的なエラーが示されているため、次のように結論付けることができます。

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
]>
<lolz>&lol2;</lolz>

エンティティの置換は有効ではなく、これは機能するので、試してみましょう

二次ブローアップ

これはここにあるもので、ご覧いただけるように短縮されています (私の亜種は約 27/11kb です)。

<?xml version="1.0"?>
<!DOCTYPE kaboom [
  <!ENTITY a "aaaaaaaaaaaaaaaaaa...">
]>
<kaboom>&a;&a;&a;&a;&a;&a;&a;&a;&a;...</kaboom>

これを使用する$doc->loadXML($src, LIBXML_NOENT);と、攻撃として機能します。これを書いている間、スクリプトはまだロード中です... . そのため、実際には読み込みに時間がかかり、メモリを消費します。自分で遊べるもの。それがなければ、完璧LIBXML_NOENTかつ高速に動作します。

ただし、警告があります。nodeValueたとえば、タグの を取得すると、その読み込みフラグを使用しなくても、エンティティが展開されます。

この問題の回避策は、ドキュメントから DocumentType ノードを削除することです。次のコードに注意してください。

$doc = new DOMDocument();
$doc->loadXML($s); // where $s is a Quadratic attack xml string above.
// now remove the doctype node
foreach ($doc->childNodes as $child) {
    if ($child->nodeType===XML_DOCUMENT_TYPE_NODE) {
        $doc->removeChild($child);
        break;
    }
}
// Now the following is true:
assert($doc->doctype===NULL);
assert($doc->lastChild->nodeValue==='...');
// Note that entities remain unexpanded in the output XML
// This is not so good since this makes the XML invalid.
// Better is a manual walk through all nodes looking for XML_ENTITY_NODE
assert($doc->saveXML()==="<?xml version="1.0"?>\n<kaboom>&a;&a;&a;&a;&a;&a;&a;&a;&a;...</kaboom>\n");
// however, canonicalization will produce warnings because it must resolve entities
assert($doc->C14N()===False);
// Warning will be like:
//    PHP Warning:  DOMNode::C14N(): Node XML_ENTITY_REF_NODE is invalid here 

したがって、この回避策は、XML ドキュメントが DoS でリソースを消費するのを防ぎますが、無効な XML を簡単に生成してしまいます。

いくつかの図(ファイルサイズを減らした場合、時間がかかりすぎます)(コード):

LIBXML_NOENT disabled                                          LIBXML_NOENT enabled

Mem: 356 184 (Peak: 435 464)                                   Mem: 356 280 (Peak: 435 464)                             
Loaded file quadratic-blowup-2.xml into string.                Loaded file quadratic-blowup-2.xml into string.          
Mem: 368 400 (Peak: 435 464)                                   Mem: 368 496 (Peak: 435 464)                             
DOMDocument loaded XML 11 881 bytes in 0.001368 secs.          DOMDocument loaded XML 11 881 bytes in 15.993627 secs.   
Mem: 369 088 (Peak: 435 464)                                   Mem: 369 184 (Peak: 435 464)                             
Removed load string.                                           Removed load string.                                     
Mem: 357 112 (Peak: 435 464)                                   Mem: 357 208 (Peak: 435 464)                             
Got XML (saveXML()), length: 11 880                            Got XML (saveXML()), length: 11 165 132                  
Got Text (nodeValue), length: 11 160 314; 11.060893 secs.      Got Text (nodeValue), length: 11 160 314; 0.025360 secs. 
Mem: 11 517 776 (Peak: 11 532 016)                             Mem: 11 517 872 (Peak: 22 685 360)                       

私はこれまで保護戦略について決心していませんでしたが、たとえばPHPStormに10億の笑いをロードするとフリーズすることがわかったので、これを書いている間はフリーズしたくなかったので、後者のテストをやめました.

于 2012-04-18T21:46:00.710 に答える
7

実際にサンプル ドキュメントを使用してアプリケーションをテストし、脆弱性があるかどうかを確認する必要があります。

PHP の xml ライブラリの基礎となるライブラリは libxml2 です。その動作は、ほとんどのライブラリが xml をロードするときに引数として受け入れるオプションの定数を介して php から制御されます。

あなたのphpのlibxml2バージョンを決定することができますecho LIBXML_DOTTED_VERSION;

それ以降のバージョン (2.6 以降) では、libxml2 には指数攻撃と二次攻撃の両方を防ぐように設計されたエンティティ置換制限が含まれています。これらはオプションでオーバーライドできLIBXML_PARSEHUGEます。

デフォルトでは、libxml2 は dtd をロードしたり、デフォルト属性を追加したり、エンティティ置換を実行したりしません。したがって、デフォルトの動作は dtds を無視することです。

次のように、これの一部をオンにすることができます。

  • LIBXML_DTDLOADdtds をロードします。
  • LIBXML_NONETdtds のネットワーク読み込みを無効にします。これを常にオンにして、libxml の dtd カタログを使用して dtds をロードする必要があります。
  • LIBXML_DTDVALID解析中に dtd 検証を実行します。
  • LIBXML_NOENTエンティティ置換を実行します。
  • LIBXML_DTDATTRデフォルトの属性を追加します。

したがって、デフォルト設定を使用すると、PHP/libxml2 はおそらくこれらの問題のいずれに対しても脆弱ではありませんが、確実に知る唯一の方法はテストすることです。

于 2012-04-18T15:55:16.297 に答える