某bip漏洞挖掘-从0day到1day的伤心之旅
字数 2989 2025-10-13 22:56:21

从代码审计到漏洞利用:某BIP系统0Day漏洞挖掘教学文档

文档概述

本教学文档基于一篇真实的技术文章,详细讲解了如何通过静态代码审计,在一个使用Google Protobuf进行数据序列化/反序列化的Java应用程序中,发现一处从反序列化数据到SQL注入的完整漏洞链。文档将遵循漏洞研究的自然流程:信息收集 -> 代码分析 -> 漏洞定位 -> PoC构造 -> 漏洞利用,旨在为安全研究人员提供一套可复用的方法论。

一、 漏洞背景与入口点分析

  1. 目标特征:目标系统(某BIP系统)在处理客户端数据时,采用了以下技术栈:

    • 数据传输:数据经过Base64编码。
    • 数据序列化:使用 Google Protocol Buffers (Protobuf) 作为序列化格式。Protobuf是Google开发的一种语言中立、平台无关、可扩展的机制,用于高效地序列化结构化数据。
    • 关键方法:在代码中,数据解析的入口是 parseFrom 方法,其内部调用 parsePartialFrommergeFrom 来完成复杂的反序列化流程。
  2. 初始线索:研究人员在调试过程中,程序抛出异常,从异常堆栈信息中可以清晰地看到 Protobuf 相关的类名(如 MessageBuilder 等)。这是判断目标使用了Protobuf的关键迹象。

    关键点:异常信息、日志文件、堆栈跟踪是漏洞挖掘的宝贵信息来源,常常能直接揭示底层使用的组件和技术。

二、 Protobuf快速入门与PoC构造

既然确定了序列化方式是Protobuf,下一步就是理解如何与之交互。

  1. 什么是Protobuf

    • 它需要先定义一个 .proto 文件,该文件描述了数据的结构(即“消息”的格式)。
    • 使用Protobuf编译器 (protoc) 根据 .proto 文件生成特定编程语言(如Java、C++、Python)的代码。
    • 应用程序使用生成的代码来构建、序列化和解析消息。
  2. 逆向.proto文件:目标程序已经包含了编译生成的Java类,研究人员需要逆向出大致的 .proto 文件结构,才能构造合法的序列化数据。文中提到,他们通过“拷打AI”或分析现有Java类,得到了一个近似的 .proto 文件定义:

    syntax = "proto2"; // 使用proto2语法
    package com.example.chain.ProtobufTest.TbMessagesDefFrame; // 包名,与Java类对应
    
    option java_outer_classname = "ProtoBufferPractice"; // 生成的Java外部类名
    
    // 定义了一个名为PBLoginInfo的消息,对应反序列化的目标类
    message PBLoginInfo {
      optional bytes userID               = 1;  // 字段规则(optional)、类型(bytes)、标签(编号)
      optional bytes userCode             = 2;
      // ... 其他字段省略 ...
      optional bytes accountName          = 10;
      // ... 更多字段 ...
      optional bytes language             = 22;
    }
    
    // 文中提到还有PBGroup和PBFuncNode消息,但字段未定义,说明在初步PoC中非必需。
    message PBGroup {}
    message PBFuncNode {}
    
  3. 生成Java类并构造PoC

    • 使用 protoc 编译器根据上述 .proto 文件生成 ProtoBufferPractice.java 类。
    • 编写Java代码,使用生成的Builder类来创建一个 PBLoginInfo 对象,并填充必要字段。
    package com.example.chain.ProtobufTest;
    import com.google.protobuf.ByteString;
    import java.util.Base64;
    
    public class MakeTest {
        public static void main(String[] args) {
            // 使用Builder模式构造PBLoginInfo对象
            ProtoBufferPractice.PBLoginInfo info = ProtoBufferPractice.PBLoginInfo.newBuilder()
                    .setUserID(ByteString.copyFromUtf8("u001"))
                    .setUserName(ByteString.copyFromUtf8("管理员"))
                    .setAccountName(ByteString.copyFromUtf8("bip")) // 关键字段:用于后续绕过检查
                    .setLanguage(ByteString.copyFromUtf8("zh_CN"))
                    .build();
    
            // 将对象序列化为字节数组,并进行Base64编码
            byte[] data = info.toByteArray();
            String base64EncodedData = Base64.getEncoder().encodeToString(data);
            System.out.println(base64EncodedData);
        }
    }
    
  4. 绕过初始检查:运行程序后,将输出的Base64字符串作为Payload发送给目标。这个Payload的目的是为了通过一个初始的IF条件检查:this.loginContext.getAccountName().length() > 0。通过设置 accountName 字段,我们确保了反序列化后的对象拥有该属性,从而顺利通过检查,进入更深层次的业务逻辑。

