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:
- https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-29357
- https://www.zerodayinitiative.com/advisories/ZDI-23-882/
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
.nbf
vàexp
: là giá trị thời gian hiện tại và thời gian hết hạn của JWT tokenaud
vàiss
: 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.realm
: Giá trị này được leak bằng cách gửi 1 request đến API với giá trị token trong headerAuthorization
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.
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.ClientId
⇒ SPApplicationPrincipalName.CreateFromString
như ảnh stack trace ở trên.
Vậy chỉ cần làm cho giá trị của SPAppRequestContext.Current
là null
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.UserAndApplication
và SPIncomingOAuthIdentityType.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.IsApplicationRequest
là false
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.