Mình đã từng làm về dạng Prototype Pollution trong CTF rất nhiều nhưng có lẽ đây là lần đầu mình research CVE về lỗi này
ADVISORY DETAILS. Khi mình được @n3mo rủ làm về CVE này thì chúng mình đã bắt tay vào làm ngay và sau 1 tuần debug vào mỗi đêm thì cuối cùng cũng đã build được POC. Mình viết bài này để mong muốn chia sẻ và lưu lại kiến thức, biết đâu sau này sẽ cần >_<. Vào việc thôi nào …
Brief CVE description
Lỗ hổng này cho phép kẻ tấn công từ xa thực thi mã tùy ý trên Parse Server bị ảnh hưởng. Authentication là không cần thiết để khai thác lỗ hổng này.
Lỗ hổng cụ thể tồn tại trong hàm transformUpdate. Do thiếu kiểm soát đối với các các thuộc tính trong object. Kẻ tấn công có thể tận dụng lỗ hổng này để ghi đè thuộc tính tùy ý và impact cao nhất là dẫn đến thực thi mã từ xa.
Danh sách các phiên bản bị ảnh hưởng:
Điểm CVSS 3.1:
9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
Advisories:
https://www.zerodayinitiative.com/advisories/ZDI-22-1590/
Commit:
https://github.com/parse-community/parse-server/compare/5.3.0...5.3.1
Setup
Đầu tiên cần tải source code ở trên github về.
git clone https://github.com/parse-community/parse-server.git
cd parse-server/
git checkout tags/5.3.0
CVE-2022-39396 có bản vá ở 2 versions 4.10.18 và 5.3.1, nên mình quyết định checkout ở 5.3.0.
Sau khi checkout thì sửa file Dockerfile nhằm remote debug:
- Thêm expose port
EXPOSE 9229
- Sửa entrypoint
ENTRYPOINT ["node","--inspect=0.0.0.0:9229", "./bin/parse-server"]
Build parse-server docker
docker build --tag parse-server .
Run mongo docker
docker run --name my-mongo -d mongo
Cuối cùng chạy lệnh sau để run parse server
docker run --name my-parse-server -v config-vol:/parse-server/config -p 9229:9229 -p 1337:1337 --link my-mongo:mongo -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test
Check server đã up chưa bằng lệnh
curl -X POST \
-H "X-Parse-Application-Id: APPLICATION_ID" \
-H "Content-Type: application/json" \
-d '{"score":1337,"playerName":"Sean Plott","cheatMode":false}' \
http://localhost:1337/parse/classes/GameScore
Remote debug
Ở đây mình dùng WebStorm, mở project source code đã checkout ở trên, sau đó add config -> search chữ chrome ( attach to nodejs/chrome) -> sửa host tương ứng vs ip server, port là
9229.
The Patch
Ở CVE này thì source code có trên github và các commit fix đều nằm ở trên đó nên mình không cần phải sử dụng phần mềm khác đề diff mà compare 2 bản ở trên github luôn.
Root Cause
Đầu tiên mình focus vào đoạn diff ở trên github thì thấy được bản mới chỉ thực sự fix ở file
src/Routers/FilesRouter.js bằng cách thêm đoạn check từ dòng 144-168

Cụ thể đoạn code này như sau:
- Check đầu vào của chúng ta là
metadata và tags nếu như chứa key và value nằm trong requestKeywordDenylist thì sẽ hiện thị lỗi.
- Cụ thể
requestKeywordDenylist được set mặc định trong file src/Options/Definitions.js

- Ở đây được chặn được một số keyword như trên và thứ mình thấy ngay lúc này là có chặn
constructor và __proto__ nên mình đã tưởng rằng tìm được chỗ prototype pollution và đã bám vào chỗ này rất lâu.
Khi tới đây, mình tưởng rằng chỉ cần pollution chỗ này là xong với đặt câu hỏi trong đầu:
- Nếu có thể prototype pollution ở đây thì mình cần pollution cái gì?
- Làm thế nào để RCE khi không thể thấy sink RCE ở chỗ này?
Sink đầu tiên mà mình tưởng prototype pollution là tại đoạn code sau