三、 漏洞发现与分析(核心)

在成功绕过初始检查后,研究人员继续跟踪程序流程,发现了真正的漏洞。

  1. 漏洞触发路径

    • 程序继续执行,进入 setInvocationInfo 方法。
    • 该方法内部调用了 getUser 方法。
  2. 漏洞代码分析(推测)

    • getUser 方法中,发现了SQL注入漏洞。关键点在于:程序直接将反序列化得到的用户可控数据(很可能是 accountName 或类似字段)拼接到了SQL查询语句中,而没有使用预编译语句(PreparedStatement)进行参数化查询。
    • 代码逻辑可能类似于:
      // 漏洞代码示例(非原代码,基于描述推断)
      String sql = "SELECT * FROM users WHERE account_name = '" + loginInfo.getAccountName().toStringUtf8() + "'";
      Statement stmt = connection.createStatement();
      ResultSet rs = stmt.executeQuery(sql); // 这里存在SQL注入
      
  3. 漏洞性质:这是一个典型的二次反序列化漏洞链。Protobuf反序列化本身并不是漏洞,但它是一个可靠的、可控的输入点。漏洞的根源在于业务逻辑错误地信任了反序列化后的数据,并进行了不安全的SQL拼接。

四、 漏洞复现与利用

  1. 构造恶意Payload:要利用这个SQL注入,需要构造一个特殊的 PBLoginInfo 对象,使其 accountName 字段包含SQL注入Payload。

    • 例如,将 accountName 设置为 bip' OR '1'='1 来进行基础验证。
    • 更高级的利用可能涉及联合查询(UNION SELECT)或堆叠查询(Stacked Queries,取决于数据库驱动支持),以窃取数据库信息。
  2. 利用条件与技巧

    • 关键条件:文中强调,AccountName 的值必须是一个有效的数据源名称。这表明后台的SQL逻辑可能与动态数据源有关,如果 accountName 不合法,程序可能会提前报错,无法执行到有漏洞的SQL语句。这需要研究人员对目标系统的配置有一定了解。
    • 利用步骤
      1. 使用修改后的 MakeTest 类,将恶意的SQL注入Payload设置到 accountName 字段。
      2. 将生成的Base64数据包发送给目标系统的特定接口。
      3. 观察服务器响应,通过报错信息或时间延迟来判断注入是否成功。

五、 后利用思路(权限提升与文件写入)

文章最后提到了一个完整的后利用场景,展示了漏洞的严重性。

  1. 前提:数据库用户权限较高,具备执行存储过程(如SQL Server的 xp_cmdshell)的权限。
  2. 步骤
    • 信息收集:利用另一个文件读取接口,通过制造报错来泄露Web应用的绝对路径
    • 写入Webshell:通过SQL注入漏洞,结合数据库的文件写入功能(如SQL Server的 OUTFILExp_cmdshell 调用 echo 命令等),将一个JSP格式的Webshell写入到泄露出的Web目录下。
    • 获取控制权:通过浏览器访问写入的Webshell,从而获得服务器命令执行权限。

