VYPR
High severityNVD Advisory· Published Apr 4, 2024· Updated Mar 14, 2025

Remote Code Execution Vulnerability through the validate binary path API in pgAdmin 4

CVE-2024-3116

Description

pgAdmin <= 8.4 is affected by a Remote Code Execution (RCE) vulnerability through the validate binary path API. This vulnerability allows attackers to execute arbitrary code on the server hosting PGAdmin, posing a severe risk to the database management system's integrity and the security of the underlying data.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
pgadmin4PyPI
< 8.58.5

Affected products

1

Patches

1
fbbbfe22dd46

Fixed a remote code execution issue in the validate binary path (CVE-2024-3116). #7326

https://github.com/pgadmin-org/pgadmin4Khushboo VashiApr 1, 2024via ghsa
7 files changed · +59 10
  • docs/en_US/release_notes_8_5.rst+1 1 modified
    @@ -50,4 +50,4 @@ Bug fixes
       | `Issue #7305 <https://github.com/pgadmin-org/pgadmin4/issues/7305>`_ -  Fix an issue in query tool where custom keyboard shortcuts are not working for some.
       | `Issue #7304 <https://github.com/pgadmin-org/pgadmin4/issues/7304>`_ -  Fixed the issue where the update-user CLI command doesn't change the password.
       | `Issue #7308 <https://github.com/pgadmin-org/pgadmin4/issues/7308>`_ -  Fixed issue related to email authentication of Two-factor authentication.
    -
    +  | `Issue #7326 <https://github.com/pgadmin-org/pgadmin4/issues/7326>`_ -  Fixed a remote code execution issue in the validate binary path (CVE-2024-3116).
    
  • web/config.py+20 0 modified
    @@ -469,6 +469,26 @@
         "ppas-16": ""
     }
     
    +##########################################################################
    +
    +# Admin can specify fixed binary paths to prevent users from changing.
    +# It will take precedence over DEFAULT_BINARY_PATHS.
    +
    +FIXED_BINARY_PATHS = {
    +    "pg": "",
    +    "pg-12": "",
    +    "pg-13": "",
    +    "pg-14": "",
    +    "pg-15": "",
    +    "pg-16": "",
    +    "ppas": "",
    +    "ppas-12": "",
    +    "ppas-13": "",
    +    "ppas-14": "",
    +    "ppas-15": "",
    +    "ppas-16": ""
    +}
    +
     ##########################################################################
     # Test settings - used primarily by the regression suite, not for users
     ##########################################################################
    
  • web/pgadmin/browser/server_groups/servers/static/js/binary_path.ui.js+4 0 modified
    @@ -49,6 +49,10 @@ export default class BinaryPathSchema extends BaseUISchema {
           {
             id: 'binaryPath', label: gettext('Binary Path'), cell: 'file', type: 'file',
             isvalidate: true,
    +        disabled: function (state) {
    +          // If Fixed path is assigned, user will not able to edit it.
    +          return state?.isFixed ? state.isFixed : false;
    +        },
             controlProps: {
               dialogType: 'select_folder',
               supportedTypes: ['*', 'sql', 'backup'],
    
  • web/pgadmin/browser/server_groups/servers/types.py+11 5 modified
    @@ -11,7 +11,6 @@
     import json
     import config
     import copy
    -
     from flask import render_template
     from flask_babel import gettext as _
     from pgadmin.utils.preferences import Preferences
    @@ -240,15 +239,22 @@ def set_default_binary_path(bin_paths, server_type):
             """
             is_default_path_set = ServerType.is_default_binary_path_set(bin_paths)
             for path in config.DEFAULT_BINARY_PATHS:
    -            path_value = config.DEFAULT_BINARY_PATHS[path]
    +            is_fixed_path = (path in config.FIXED_BINARY_PATHS and
    +                             config.FIXED_BINARY_PATHS[path] != '' and
    +                             config.FIXED_BINARY_PATHS[path] is not None)
    +            path_value = (is_fixed_path and config.FIXED_BINARY_PATHS[path]
    +                          ) or config.DEFAULT_BINARY_PATHS[path]
    +
                 if path_value is not None and path_value != "" and \
                         path.find(server_type) == 0 and len(path.split('-')) > 1:
    -                set_binary_path(path_value, bin_paths, server_type,
    -                                path.split('-')[1])
    +                set_binary_path(
    +                    path_value, bin_paths, server_type, path.split('-')[1],
    +                    is_fixed_path=is_fixed_path)
                 elif path_value is not None and path_value != "" and \
                         path.find(server_type) == 0:
                     set_binary_path(path_value, bin_paths, server_type,
    -                                set_as_default=not is_default_path_set)
    +                                set_as_default=not is_default_path_set,
    +                                is_fixed_path=is_fixed_path)
     
     
     # Default Server Type
    
  • web/pgadmin/browser/templates/browser/js/utils.js+2 0 modified
    @@ -62,6 +62,8 @@ define('pgadmin.browser.utils',
       /* GET Binary Path Browse config */
       pgAdmin['enable_binary_path_browsing'] = '{{ current_app.config.get('ENABLE_BINARY_PATH_BROWSING') }}' == 'True';
     
    +  pgAdmin['fixed_binary_paths'] = {{ current_app.config.get('FIXED_BINARY_PATHS') }};
    +
       /* GET the pgadmin server's locale */
       pgAdmin['pgadmin_server_locale'] =  '{{pgadmin_server_locale}}';
     
    
  • web/pgadmin/misc/__init__.py+8 2 modified
    @@ -14,6 +14,7 @@
     from flask.helpers import url_for
     from flask_babel import gettext
     from flask_security import login_required
    +from pathlib import Path
     from pgadmin.utils import PgAdminModule, replace_binary_path, \
         get_binary_path_versions
     from pgadmin.utils.csrf import pgCSRFProtect
    @@ -234,7 +235,11 @@ def validate_binary_path():
             data = json.loads(data)
     
         version_str = ''
    -    if 'utility_path' in data and data['utility_path'] is not None:
    +
    +    # Do not allow storage dir as utility path
    +    if 'utility_path' in data and data['utility_path'] is not None and \
    +        Path(config.STORAGE_DIR) != Path(data['utility_path']) and \
    +            Path(config.STORAGE_DIR) not in Path(data['utility_path']).parents:
             binary_versions = get_binary_path_versions(data['utility_path'])
             for utility, version in binary_versions.items():
                 if version is None:
    @@ -248,7 +253,8 @@ def validate_binary_path():
         return make_json_response(data=gettext(version_str), status=200)
     
     
    -@blueprint.route("/upgrade_check", endpoint="upgrade_check", methods=['GET'])
    +@blueprint.route("/upgrade_check", endpoint="upgrade_check",
    +                 methods=['GET'])
     @login_required
     def upgrade_check():
         # Get the current version info from the website, and flash a message if
    
  • web/pgadmin/utils/__init__.py+13 2 modified
    @@ -14,13 +14,14 @@
     from collections import defaultdict
     from operator import attrgetter
     
    +from pathlib import Path
     from flask import Blueprint, current_app, url_for
     from flask_babel import gettext
     from flask_security import current_user, login_required
     from flask_security.utils import get_post_login_redirect, \
         get_post_logout_redirect
     from threading import Lock
    -
    +import config
     from .paths import get_storage_directory
     from .preferences import Preferences
     from pgadmin.utils.constants import UTILITIES_ARRAY, USER_NOT_FOUND, \
    @@ -308,11 +309,18 @@ def does_utility_exist(file):
         :return:
         """
         error_msg = None
    +
         if file is None:
             error_msg = gettext("Utility file not found. Please correct the Binary"
                                 " Path in the Preferences dialog")
             return error_msg
     
    +    if Path(config.STORAGE_DIR) == Path(file) or \
    +            Path(config.STORAGE_DIR) in Path(file).parents:
    +        error_msg = gettext("Please correct the Binary Path in the Preferences"
    +                            " dialog. pgAdmin storage directory can not be a"
    +                            " utility binary directory.")
    +
         if not os.path.exists(file):
             error_msg = gettext("'%s' file not found. Please correct the Binary"
                                 " Path in the Preferences dialog" % file)
    @@ -364,7 +372,8 @@ def get_binary_path_versions(binary_path: str) -> dict:
     
     
     def set_binary_path(binary_path, bin_paths, server_type,
    -                    version_number=None, set_as_default=False):
    +                    version_number=None, set_as_default=False,
    +                    is_fixed_path=False):
         """
         This function is used to iterate through the utilities and set the
         default binary path.
    @@ -394,6 +403,8 @@ def set_binary_path(binary_path, bin_paths, server_type,
                             if path_with_dir is not None else binary_path
                         if set_as_default:
                             path['isDefault'] = True
    +                    # Whether the fixed path in the config file exists or not
    +                    path['isFixed'] = is_fixed_path
                         break
                 break
             except Exception:
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.