Với
fileOptions và
fileTags được xử lí như dưới đây.
- Cả hàm
setTags và setMetadata đều có chức năng giống nhau là gán giá trị _metadata hoặc _tags theo key-value.
Chỗ xử lí chính của setMetadata
Chỗ xử lí chính của setTags
Cuối cùng 2 object fileOptions và fileTags có attribute metadata và tags được lấy từ fileObject.file._metadata và fileObject.file._tags được set ở trên.

Với
key và
value mình có thể control thì hiện tại mình có thể chèn
key bằng
__proto__ với
value mình muốn.
Để có thể truy cập vô đây thì mình sử dụng router
/files/:filename với method POST
- Khi truy cập vào router này thì đầu tiên hàm
handleParseHeaders để xác thực auth. Hàm này chỉ thực hiện check header X-Parse-Master-Key và tham số truyền vào ở json là _ApplicationId để xác thực auth.
- Sau đó sẽ gọi tới
createHandler là đoạn code xử lí mà mình cần vào.

Đặt breakpoint tại dòng 483 ở file
node_modules/parse/lib/node/ParseFile.js và send với request sau để xác định xem mình đã có thể nhảy vào đúng thứ mình muốn chưa:
POST /parse/files/hello HTTP/1.1
Host: localhost:1337
Connection: close
X-Parse-Master-Key: MASTER_KEY
Content-Type: text/json
Content-Length: 100
{"_ApplicationId":"APPLICATION_ID","base64":"AAA","fileData":{"metadata":{"__proto__":{"polluted":true}}}}
Tiếp tục đặt breakpoint ở dòng 183 và nhảy đến đây.

Nhưng khi check thì thấy thực sự ở đây không thể pollution toàn bộ object.

Giống với một ví dụ trên mạng mình đã tìm được và test
Ref

Example:
- Thực sử nó chỉ đè
__proto__ của object đó chứ không phải là prototype pollution.
- Một điều nữa là nó không đệ quy ở đoạn này nên pollution toàn bộ object là không khả thi. Explain
Tới đây mình khá là bế tắc không phải làm gì tiếp theo, vẫn như thường lệ khi làm 1day thì sẽ tìm mục đích cần làm là gì. Đây là lí do mình thích làm 1day vì nó khá giống chơi CTF, biết được mục tiêu cuối cùng mình làm là gì. Vậy ở đây trước khi mình pollution được toàn bộ object thì mình sẽ tìm sink RCE nằm ở đâu.
Sink exploit for RCE
Để tìm được chỗ có thể exploit RCE thì mình đã đọc spec mà được cung cấp cùng với trong diff github

Qua đây thì có thể đoán được có vẻ là liên quan đến
evalFuntions và mình có tham khảo các CVE trước đây của parse server thì hầu hết nó vẫn liên quan đến đoạn này.
Ref1,
Ref2
Sau khi search
evalFuntions trong source và đọc những CVE trước đó thì thấy được đoạn code như sau:
- Trong hàm
deserializeObject khi evalFunctions được set thành true thì sẽ nhảy vào hàm isolateEval

- Hàm này có sử dụng
Function.prototype.bind() nên mình có thể lợi dụng ở đây để RCE.
- CVE trước đây thì ở hàm
isolateEval sử dụng eval nên RCE dễ dàng hơn.
Mình đọc các CVE cũ của những người khác phân tích thì thấy rằng đoạn này như sau:
- Trước khi lưu dữ liệu lên DB, luồng dữ liệu sẽ được serialize qua thư viện BSON tại
node_modules/mongodb/node_modules/bson/src/parser/serializer.ts
- Sau đó nếu chúng ta truy cập vào nội dung được lưu trong DB thì chương trình sẽ tiến hành deserialize.
Cụ thể như nào thì mình sẽ debug dưới đây.
Đặt breakpoint tại 2 dòng 183 và 185

và dòng 542 của file
serializer.ts

Thực hiện send request với
_bsontype là
Code.
POST /parse/files/hello HTTP/1.1
Host: localhost:1337
X-Parse-Master-Key: MASTER_KEY
Content-Type: text/json
Content-Length: 176
{"_ApplicationId":"APPLICATION_ID","base64":"AAA","fileData":{"metadata":{"_bsontype":"Code","code":"console.log(1)"}}}
Sau khi f9 qua dòng 185 thì chương trình sẽ nhảy vô hàm
serializeCode