六、 总结与关键知识点

关键步骤 核心知识点 说明
信息收集 异常分析、堆栈跟踪 从报错信息中识别第三方组件(Protobuf)。
技术理解 Google Protobuf 理解其序列化/反序列化原理,以及如何生成和解析数据。
PoC构造 逆向工程、代码生成 通过Java类反向推导.proto定义,并生成可交互的代码。
漏洞定位 代码审计、动态调试 跟踪反序列化后的数据流,发现不安全的SQL拼接点。
漏洞利用 SQL注入、数据包构造 构造包含恶意SQL的Protobuf数据,并编码传输。
后利用 权限提升、路径泄露、文件写入 将数据库权限转化为操作系统权限,获取完全控制。

根本原因:该漏洞链的根源在于 “对反序列化数据缺乏足够的信任边界验证” 。系统认为通过Protobuf反序列化而来的对象是内部可信的,从而直接用于危险操作(SQL拼接),最终导致安全边界被突破。


希望这份详尽的教学文档能够帮助您深入理解漏洞挖掘的流程和技巧。请务必在合法授权的环境下进行安全测试。

从代码审计到漏洞利用:某BIP系统0Day漏洞挖掘教学文档 文档概述 本教学文档基于一篇真实的技术文章,详细讲解了如何通过静态代码审计,在一个使用Google Protobuf进行数据序列化/反序列化的Java应用程序中,发现一处从反序列化数据到SQL注入的完整漏洞链。文档将遵循漏洞研究的自然流程: 信息收集 -> 代码分析 -> 漏洞定位 -> PoC构造 -> 漏洞利用 ,旨在为安全研究人员提供一套可复用的方法论。 一、 漏洞背景与入口点分析 目标特征 :目标系统(某BIP系统)在处理客户端数据时,采用了以下技术栈: 数据传输 :数据经过Base64编码。 数据序列化 :使用 Google Protocol Buffers (Protobuf) 作为序列化格式。Protobuf是Google开发的一种语言中立、平台无关、可扩展的机制,用于高效地序列化结构化数据。 关键方法 :在代码中,数据解析的入口是 parseFrom 方法,其内部调用 parsePartialFrom 和 mergeFrom 来完成复杂的反序列化流程。 初始线索 :研究人员在调试过程中,程序抛出异常,从异常堆栈信息中可以清晰地看到 Protobuf 相关的类名(如 Message 、 Builder 等)。这是判断目标使用了Protobuf的关键迹象。 关键点 :异常信息、日志文件、堆栈跟踪是漏洞挖掘的宝贵信息来源,常常能直接揭示底层使用的组件和技术。 二、 Protobuf快速入门与PoC构造 既然确定了序列化方式是Protobuf,下一步就是理解如何与之交互。 什么是Protobuf : 它需要先定义一个 .proto 文件,该文件描述了数据的结构(即“消息”的格式)。 使用Protobuf编译器 ( protoc ) 根据 .proto 文件生成特定编程语言(如Java、C++、Python)的代码。 应用程序使用生成的代码来构建、序列化和解析消息。 逆向.proto文件 :目标程序已经包含了编译生成的Java类,研究人员需要逆向出大致的 .proto 文件结构,才能构造合法的序列化数据。文中提到,他们通过“拷打AI”或分析现有Java类,得到了一个近似的 .proto 文件定义: 生成Java类并构造PoC : 使用 protoc 编译器根据上述 .proto 文件生成 ProtoBufferPractice.java 类。 编写Java代码,使用生成的Builder类来创建一个 PBLoginInfo 对象,并填充必要字段。 绕过初始检查 :运行程序后,将输出的Base64字符串作为Payload发送给目标。这个Payload的目的是为了通过一个初始的IF条件检查: this.loginContext.getAccountName().length() > 0 。通过设置 accountName 字段,我们确保了反序列化后的对象拥有该属性,从而顺利通过检查,进入更深层次的业务逻辑。 三、 漏洞发现与分析(核心) 在成功绕过初始检查后,研究人员继续跟踪程序流程,发现了真正的漏洞。 漏洞触发路径 : 程序继续执行,进入 setInvocationInfo 方法。 该方法内部调用了 getUser 方法。 漏洞代码分析(推测) : 在 getUser 方法中,发现了 SQL注入漏洞 。关键点在于:程序 直接将反序列化得到的用户可控数据(很可能是 accountName 或类似字段)拼接到了SQL查询语句中 ,而没有使用预编译语句(PreparedStatement)进行参数化查询。 代码逻辑可能类似于: 漏洞性质 :这是一个典型的 二次反序列化漏洞链 。Protobuf反序列化本身并不是漏洞,但它是一个 可靠的、可控的输入点 。漏洞的根源在于业务逻辑 错误地信任了反序列化后的数据 ,并进行了不安全的SQL拼接。 四、 漏洞复现与利用 构造恶意Payload :要利用这个SQL注入,需要构造一个特殊的 PBLoginInfo 对象,使其 accountName 字段包含SQL注入Payload。 例如,将 accountName 设置为 bip' OR '1'='1 来进行基础验证。 更高级的利用可能涉及联合查询(UNION SELECT)或堆叠查询(Stacked Queries,取决于数据库驱动支持),以窃取数据库信息。 利用条件与技巧 : 关键条件 :文中强调, AccountName 的值必须是一个有效的 数据源名称 。这表明后台的SQL逻辑可能与动态数据源有关,如果 accountName 不合法,程序可能会提前报错,无法执行到有漏洞的SQL语句。这需要研究人员对目标系统的配置有一定了解。 利用步骤 : 使用修改后的 MakeTest 类,将恶意的SQL注入Payload设置到 accountName 字段。 将生成的Base64数据包发送给目标系统的特定接口。 观察服务器响应,通过报错信息或时间延迟来判断注入是否成功。 五、 后利用思路(权限提升与文件写入) 文章最后提到了一个完整的后利用场景,展示了漏洞的严重性。 前提 :数据库用户权限较高,具备执行存储过程(如SQL Server的 xp_cmdshell )的权限。 步骤 : 信息收集 :利用另一个文件读取接口,通过制造报错来泄露Web应用的 绝对路径 。 写入Webshell :通过SQL注入漏洞,结合数据库的文件写入功能(如SQL Server的 OUTFILE 、 xp_cmdshell 调用 echo 命令等),将一个JSP格式的Webshell写入到泄露出的Web目录下。 获取控制权 :通过浏览器访问写入的Webshell,从而获得服务器命令执行权限。 六、 总结与关键知识点 | 关键步骤 | 核心知识点 | 说明 | | :--- | :--- | :--- | | 信息收集 | 异常分析、堆栈跟踪 | 从报错信息中识别第三方组件(Protobuf)。 | | 技术理解 | Google Protobuf | 理解其序列化/反序列化原理,以及如何生成和解析数据。 | | PoC构造 | 逆向工程、代码生成 | 通过Java类反向推导.proto定义,并生成可交互的代码。 | | 漏洞定位 | 代码审计、动态调试 | 跟踪反序列化后的数据流,发现不安全的SQL拼接点。 | | 漏洞利用 | SQL注入、数据包构造 | 构造包含恶意SQL的Protobuf数据,并编码传输。 | | 后利用 | 权限提升、路径泄露、文件写入 | 将数据库权限转化为操作系统权限,获取完全控制。 | 根本原因 :该漏洞链的根源在于 “对反序列化数据缺乏足够的信任边界验证” 。系统认为通过Protobuf反序列化而来的对象是内部可信的,从而直接用于危险操作(SQL拼接),最终导致安全边界被突破。 希望这份详尽的教学文档能够帮助您深入理解漏洞挖掘的流程和技巧。请务必在合法授权的环境下进行安全测试。