.Net 反序列化一直是一個很熱門的話題,但是因為背後通常都是 Windows,搭建題目不便的原因,導致 CTF 通常很少出現。 這邊會用一個很簡單的例子,從 .Net 麻瓜的 Hello World 開始介紹。
因為不想用肥肥 CS,因此使用 csc 進行編譯,需要先把 C:\Windows\Microsoft.NET\Framework\v4.0.30319\ 加到 path
0x1 Hello World
身為一個不熟 C# 的人,凡事從 Hello World 開始
using System;
namespace helloworld{
class Program{
static void Main(string[] args){
Console.WriteLine("Hello World!");
}
}
}
編譯
csc /reference:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll;C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\WindowsBase.dll /t:exe /out:hello.exe hello.cs
可以順利的 Hello World!
0x2 Hello Class
C# 中,Class 的寫法是這樣
using System;
namespace hello_class{
class Program{
static void Main(string[] args){
Meow myMeow = new Meow();
myMeow.MeowName = "Steven";
myMeow.Meowww();
}
}
public class Meow{
public string MeowName{
get;
set;
}
public void Meowww(){
Console.WriteLine("Hello " + MeowName + " meow meow!");
}
}
}
編譯
csc /reference:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll;C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\WindowsBase.dll /t:exe /out:hello_class.exe hello_class.cs
0x3 Serialize
如同其他程式語言一樣, C# 也有序列化功能,可以把 Class 的 Public data 存出來
using System;
using System.Xml.Serialization;
using System.IO;
namespace serial_class{
class Program{
static void Main(string[] args){
Meow myMeow = new Meow();
myMeow.MeowName = "Steven";
myMeow.Meowww();
var ser = new XmlSerializer(typeof(Meow));
TextWriter writer = new StreamWriter("serial_meow.xml");
ser.Serialize(writer,myMeow);
writer.Close();
}
}
public class Meow{
public string MeowName{
get;
set;
}
public void Meowww(){
Console.WriteLine("Hello " + MeowName + " meow meow!");
}
}
}
Compile
csc /reference:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll;C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\WindowsBase.dll /t:exe /out:serial_class.exe serial_class.cs
serial_meow.xml 檔案內容會是這樣
<?xml version="1.0" encoding="utf-8"?>
<Meow xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MeowName>Steven</MeowName>
</Meow>
先不管 xmlns:xsd
跟 xmlns:xsi
,其實它就是一個 Class Name (Meow
) 包了裡面的 public string MeowName
0x4 Unserialize
using System;
using System.Xml.Serialization;
using System.IO;
namespace unserial_class{
class Program{
static void Main(string[] args){
var streamReader = new StreamReader("serial_meow.xml");
var ser = new XmlSerializer(typeof(Meow));
Meow myMeow = (Meow)ser.Deserialize(streamReader);
myMeow.Meowww();
}
}
public class Meow{
public string MeowName{
get;
set;
}
public void Meowww(){
Console.WriteLine("Hello " + MeowName + " meow meow!");
}
}
}
編譯
csc /reference:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll;C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\WindowsBase.dll /t:exe /out:unserial_class.exe unserial_class.cs
透過這隻程式,我們可以把 XML 檔案給還原回原本的 Class
0x5 Unserialize 2.0
.Net 中的反序列化,會去 Call set 的 Method,如同下面的程式碼,程式輸出結果會 Print 出 Set Value!!
,因此我們只需要掌握某些會 Set Value 的 Class Public data member,就有機會用反序列化做壞事。
using System;
using System.Xml.Serialization;
using System.IO;
//4
namespace unserial_class{
class Program{
static void Main(string[] args){
var streamReader = new StreamReader("serial_meow.xml");
var ser = new XmlSerializer(typeof(Meow));
Meow myMeow = (Meow)ser.Deserialize(streamReader);
// myMeow.Meowww();
}
}
public class Meow{
private string _MeowName;
public string MeowName{
get {
Console.WriteLine("Get Value!!");
return _MeowName;
}
set {
_MeowName = value;
Console.WriteLine("Set Value!!");
}
}
public void Meowww(){
Console.WriteLine("Hello " + MeowName + " meow meow!");
}
}
}
0x6 ObjectDataProvider
ObjectDataProvider 是一個 WPF 的東東,我們只要呼叫了 ObjectDataProvider,它就會自動幫我們去執行 Class 中的 Method
編譯前請確定把 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll
加入 Reference 中
using System;
using System.Windows.Data;
namespace odp{
class Program{
static void Main(string[] args){
ObjectDataProvider odp = new ObjectDataProvider();
odp.ObjectInstance = new Meow();
odp.MethodName = "Meowww";
// but this can't deserialize
}
}
public class Meow{
public string MeowName{
get;
set;
}
public void Meowww(){
Console.WriteLine("Hello meow meow!");
}
}
}
程式執行結果會順利的 Print 出 Hello meow meow!
,不過 ObjectDataProvider 無法被反序列化 QQ
0x7 ExpandedWrapper Serialize
我們可以借助 ExpandedWrapper 來幫 ObjectDataProvider 進行序列化,未來只需要反序列化這一包,程式就會乖乖去執行我們設定好的 Class Method
使用 ExpandedWrapper 需要確保將 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\WindowsBase.dll
加入 Reference
using System;
using System.Windows.Data;
using System.Data.Services.Internal;
using System.Xml.Serialization;
using System.IO;
namespace odp{
class Program{
static void Main(string[] args){
ExpandedWrapper<Meow, ObjectDataProvider> expandedWrapper = new ExpandedWrapper<Meow, ObjectDataProvider>();
expandedWrapper.ProjectedProperty0 = new ObjectDataProvider();
expandedWrapper.ProjectedProperty0.ObjectInstance = new Meow();
expandedWrapper.ProjectedProperty0.MethodName = "Meowww";
var ser = new XmlSerializer(typeof(ExpandedWrapper<Meow, ObjectDataProvider>));
TextWriter writer = new StreamWriter("expandedWrapper_serial_meow.xml");
ser.Serialize(writer,expandedWrapper);
writer.Close();
}
}
public class Meow{
public string MeowName{
get;
set;
}
public void Meowww(){
Console.WriteLine("Hello meow meow!");
}
}
}
0x8 ExpandedWrapper Deserialize
透過以下程式,我們可以順利的幫 ExpandedWrapper 進行反序列化,順便它也會去執行 Meow.Meowww()
using System;
using System.Xml.Serialization;
using System.IO;
using System.Windows.Data;
using System.Data.Services.Internal;
namespace unserial_class{
class Program{
static void Main(string[] args){
var streamReader = new StreamReader("expandedWrapper_serial_meow.xml");
var ser = new XmlSerializer(typeof(ExpandedWrapper<Meow, ObjectDataProvider>));
ser.Deserialize(streamReader);
}
}
public class Meow{
public string MeowName{
get;
set;
}
public void Meowww(){
Console.WriteLine("Hello " + MeowName + " meow meow!");
}
}
}
其實到這一步,我們已經可以執行程式裡面的任何 Class 中的 Function 了,也可以找到某些讀寫檔的 Function (Gadget) 來進行跳躍。