Khi duyệt qua
serializeInto lần thứ 3 thì chương trình có
value['_bsontype'] === 'Code' và sẽ nhảy vào
serializeCode.
Stack Frame của khi gọi
serializeCode:

Tiếp đến mình sẽ debug xem deserialize sẽ được gọi như nào.
Đặt breakpoint tại dòng 577 của file
node_modules/mongodb/node_modules/bson/src/parser/deserializer.ts chính là sink mà mình cần vào.

Với request ở trên serialize thì reponse trả về sẽ chứa url truy cập vô file. Thực hiện truy cập vào url trả về đó.
http://localhost:1337/parse/files/APPLICATION_ID/d634ffe8019ffc125f27e230ea5f2a69_hello
evalFunctions lúc này được set là false nên không thể nhảy vô hàm isolateEval.
- Stack Frame để đến được đây như sau

- Đoạn code
evalFunctions được set

Vậy mình đã tìm được giá trị cần pollution để vào được sink RCE là
evalFunctions => giờ cần tìm chỗ prototype pollution toàn bộ object sau đó ghi đè
evalFunctions thành true thì sẽ hoàn thành.
Sau khi lỗ hổng này được public trên ZDI thì mình mới biết được nó có liên quan đến hàm
transformUpdate, lúc đầu mình làm chỉ focus vào những đoạn patch và thực sự trong đoạn patch thì lại không nhắc đến hàm này.
Search hàm
transformUpdate trong source thì cuối cùng cũng đã tìm được sink cho việc prototype pollution tại hàm
transformUpdate trong file
src/Adapters/Storage/Mongo/MongoTransform.js.

Vậy là đã về dạng pollution mà mình thường gặp. Vậy việc bây giờ cần làm là control được những giá trị sau:
out.value.__op -> __proto__
out.key -> evalFunctions
out.value.arg -> true
Bây giờ mình cần đi tìm route nào sẽ xử lí và gọi đến hàm
transformUpdate.
Search
transformUpdate để xem được sử dụng ở đâu thì thấy được chủ yếu được gọi ở trong file
src/Adapters/Storage/Mongo/MongoStorageAdapter.js.

Có 3 hàm gọi đến là:
updateObjectsByQuery
findOneAndUpdate
upsertOneObject
Tiếp tục 3 hàm này đều được gọi ở trong hàm
update tại
src/Controllers/DatabaseController.js.
Trace tiếp xem hàm
update này sẽ được gọi những nơi nào. Ở đây rất nhiều chỗ sẽ gọi đến hàm này nhưng mình đã focus vào 2 chỗ mà 2 route sử dụng mà mình thấy là hợp lí, trong đó 1 route auth và 1 route unauth.

Mình sẽ đi vào route
/graphql-config
- Nhận đầu vào mới tham số
params

- Check đầu vào của chúng ta

- Ở đoạn check này chỉ thực hiện check các atribute đưa vào phải là 1 trong số này và tất cả đều phải là array. Nhưng chúng ta có thể truyền vào
__proto__ vì đây là 1 atribute đặc biệt nên được chấp nhận và vượt qua đoạn check này.

- Nhảy vào hàm
update

- Tới đây có thể debug dần dần tiếp nhưng mình chọn đặt breakpoint tại hàm
transformUpdate ở src/Adapters/Storage/Mongo/MongoTransform.js luôn, sau đó mình sẽ xem stack frame biết được nó sẽ đi qua những hàm nào.

Như đã nói ở trên có 3 hàm gọi đến
transformUpdate thì ở route này đã sử dụng
upsertOneObject.
Các giá trị mà chúng ta muốn thì hiện tại không thể control. Khi mình đi đọc stackframe và debug thì thấy được ở route này chúng ta chỉ có thể control được
out.value.__op và
out.value.arg theo flow như sau:
- Trước khi nhảy vào sink pollution thì sẽ có đi qua hàm
transformKeyValueForUpdate.

- Với
restKey lúc này là config không nằm trong switch thì sẽ nhảy xuống đoạn code tiếp theo.

