Home CVE-2025-2945 - pgAdmin4 for RCE
Post
Cancel

CVE-2025-2945 - pgAdmin4 for RCE

CVE-2025-2945에 대해서 알아봅시다.

What is pgAdmin4

pgAdmin4는 postgresql을 쉽게 관리할 수 있도록 도와주는 도구입니다.
pgAdmin4는 Python, ReactJs, Javascript를 사용하여 pgAdmin을 만들었습니다.

Electron으로 만들어진 Desktop 런타임을 통해 개별 사용자가 단독으로 실행하거나, Flask 기반으로 만들어진 웹 서비스를 통해 1명 이상의 사용자가 웹 브라우저를 통해 사용할 수 있습니다.

해당 취약점은 Flask 기반으로 만들어진 웹 서비스에서 발생했습니다.

CVE-2025-2945

PgAdmin4의 Query Tool 및 Cloud Deployment 모듈에서 발견된 원격 코드 실행 취약점으로, Python eval() 함수의 안전하지 않은 사용으로 인해 발생합니다.

영향 받는 엔드포인트

  • Query Tool (/sqleditor/query_tool/download/<int:trans_id>)
  • Cloud Deployment modules (/cloud/deploy)

공격 전제 조건

  • PgAdmin4 버전 9.2 미만
  • 웹 서비스 형태로 실행되는 PgAdmin4 환경
  • 인증된 사용자(로그인 가능)
  • 엔드포인트에 POST 요청이 가능해야함

취약한 코드

  • 마지막 줄을 보면 eval 함수를 통해 사용자의 입력 값을 실행하고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# https://github.com/pgadmin-org/pgadmin4/blob/REL-9_1/web/pgadmin/tools/sqleditor/__init__.py#L2124-L2160

@blueprint.route(
    '/query_tool/download/<int:trans_id>',
    methods=["POST"],
    endpoint='query_tool_download'
)
@pga_login_required
def start_query_download_tool(trans_id):
    (status, error_msg, sync_conn, trans_obj,
     session_obj) = check_transaction_status(trans_id)

    if not status or sync_conn is None or trans_obj is None or \
            session_obj is None:
        return internal_server_error(
            errormsg=TRANSACTION_STATUS_CHECK_FAILED
        )

    data = request.values if request.values else request.get_json(silent=True)
    if data is None:
        return make_json_response(
            status=410,
            success=0,
            errormsg=gettext(
                "Could not find the required parameter (query)."
            )
        )

    try:
        sql = None
        query_commited = data.get('query_commited', False)
        # Iterate through CombinedMultiDict to find query.
        for key, value in data.items():
            if key == 'query':
                sql = value
            if key == 'query_commited':
                query_commited = (
                   eval(value) if isinstance(value, str) else value # vuln code
                )

취약점 검증

1
2
3
4
5
1. python -m venv pgadmin //파이썬 가상환경
2. cd pgadmin/Script 
3. activate // 가상환경 실행
4. pip install pgadmin4-9.1-py3-none-any.whl //PgAdmin4 공식 홈페이지 다운로드
5. pgadmin4 //서비스 실행

[Result]

1
2
3
4
(pgadmin) C:\Users\AKM\Downloads\pgadmin\Scripts>pgadmin4
Starting pgAdmin 4. Please navigate to http://127.0.0.1:5050 in your browser.
 * Serving Flask app 'pgadmin'
 * Debug mode: off

Exploit

  1. postgres에 연결하여 trans_id를 얻어야합니다.
    • 쿼리 결과를 다운로드를 하여 trans_id를 획득합니다.

Download

  1. query_commited에 RCE 코드를 삽입 시 eval()을 통해 해당 코드가 실행됩니다. RCE

대응 방안

  • PgAdmin4를 9.2 버전 이상으로 업데이트
    • 9.2 미만 버전에 CVE-2025-2946(XSS) 추가 취약점이 발견되어 9.2버전에서 해소되었음
  • 9.2버전에서는 eval()을 안쓰도록 수정되었음

Conclusion

eval() 함수는 절때 쓰지 말아야 합니다….