LoginSignup
2
1

More than 3 years have passed since last update.

XXEと.NET Framework

Last updated at Posted at 2019-04-16

XXEと.NET Framework

XXEとは

XXE(XML External Entity)とは、XMLの外部参照機能を利用して、サーバ内部のファイル内容を取得(漏えい)したり、内部ネットワーク上のファイルにアクセスしたりする不正行為

XXEは、XMLを扱うアプリケーションで発現する可能性があるので、XML文書を取り扱う際には注意する必要がある。

.NET FrameworkとXML

.NET FrameworkでXMLを扱うには、Sytem.Xml名前空間を利用が考えられる。

よって、以降は、System.Xml.XmlDocumentクラスを使って、再現させたり、対策を考えてみたりしよう。

結論

手っ取り早くXXEを予防したければ、System.Xml.XmlDocument#XmlResolver プロパティを「NULL」にする。

.NET FrameworkでXXEを発現させる

下記のソースコードのプログラムは、第一引数にXMLファイル、第二引数に外部参照の動作モードを指定するようになっているので、既定の動作モードで、XMLファイル(C:\z\xmlxxe\in1.xml)を読んでみる。

こんな感じ。

xxe-cs-defo.png

.NET Framework の場合は、既定ではXXE攻撃を受けてしまうということになる。

外部参照のオブジェクトをNULLにする

次はSystem.Xml.XmlDocument#XmlResolver プロパティを「NULL」にしてみる

xxe-cs-null.png

つまり、手っ取り早くXXEを予防したければ、System.Xml.XmlDocument#XmlResolver プロパティを「NULL」にする。

外部参照のオブジェクトを自作する

次は「外部参照機能を限定的に使いたいけど、XXEを防ぎたい」という場合は、リゾルバを自作すればいい。

つまり、System.Xml.XmlDocument#XmlResolver プロパティは「System.Xml.XmlResolver」型なので、それを継承したクラスを自作して、それをSystem.Xml.XmlDocument#XmlResolver プロパティに割り当てれば、外部参照を自由に制御する事ができる。

上書き必須のメソッドは、「object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)」メソッドだけ。
これの肝になる部分は、第一引数に外部参照のURLを示すUri型が来るので、それに応じた「System.IO.Stream」型を返せばいいだけ。

例えば、認証が必要だったり、特定のURIだけ許可したり、というような個別カスタマイズが可能。

ソースコードの「exXmlResolver.cs」の「exXmlResolverクラス」は、全てのSystem.Uriに対して、空のSystem.IO.MemoryStremを返すだけとした。
これは、実質的にSystem.Xml.XmlDocument#XmlResolver プロパティを「NULL」にした時と同じような挙動になると思う。

こんな感じになる。

xxe-cs-cust.png

外部参照のオブジェクトを自作するための子クラス

.NET Frameworkの場合、System.Xml.XmlResolverを直接継承しなくても、以下のクラスがあるので、自分の仕様に適合した既存クラスを使ってもよいだろう。

ソースコード(Program.cs)

Program.cs
using System;
using System.IO;
using System.Xml;

namespace XMLandXXEtest{
    class Program{
        static void Main(string[] args){
            Boolean Isresolve = true;
            exXmlResolver myExXmlResolver = null;
            if (1 < args.Length) {
                if (args[1].ToLower() == "null"){
                    Isresolve = false;
                }else {
                    myExXmlResolver = new exXmlResolver();
                }
            }
            if (0 == args.Length) {
                Console.WriteLine("usage: XMLandXXEtest.exe <<xmlFile>> [NotResolve]");
            }else{
                StreamReader sr = new StreamReader(new FileStream(args[0], FileMode.Open, FileAccess.Read));
                String str = sr.ReadToEnd();
                sr.Close();
                XmlDocument tempXmlDocument = new XmlDocument();
                if (Isresolve == false){
                    tempXmlDocument.XmlResolver = null;
                }else if (myExXmlResolver != null){
                    tempXmlDocument.XmlResolver = myExXmlResolver;
                }
                tempXmlDocument.LoadXml(str);
                Console.WriteLine("after XML Resolve is (OuterXml)");
                Console.WriteLine(tempXmlDocument.OuterXml);
                Console.WriteLine("----------------------------------");
                Console.WriteLine("after XML Resolve is (InnerXml)");
                Console.WriteLine(tempXmlDocument.InnerXml);
                Console.WriteLine("----------------------------------");
                Console.WriteLine("after XML Resolve is (InnerText)");
                Console.WriteLine(tempXmlDocument.InnerText);
                Console.WriteLine("----------Detail------------------");
                foreach (XmlNode x in tempXmlDocument.ChildNodes){
                    WroteXMLnode(x, "");
                }
            }
        }
        static void WroteXMLnode(XmlNode node, String spaceStr) {
            Console.WriteLine(spaceStr + "name=" + node.Name + ", type=" + node.NodeType.ToString());
            Console.WriteLine(spaceStr + "valT=" + node.InnerText);
            Console.WriteLine(spaceStr + "valX=" + node.InnerXml);
            if (node.HasChildNodes == true){
                foreach (XmlNode x in node.ChildNodes){
                    WroteXMLnode(x, spaceStr + " ");
                }
            }
        }
    }
}

ソースコード(exXmlResolver.cs)

空の MemoryStream を返すだけのリゾルバ

exXmlResolver.cs
using System;
using System.IO;
using System.Xml;

namespace XMLandXXEtest{
    public class exXmlResolver : XmlResolver{
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public exXmlResolver() { }
        /// <summary>
        /// 唯一、実装する必要のあるメソッド
        /// </summary>
        /// <param name="absoluteUri"></param>
        /// <param name="role"></param>
        /// <param name="ofObjectToReturn"></param>
        /// <returns></returns>
        public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn){
            if (absoluteUri == null){
                Console.WriteLine("Uri: null");
            }else {
                Console.WriteLine("Uri: " + absoluteUri.ToString());
            }
            if (role == null) {
                Console.WriteLine("Role: null");
            }else if (role == String.Empty){
                Console.WriteLine("Role: ");
            }else{
                Console.WriteLine("Role: " + role);
            }
            if (ofObjectToReturn == null){
                Console.WriteLine("Type: null");
            }else{
                Console.WriteLine("Type: " + ofObjectToReturn.ToString());
            }
            MemoryStream mem = new MemoryStream();
            return mem;
        }
        /// <summary>
        /// 
        /// </summary>
        public override System.Net.ICredentials Credentials{
            set {  }
        }
    }
}

戻る

XXE基本編へ戻る

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1