- Tại đoạn code tiếp theo này thì nhánh if cũng không vô được vì sai các điều kiện của nó.

- Vậy tiếp tục code sẽ nhảy vào hàm
transformTopLevelAtom với restValue là một object của mình truyền vào.

- Khi nhảy vào function đó thì mình thấy có 1 đoạn check
isValidJSON, cụ thể hàm này sẽ check xem value.__type === 'File' và sau đó sẽ nhảy vào JSONToDatabase. atom chính là input của mình nhập vào, nên mình có thể control được giá trị trong này.
Check điều kiện
Cuối cùng gọi JSONToDatabase trả về json.name. Vậy ở đây mình có thể dựa vào name để control __op và arg.

Send request với
__type là
File để thoải điều kiện và nhảy vào được
JSONToDatabase:
PUT /parse/graphql-config HTTP/1.1
Host: localhost:1337
Connection: close
X-Parse-Master-Key: MASTER_KEY
X-Parse-Application-Id: APPLICATION_ID
Content-Type: text/json
Content-Length: 120
{"params":{"classConfigs":[{"className":"haha"}],"__proto__":{"__type":"File"}}}

Khi debug tới hàm
isValidJSON thì thấy được với request trên thì mình đã vô được
JSONToDatabase, vậy bây giờ mình sẽ thêm atribute
name với giá trị
__op và
arg mình muốn.
Tới sink thì thấy được giá trị của
__op và
arg đã thay đổi theo ý mình.

Nhưng ở đây mình không thể control
key, vì vậy mình chỉ pollution
config cho toàn bộ object.

Lí do ở đây không thể control được
key vì nó đã được set cứng ở file
src/Controllers/ParseGraphQLController.js.

=> Mình không thể prototype pollution ở route này.
Flow của route
graphql-config.
updateGraphQLConfig
_validateGraphQLConfig
update
upsertOneObject
transformUpdate
transformKeyValueForUpdate
transformTopLevelAtom
isValidJSON
JSONToDatabase
Tiếp tục đến với route thứ hai là
/classes/:className/:objectId

Ở đây sẽ gọi đến
handleUpdate và có gọi đến
update

Tiếp tục chương trình sẽ gọi đến hàm
execute của
RestWrite

Rồi sau đó chương trình sẽ về dạng gần giống như ở route trên để xử lí

Ở route này mình có thể control được đầu vào là
className,
objectId và
req.body là tùy ý của mình.

Như route trước mình đặt breakpoint ở hàm
transformUpdate chứa sink để khi cho chương trình chạy đến đây thì mình sẽ đọc các stack frame và xem input của mình đi qua những chỗ nào và xử lí ở đâu.

Vậy ở đây khi mình nhập body là một object thì
restUpdate sẽ nhận tất cả object đó.

Tới đoạn này thì thấy được
restKey sẽ đượclấy ra từ
restUpdate => đã có thể control được
key => hoàn thành việc prototype pollution. Ở đây mình sẽ không nói tiếp việc control
__op và
arg vì khi nhảy vào hàm
transformKeyValueForUpdate thì sẽ xử lí như nhau. Với việc mình có thể control toàn bộ đầu vào là 1 object bằng cách nhập vào key-value tương ứng thì để vượt qua các đoạn check trong hàm
transformTopLevelAtom để tới
JSONToDatabase đã là điều dễ dàng.
Tóm lại.
- Thực hiện prototype pollution
evalFunctions tại route /classes/:className/:objectId để nhảy vào được hàm isolateEval có chứa sink để dẫn đến RCE.
- Khi nhảy vào được hàm
isolateEval thì sẽ mục đích của mình là sẽ lợi dụng .bind để gọi một function khác -> dẫn đến RCE

- Tại hàm
metadataHandler và có một route gọi đến hàm này nên mình có thể lợi dụng chỗ này để thực thi một function khác. Nếu như chúng ta send với một atribute toJSON thì khi code chạy tớires.json thì trong hàm này có chứa JSON.stringify(obj) vì vậy nó sẽ tự động được gọi tới.

