.NET serialization/deserialization – basic C# attack example

In this post we’ll have a closer look at .NET serialization/deserialization attacks. We’ll have a .NET (C#) vulnerable code as an example (inspired by James’s work) and we will walk through it to see where the issue lies.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace testsrlz
{
  [Serializable]
  public class testcl
  {
    public string _cmd = "calc.exe";
    public testcl(string cmd)
    {
      if (cmd != "calc.exe") 
        Console.WriteLine("Invalid command");
      else
        _cmd = cmd;
    }
    public testcl()
    {
      if (_cmd != "calc.exe")
        Console.WriteLine("Invalid command");
    }

    public void Run()
    {
      Process.Start(_cmd);
    }
  }

  class Program
  {
    static void SerializeToXml(string fname)
    {
      testcl sc_testcl = new testcl("calc.exe");

      XmlSerializer ser_xml = new XmlSerializer(typeof(testcl));
      
      using (FileStream fs = File.Open(fname, FileMode.Create))
      {
        ser_xml.Serialize(fs, sc_testcl);
      }
    }

    static void DeSerializeFromXml(string fname)
    {
      testcl sc_testcl;
      XmlSerializer ser_xml = new XmlSerializer(typeof(testcl));
      using (FileStream fs = File.Open(fname, FileMode.Open))
      {
        XmlReader xread = XmlReader.Create(fs);
        sc_testcl = (testcl)ser_xml.Deserialize(xread);
      }
      Console.WriteLine("Run: " + sc_testcl._cmd);
      sc_testcl.Run();
    }

    static void Main(string[] args)
    {
      //Console.WriteLine("[+] Serialize class");
      //SerializeToXml("test.xml");
      Console.WriteLine("[+] Deserialize class");
      DeSerializeFromXml("test.xml");
    }
  }
}

If you serialize the class, the contents of test.xml will be something like the following:

<?xml version="1.0"?>
<testcl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <_cmd>calc.exe</_cmd>
</testcl>

The _cmd variable is checked only in theĀ testcl class constructor. So when the function will be deserialized, the constructor will no longer be called, so the Run method will use the value set in the XML file.

If you change the xml to something like this..

<?xml version="1.0"?>
<testcl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <_cmd>notepad.exe</_cmd>
</testcl>

.. you will see the notepad window popping up, instead of calculator.