.NET Deserialization 101

hkln1
tradahacking
Published in
5 min readAug 29, 2021
  • Như các bạn đã biết, các ngôn ngữ điều có các hàm serialize/deserialize object. Nó được sử dụng khi chúng ta cần lưu trữ object trên media (database hoặc file) hay transfer trên network. Nếu developer sử dụng các hàm này không cẩn thận/deserialize các untrusted data có thể dẫn đến các vấn đề nghiêm trọng như code execution, DoS, protection bypasses hoặc logic flaws. Nếu các bạn search “is a security risk” trên trang document của M$, các bạn có thể thấy kha khá các method và class được cảnh báo có thể gây ra những lỗi deserialize.
  • Mặc dù có rất nhiều method và class được xác định là nguy hiểm, nhưng để khai thác thành công cần có sự nghiên cứu thêm, vì ngữ cảnh khai thác của chúng khá đặc biệt. Trong loạt bài này mình chỉ chia sẻ về các method hay gadget đã được các researcher trên thế giới nghiên cứu, áp dụng exploit thành công trên các CVE (hầu hết được tích hợp trong công cụ nổi tiếng ysoserial.net).

Ví dụ cơ bản về Serialization

  • Bạn có thể mark annotation [NonSerialized] cho những field mà bạn không muốn serialize (trong ví dụ này là field LastName).

Formatters

  • Trong .Net framework có rất nhiều formatter class dùng để serialize (ví dụ trên mình dùng BinaryFormatter). Mỗi formatter được dùng để serialize cho một định dạng khác nhau. Ví dụ:

BinaryFormatter cho binary format
SoapFormatter được dùng để serialize cho soap format
LosFormatter được dùng để serialize View State của Webform
ObjectStateFormatter được dùng để serialize object graphs

  • Ngoài ra chúng ta cũng có serialization classes cho các format khác như XmlSerializer, JsonSerializer,… Loạt bài sau mình sẽ đi vào phân tích những classes/methods này và gadget của nó.
  • Những formatter class này implement interface IFormatter và IRemotingFormatter. IRemotingFormatter là RPC interface cho các remote call. Bản thân nó cũng implement interface IFormatter. Nên chúng ta sẽ focus vào IFormatter.
  • Interface IFormatter định nghĩa hai methods là serialization, deserialization và ba fields. Để đơn giản hóa nội dụng, mình ko giải thích ý nghĩa của nó ở đây, bạn nào muốn tìm hiểu thêm có thể tra google nhé :). Giai đoạn này các bạn chỉ cần chú ý class field ISurrogateSelector SurrogateSelector, vì nó sẽ ảnh hưởng đến life cycle của quá trình serialization/deserialization.

Life cycle và các sự kiện diễn ra trong quá trình serialization

  • Theo tài liệu của M$, khi call các method serialize của các Formatter class. Quá trình life cycle như sau:

1 - Đầu tiên nó sẽ xác định xem các Formatter có proxy selector hay không (là cái class field ISurrogateSelector SurrogateSelector mình nói ở trên đó). Nếu có, nó check xem proxy selector có handle object type không. Nếu có nó sẽ gọi ISerializable.GetObjectData của proxy selector

2 - Nếu không có proxy selector hoặc proxy selector không handle object type. Nó kiểm tra xem object có annotation [Serializable] hay không. Nếu không, sẽ ném SerializationException.

3 - Nếu object được mark [Serializable], kiểm tra xem object có implement interface ISerializable không. Nếu có, nó sẽ gọi method GetObjectData của object.

4-Nếu object không implement interface ISerializable, default serialization sẽ được thực hiện (với những field ko mark [NonSerialized] ).

  • Có 4 callback events trong quá trình serialization và deserialization
  • Đây là đoạn mã mình code để mô tả rõ hơn life cycle quá trình serialization/deserialization (đoạn mã có dùng proxy selector)
  • Như đã chia sẻ, callback events OnSerializingAttribute sẽ được gọi trước khi serialize nên method có annotation [OnSerializing] sẽ print trước
  • Sau đó, do Formatter có dùng proxy selector nên method ISerializationSurrogate.GetObjectData của proxy selector sẽ được gọi và print
  • Tiếp tục callback events OnSeserializedAttribute sẽ được gọi sau khi serialize, nên method có annotation [OnSerialized] sẽ thực thi
  • Khi deserialization, callback events OnDeserializingAttribute sẽ được gọi trước khi deserialize, như vậy method có annotation [Ondeserializing] sẽ được gọi
  • Bởi vì có dùng proxy selector nên method ISerializationSurrogate.SetObjectData của proxy selector sẽ được gọi sau đó
  • Sau đó là method có annotation [Ondeserialized] được gọi
  • Đây là kết quả sau khi chạy đoạn mã, thứ tự các method được gọi và print ra
  • Nếu trong trường hợp không sử dụng proxy selector (comment dòng //binaryFormatter.SurrogateSelector = ss;), các bạn thử chạy sẽ ra kết quả
  • Các bạn để ý, trong quá trình serialize ứng dụng sẽ call method GetObjectData của object thay cho GetObjectData của proxy selector và khi trong quá trình deserialize ứng dụng sẽ gọi constructor của object chứ ko phải method SetObjectData của proxy selector
  • Và như mình nói ở trên, khi không implement interface ISerializable (các bạn thử xóa và chạy thử), ứng dụng sẽ dùng default serialization của .Net để thực thi. Kết quả sẽ là:
  • Như vậy chúng ta đã đi qua khái niệm cơ bản serialization/deserization trong .Net. Để kết thúc part này mình cũng muốn giới thiệu sơ qua công cụ Ysoserial.Net mà các part sau mình sẽ làm việc nhiều với nó. Ysoserial.Net chia làm ba phần chính: Formatters, Gadget và Plugin
  • Formatters là các class/method dùng để serialize/deserialize. Gadget thì không có gì lạ với những bạn chơi hệ deserialization phải ko;). Plugin để generate ra những định dạng payload để exploit trong 1 số ngữ cảnh/ứng dụng cụ thể. Mình sẽ tạm kết thúc part này ở đây. Happy reading !

--

--