POC
https://www.youtube.com/watch?v=ETeTqtmEICE
Brief CVE description
Lỗ hổng này cho phép kẻ tấn công từ xa thực thi mã tùy ý trên Parse Server bị ảnh hưởng. Authentication là không cần thiết để khai thác lỗ hổng này. Lỗ hổng cụ thể tồn tại trong hàm transformUpdate. Do thiếu kiểm soát đối với các các thuộc tính trong object. Kẻ tấn công có thể tận dụng lỗ hổng này để ghi đè thuộc tính tùy ý và impact cao nhất là dẫn đến thực thi mã từ xa. Danh sách các phiên bản bị ảnh hưởng:- < 4.10.18 và < 5.3.1
Điểm CVSS 3.1:9.8(AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)Advisories: https://www.zerodayinitiative.com/advisories/ZDI-22-1590/ Commit: https://github.com/parse-community/parse-server/compare/5.3.0...5.3.1Setup
Đầu tiên cần tải source code ở trên github về. CVE-2022-39396 có bản vá ở 2 versions 4.10.18 và 5.3.1, nên mình quyết định checkout ở 5.3.0. Sau khi checkout thì sửa file Dockerfile nhằm remote debug:- Thêm expose port
- Sửa entrypoint
Build parse-server dockerEXPOSE 9229ENTRYPOINT ["node","--inspect=0.0.0.0:9229", "./bin/parse-server"]docker build --tag parse-server .Run mongo dockerdocker run --name my-mongo -d mongoCuối cùng chạy lệnh sau để run parse serverdocker run --name my-parse-server -v config-vol:/parse-server/config -p 9229:9229 -p 1337:1337 --link my-mongo:mongo -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/testCheck server đã up chưa bằng lệnhRemote debug
Ở đây mình dùng WebStorm, mở project source code đã checkout ở trên, sau đó add config -> search chữ chrome ( attach to nodejs/chrome) -> sửa host tương ứng vs ip server, port là9229.The Patch
Ở CVE này thì source code có trên github và các commit fix đều nằm ở trên đó nên mình không cần phải sử dụng phần mềm khác đề diff mà compare 2 bản ở trên github luôn.transformUpdate.Root Cause
Đầu tiên mình focus vào đoạn diff ở trên github thì thấy được bản mới chỉ thực sự fix ở filesrc/Routers/FilesRouter.jsbằng cách thêm đoạn check từ dòng 144-168- Check đầu vào của chúng ta là
- Cụ thể

- Ở đây được chặn được một số keyword như trên và thứ mình thấy ngay lúc này là có chặn
Khi tới đây, mình tưởng rằng chỉ cần pollution chỗ này là xong với đặt câu hỏi trong đầu:metadatavàtagsnếu như chứakeyvàvaluenằm trongrequestKeywordDenylistthì sẽ hiện thị lỗi.requestKeywordDenylistđược set mặc định trong filesrc/Options/Definitions.jsconstructorvà__proto__nên mình đã tưởng rằng tìm được chỗ prototype pollution và đã bám vào chỗ này rất lâu.- Nếu có thể prototype pollution ở đây thì mình cần pollution cái gì?
- Làm thế nào để RCE khi không thể thấy sink RCE ở chỗ này?
Sink đầu tiên mà mình tưởng prototype pollution là tại đoạn code saufileOptionsvàfileTagsđược xử lí như dưới đây.- Cả hàm
Chỗ xử lí chính của
Cuối cùng 2 object 
VớisetTagsvàsetMetadatađều có chức năng giống nhau là gán giá trị_metadatahoặc_tagstheo key-value. Chỗ xử lí chính củasetMetadatasetTagsfileOptionsvàfileTagscó attributemetadatavàtagsđược lấy từfileObject.file._metadatavàfileObject.file._tagsđược set ở trên.keyvàvaluemình có thể control thì hiện tại mình có thể chènkeybằng__proto__vớivaluemình muốn. Để có thể truy cập vô đây thì mình sử dụng router/files/:filenamevới method POSThandleParseHeadersđể xác thực auth. Hàm này chỉ thực hiện check headerX-Parse-Master-Keyvà tham số truyền vào ở json là_ApplicationIdđể xác thực auth.createHandlerlà đoạn code xử lí mà mình cần vào.node_modules/parse/lib/node/ParseFile.jsvà send với request sau để xác định xem mình đã có thể nhảy vào đúng thứ mình muốn chưa: Tiếp tục đặt breakpoint ở dòng 183 và nhảy đến đây.- Thực sử nó chỉ đè
- Một điều nữa là nó không đệ quy ở đoạn này nên pollution toàn bộ object là không khả thi. Explain
Tới đây mình khá là bế tắc không phải làm gì tiếp theo, vẫn như thường lệ khi làm 1day thì sẽ tìm mục đích cần làm là gì. Đây là lí do mình thích làm 1day vì nó khá giống chơi CTF, biết được mục tiêu cuối cùng mình làm là gì. Vậy ở đây trước khi mình pollution được toàn bộ object thì mình sẽ tìm sink RCE nằm ở đâu.__proto__của object đó chứ không phải là prototype pollution.Sink exploit for RCE
Để tìm được chỗ có thể exploit RCE thì mình đã đọc spec mà được cung cấp cùng với trong diff githubevalFuntionsvà mình có tham khảo các CVE trước đây của parse server thì hầu hết nó vẫn liên quan đến đoạn này. Ref1, Ref2 Sau khi searchevalFuntionstrong source và đọc những CVE trước đó thì thấy được đoạn code như sau:- Trong hàm

