Brief CVE description

Lỗ hổng CVE-2023-29357 cho phép kẻ tấn công bỏ qua xác thực, leo thang đặc quyền trên các hệ thống sử dụng SharePoint. Lỗ hổng tồn tại bên trong method ValidateTokenIssuer do không thực hiện chính xác việc xác minh signature của JWT token.

Các phiên bản bị ảnh hưởng: < 16.0.10399.20005.

Advisory:

Mitigation

  • Upgrade lên phiên bản mới nhất của SharePoint.
  • Bật tính năng AMSI integration và sử dụng Windows Defender.

Analysis

Thêm enum MissingAlgorithm vào để coverage các trường hợp token đưa vào bị thiếu algorithm.

Override method ReadToken của class SPJsonWebSecurityBaseTokenHandler và gọi đến SPAuthenticationAlgorithmValidator.ValidateAlgorithm.

Method ValidateAlgorithm tiếp tục gọi đến method HasValidAlgorithm để kiểm tra giá trị alg trong phần JWT header.

Trong advisory của ZDI cũng mô tả lỗ hổng này nằm ở method ValidateTokenIssuer. Bản patch thêm vào đoạn code gọi đến method SPClaimsUtility.IsEnableOldHashedProofTokenFormat.

Call stack gọi đến ValidateTokenIssuer.

Từ những điều trên, ta suy được rằng có thể craft được 1 JWT token với algorithm là none và làm một điều gì đó có liên quan đến JWT issuer (giá trị của iss trong JWT token).

Để kiểm tra authentication, SharePoint lấy giá trị tại 2 vị trí: GET param prooftoken/header X-PROOF_TOKEN và header Authorization.

Token này cần có những giá trị như sau:

  • ver: giá trị phải là hashedprooftoken.
  • nbfexp: là giá trị thời gian hiện tại và thời gian hết hạn của JWT token
  • audiss: là giá trị audience và issuer, mà ở đây bắt buộc phải là 00000003-0000-0ff1-ce00-000000000000. Giá trị 00000003-0000-0ff1-ce00-000000000000 là Application ID cho Office 365 SharePoint Online, mặc dù giá trị này cũng có thể sử dụng được ở các phiên bản SharePoint on-premise.

    Source: https://learn.microsoft.com/en-us/troubleshoot/azure/active-directory/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications
  • realm: Giá trị này được leak bằng cách gửi 1 request đến API với giá trị token trong header Authorization là bất kì.

Sau khi thử gửi JWT với format như trên, SharePoint nhận thấy được user hiện tại đang là SharePoint App.

Mặc dù đã authenticated thành công với user SharePoint App nhưng mục đích của lỗi là ta cần impersonate được account farmadmin.

Liệt kê các user có quyền trên Site hiện tại bằng endpoint /_api/web/siteusers.

Để impersonate được thành account khác, ta cần thay đổi trường nameid trong proof token.

Proof token lúc này của ta như sau:

Sau khi request bằng token mới, ta nhận được lỗi Specified method is not supported.

Tiến hành debug để tìm hiểu nguyên nhân, exception sẽ được throw tại method SPApplicationPrincipalName.CreateFromString.

Lí do vì giá trị của applicationPrincipleName hiện tại của ta không có kí tự @ nên không thể split để array2.Length bằng 2 được.

Stack trace đến method SPApplicationPrincipalName.CreateFromString

Trace ngược từ stack trace một chút, SharePoint kiểm tra SPAppRequestContext.Current có giá trị hay không, nếu có thì sẽ gọi đến SPAppRequestContext.Current.ClientIdSPApplicationPrincipalName.CreateFromString như ảnh stack trace ở trên.

Vậy chỉ cần làm cho giá trị của SPAppRequestContext.Currentnull thì sẽ không gọi đến ClientId nữa.

Sau một thời gian debug, mình nhận thấy rằng method SPAppRequestContext.InitCurrent sẽ thực hiện nhiệm vụ gán giá trị cho SPAppRequestContext.Current.Trong này gọi đến method SPApplicationRequestHelper.IsApplicationRequest để check request được gửi đến có phải là 1 “application request” hay không.

Method này được định nghĩa như ảnh sau:

Ở đây ta chỉ cần quan tâm đến đoạn check spincomingOAuthIdentityType với 2 constants là SPIncomingOAuthIdentityType.UserAndApplicationSPIncomingOAuthIdentityType.ApplicationOnly. Chỉ cần request của mình không thỏa 2 điều kiện này thì SPAppRequestContext.Current sẽ là null.

Để spincomingOAuthIdentityType không bằng hai giá trị trên, ta xét method SPIncomingTokenContext.SetIdentityType, method này sẽ dựa trên proof token mình gửi lên để quyết định token của mình là User hay Application.

Sau một lúc debug tiếp, mình phát hiện được rằng khi field isuser trong proof token của mình có giá trị là true, mình có thể làm cho giá trị của claimsIdentity.Actor bằng null và không nhảy vào đoạn code bên trong câu lệnh if.

Sau đó, chương trình tiếp tục chạy xuống method SPIncomingTokenContext.IsProofToken bên dưới, method này kiểm tra field tt trong proof token, nếu không tồn tại field này thì sẽ bỏ qua và trả về false.

Từ đó, ta đã làm cho IdentityType trở thành UserOnly mà không phải UserAndApplication hay ApplicationOnly nữa.

Lúc này, giá trị trả về của SPApplicationRequestHelper.IsApplicationRequestfalse và set app request context cho null.

Lúc này việc authentication đã thành công.

Proof token của mình hiện tại:

Kết quả sau khi thử lại lần nữa với token mới.

Sau khi impersonated thành công account farmadmin, ta có thể tiếp tục khai thác CVE-2023-24955 để khai thác RCE, lỗ hổng mà mình sẽ viết ở bài blog phần tiếp theo.

6.223 lượt xem