- Hàm này có sử dụng
- CVE trước đây thì ở hàm
Mình đọc các CVE cũ của những người khác phân tích thì thấy rằng đoạn này như sau:deserializeObjectkhievalFunctionsđược set thành true thì sẽ nhảy vào hàmisolateEvalFunction.prototype.bind()nên mình có thể lợi dụng ở đây để RCE.isolateEvalsử dụngevalnên RCE dễ dàng hơn.- Trước khi lưu dữ liệu lên DB, luồng dữ liệu sẽ được serialize qua thư viện BSON tại
- Sau đó nếu chúng ta truy cập vào nội dung được lưu trong DB thì chương trình sẽ tiến hành deserialize.
Cụ thể như nào thì mình sẽ debug dưới đây. Đặt breakpoint tại 2 dòng 183 và 185node_modules/mongodb/node_modules/bson/src/parser/serializer.tsserializer.ts_bsontypelàCode. Sau khi f9 qua dòng 185 thì chương trình sẽ nhảy vô hàmserializeCodeserializeIntolần thứ 3 thì chương trình cóvalue['_bsontype'] === 'Code'và sẽ nhảy vàoserializeCode. Stack Frame của khi gọiserializeCode:node_modules/mongodb/node_modules/bson/src/parser/deserializer.tschính là sink mà mình cần vào.
- Stack Frame để đến được đây như sau

- Đoạn code

Vậy mình đã tìm được giá trị cần pollution để vào được sink RCE làevalFunctionslúc này được set là false nên không thể nhảy vô hàmisolateEval.evalFunctionsđược setevalFunctions=> giờ cần tìm chỗ prototype pollution toàn bộ object sau đó ghi đèevalFunctionsthành true thì sẽ hoàn thành. Sau khi lỗ hổng này được public trên ZDI thì mình mới biết được nó có liên quan đến hàmtransformUpdate, lúc đầu mình làm chỉ focus vào những đoạn patch và thực sự trong đoạn patch thì lại không nhắc đến hàm này. Search hàmtransformUpdatetrong source thì cuối cùng cũng đã tìm được sink cho việc prototype pollution tại hàmtransformUpdatetrong filesrc/Adapters/Storage/Mongo/MongoTransform.js.
Bây giờ mình cần đi tìm route nào sẽ xử lí và gọi đến hàmout.value.__op->__proto__out.key->evalFunctionsout.value.arg->truetransformUpdate. SearchtransformUpdateđể xem được sử dụng ở đâu thì thấy được chủ yếu được gọi ở trong filesrc/Adapters/Storage/Mongo/MongoStorageAdapter.js.updatetạisrc/Controllers/DatabaseController.js. Trace tiếp xem hàmupdatenày sẽ được gọi những nơi nào. Ở đây rất nhiều chỗ sẽ gọi đến hàm này nhưng mình đã focus vào 2 chỗ mà 2 route sử dụng mà mình thấy là hợp lí, trong đó 1 route auth và 1 route unauth./graphql-config- Nhận đầu vào mới tham số

- Check đầu vào của chúng ta

- Ở đoạn check này chỉ thực hiện check các atribute đưa vào phải là 1 trong số này và tất cả đều phải là array. Nhưng chúng ta có thể truyền vào

- Nhảy vào hàm

- Tới đây có thể debug dần dần tiếp nhưng mình chọn đặt breakpoint tại hàm

Như đã nói ở trên có 3 hàm gọi đếnparams__proto__vì đây là 1 atribute đặc biệt nên được chấp nhận và vượt qua đoạn check này.updatetransformUpdateởsrc/Adapters/Storage/Mongo/MongoTransform.jsluôn, sau đó mình sẽ xem stack frame biết được nó sẽ đi qua những hàm nào.transformUpdatethì ở route này đã sử dụngupsertOneObject. Các giá trị mà chúng ta muốn thì hiện tại không thể control. Khi mình đi đọc stackframe và debug thì thấy được ở route này chúng ta chỉ có thể control đượcout.value.__opvàout.value.argtheo flow như sau:- Trước khi nhảy vào sink pollution thì sẽ có đi qua hàm

- Với

- Tại đoạn code tiếp theo này thì nhánh if cũng không vô được vì sai các điều kiện của nó.

- Vậy tiếp tục code sẽ nhảy vào hàm

- Khi nhảy vào function đó thì mình thấy có 1 đoạn check
Check điều kiện
Cuối cùng gọi 
Send request vớitransformKeyValueForUpdate.restKeylúc này làconfigkhông nằm trong switch thì sẽ nhảy xuống đoạn code tiếp theo.transformTopLevelAtomvớirestValuelà một object của mình truyền vào.isValidJSON, cụ thể hàm này sẽ check xemvalue.__type === 'File'và sau đó sẽ nhảy vàoJSONToDatabase.atomchính là input của mình nhập vào, nên mình có thể control được giá trị trong này.JSONToDatabasetrả vềjson.name. Vậy ở đây mình có thể dựa vàonameđể control__opvàarg.__typelàFileđể thoải điều kiện và nhảy vào đượcJSONToDatabase:isValidJSONthì thấy được với request trên thì mình đã vô đượcJSONToDatabase, vậy bây giờ mình sẽ thêm atributenamevới giá trị__opvàargmình muốn. Tới sink thì thấy được giá trị của__opvàargđã thay đổi theo ý mình.key, vì vậy mình chỉ pollutionconfigcho toàn bộ object.keyvì nó đã được set cứng ở filesrc/Controllers/ParseGraphQLController.js.graphql-config. Tiếp tục đến với route thứ hai là/classes/:className/:objectIdhandleUpdatevà có gọi đếnupdateexecutecủaRestWriteclassName,objectIdvàreq.bodylà tùy ý của mình.transformUpdatechứa sink để khi cho chương trình chạy đến đây thì mình sẽ đọc các stack frame và xem input của mình đi qua những chỗ nào và xử lí ở đâu.restUpdatesẽ nhận tất cả object đó.restKeysẽ đượclấy ra từrestUpdate=> đã có thể control đượckey=> hoàn thành việc prototype pollution. Ở đây mình sẽ không nói tiếp việc control__opvàargvì khi nhảy vào hàmtransformKeyValueForUpdatethì sẽ xử lí như nhau. Với việc mình có thể control toàn bộ đầu vào là 1 object bằng cách nhập vào key-value tương ứng thì để vượt qua các đoạn check trong hàmtransformTopLevelAtomđể tớiJSONToDatabaseđã là điều dễ dàng.Tóm lại.
evalFunctionstại route/classes/:className/:objectIdđể nhảy vào được hàmisolateEvalcó chứa sink để dẫn đến RCE.isolateEvalthì sẽ mục đích của mình là sẽ lợi dụng.bindđể gọi một function khác -> dẫn đến RCEmetadataHandlervà có một route gọi đến hàm này nên mình có thể lợi dụng chỗ này để thực thi một function khác. Nếu như chúng ta send với một atributetoJSONthì khi code chạy tớires.jsonthì trong hàm này có chứaJSON.stringify(obj)vì vậy nó sẽ tự động được gọi tới.POC
https://www.youtube.com/watch?v=ETeTqtmEICE