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

教学文档:从Protobuf反序列化到SQL注入漏洞的挖掘与分析

一、 漏洞背景与核心思想

本案例涉及一个名为bip的Java应用程序。漏洞挖掘的起点是一个程序异常,研究者通过逆向工程和动态调试,追踪到一个基于Google Protocol Buffers(Protobuf)的反序列化过程,并最终发现了一个SQL注入漏洞。

核心思想:程序使用Protobuf格式接收和处理客户端数据。在成功反序列化数据后,程序会从反序列化得到的对象中提取参数,并在未经过充分安全过滤的情况下,直接将参数拼接到SQL语句中执行,从而导致SQL注入漏洞。

二、 技术知识点详解

1. Protocol Buffers (Protobuf)
  • 是什么:Google开发的一种语言中立、平台无关、可扩展的序列化数据结构的机制。它比XML/JSON更小、更快、更简单。
  • 在漏洞中的作用:攻击载荷(Payload)是通过Protobuf格式序列化的。要构造有效的攻击数据,必须先理解目标程序期望的Protobuf数据结构,并能够生成合法的序列化字节流。
  • 关键步骤
    1. 定义.proto文件:这是一个蓝图文件,描述了你要序列化的数据的结构。
    2. 生成Java代码:使用Protobuf编译器(protoc)根据.proto文件生成对应的Java类(如PBLoginInfo)。这些类提供了构建(Builder)、序列化(toByteArray())和反序列化(parseFrom())的方法。
    3. 序列化与反序列化:在客户端,使用生成的类构建对象并序列化成字节流发送。在服务端,接收字节流并反序列化回对象。
2. 漏洞触发流程分析

文章描述了清晰的漏洞触发链:

  1. 入口点:程序接收到经过Base64编码的Protobuf数据。
  2. 反序列化:程序调用parseFrom方法(内部会调用parsePartialFrommergeFrom)对数据进行反序列化,将其转换成一个PBLoginInfo Java对象。
  3. 数据提取:反序列化成功后,程序执行到this.loginContext.getAccountName().length() 这一行。为了通过此处的逻辑检查(避免空指针异常或长度校验失败),需要确保PBLoginInfo对象中的accountName字段有值。
  4. 漏洞触发:程序调用setInvocationInfo方法,在此方法内部,通过getUser()方法获取之前反序列化得到的用户信息(如accountName)。
  5. SQL注入getUser()方法内部,将用户输入的accountName直接拼接到SQL查询字符串中,形成了SQL注入漏洞。例如,拼接的代码可能类似于:
    String sql = "SELECT ... FROM ... WHERE account_name = '" + accountName + "'";
    
  6. 命令执行:在特定配置下(如SQL Server数据库且已开启xp_cmdshell),攻击者可以通过SQL注入执行系统命令,从而完全控制服务器。

三、 漏洞复现详细步骤

步骤一:重构Protobuf数据结构
  1. 通过逆向分析或调试,推断出目标程序用于反序列化的.proto文件结构。文中作者通过“拷打AI”得到了一个近似的数据结构定义(PBLoginInfo.proto)。
  2. 关键字段是account_name(字段编号10),因为漏洞点正是使用这个字段的值。
步骤二:生成PoC数据
  1. 使用protoc编译器,根据.proto文件生成Java类(ProtoBufferPractice.java)。
  2. 编写Java代码(MakeTest.java),利用生成的类构建一个PBLoginInfo对象,并为关键字段(尤其是accountName)赋值。注意:accountName的值需要构造为SQL注入载荷
    .setAccountName(ByteString.copyFromUtf8("bip' AND 1=@@VERSION;-- "))
    
  3. 将对象序列化为字节数组(info.toByteArray()),然后进行Base64编码,得到最终的Payload。
步骤三:发送Payload进行利用
  1. 将生成的Base64字符串作为请求参数,发送给存在漏洞的应用程序接口。
  2. 应用程序会执行上述“漏洞触发流程”,将恶意载荷拼接到SQL语句中,导致注入的SQL代码被执行。

四、 后利用思路

文章简要提到了漏洞利用后的进一步操作:

  1. 获取路径:利用另一个文件读取接口,通过触发报错信息来泄露Web应用程序在服务器上的绝对路径。
  2. 写入Webshell:在能够执行系统命令的前提下(通过xp_cmdshell),向泄露的绝对路径中写入一个JSP格式的Webshell文件。
  3. 控制服务器:通过访问这个Webshell,攻击者可以获得一个稳定的远程控制通道,进而完全控制服务器。

五、 关键总结与防御建议

关键点总结
  • 漏洞根源:并非Protobuf本身有漏洞,而是程序在使用反序列化后的数据时存在不安全操作(直接拼接SQL字符串)。
  • 技术难点:漏洞挖掘的难点在于逆向分析复杂的反序列化流程,并准确重构出Protobuf数据结构。一旦突破这个点,漏洞的利用就相对直接。
  • 攻击链构造Protobuf数据 -> 序列化/编码 -> 发送 -> 服务端反序列化 -> 危险数据使用 -> SQL注入
防御建议
  1. 永远不要信任用户输入:即使数据经过了复杂的序列化/反序列化过程,其内容仍然是用户可控的。
  2. 使用参数化查询(Prepared Statements):这是防止SQL注入最有效的方法。数据库驱动程序会正确处理参数,确保用户输入不会被解析为SQL代码。
  3. 对反序列化操作进行白名单校验:限制可以反序列化的类,避免反序列化漏洞。
  4. 最小权限原则:数据库连接账户应遵循最小权限原则,避免使用具有sysadmin或类似高权限的账户,特别是不要轻易开启xp_cmdshell等危险功能。

这份文档详尽地解析了从Protobuf反序列化到SQL注入漏洞的完整知识链。希望它对您的学习有所帮助!

教学文档:从Protobuf反序列化到SQL注入漏洞的挖掘与分析 一、 漏洞背景与核心思想 本案例涉及一个名为 bip 的Java应用程序。漏洞挖掘的起点是一个程序异常,研究者通过逆向工程和动态调试,追踪到一个基于Google Protocol Buffers(Protobuf)的反序列化过程,并最终发现了一个SQL注入漏洞。 核心思想 :程序使用Protobuf格式接收和处理客户端数据。在成功反序列化数据后,程序会从反序列化得到的对象中提取参数,并在未经过充分安全过滤的情况下,直接将参数拼接到SQL语句中执行,从而导致SQL注入漏洞。 二、 技术知识点详解 1. Protocol Buffers (Protobuf) 是什么 :Google开发的一种语言中立、平台无关、可扩展的序列化数据结构的机制。它比XML/JSON更小、更快、更简单。 在漏洞中的作用 :攻击载荷(Payload)是通过Protobuf格式序列化的。要构造有效的攻击数据,必须先理解目标程序期望的Protobuf数据结构,并能够生成合法的序列化字节流。 关键步骤 : 定义.proto文件 :这是一个蓝图文件,描述了你要序列化的数据的结构。 生成Java代码 :使用Protobuf编译器( protoc )根据.proto文件生成对应的Java类(如 PBLoginInfo )。这些类提供了构建(Builder)、序列化( toByteArray() )和反序列化( parseFrom() )的方法。 序列化与反序列化 :在客户端,使用生成的类构建对象并序列化成字节流发送。在服务端,接收字节流并反序列化回对象。 2. 漏洞触发流程分析 文章描述了清晰的漏洞触发链: 入口点 :程序接收到经过Base64编码的Protobuf数据。 反序列化 :程序调用 parseFrom 方法(内部会调用 parsePartialFrom 和 mergeFrom )对数据进行反序列化,将其转换成一个 PBLoginInfo Java对象。 数据提取 :反序列化成功后,程序执行到 this.loginContext.getAccountName().length() 这一行。为了通过此处的逻辑检查(避免空指针异常或长度校验失败),需要确保 PBLoginInfo 对象中的 accountName 字段有值。 漏洞触发 :程序调用 setInvocationInfo 方法,在此方法内部,通过 getUser() 方法获取之前反序列化得到的用户信息(如 accountName )。 SQL注入 : getUser() 方法内部,将用户输入的 accountName 直接拼接 到SQL查询字符串中,形成了SQL注入漏洞。例如,拼接的代码可能类似于: 命令执行 :在特定配置下(如SQL Server数据库且已开启 xp_cmdshell ),攻击者可以通过SQL注入执行系统命令,从而完全控制服务器。 三、 漏洞复现详细步骤 步骤一:重构Protobuf数据结构 通过逆向分析或调试,推断出目标程序用于反序列化的 .proto 文件结构。文中作者通过“拷打AI”得到了一个近似的数据结构定义( PBLoginInfo.proto )。 关键字段是 account_name (字段编号10),因为漏洞点正是使用这个字段的值。 步骤二:生成PoC数据 使用 protoc 编译器,根据 .proto 文件生成Java类( ProtoBufferPractice.java )。 编写Java代码( MakeTest.java ),利用生成的类构建一个 PBLoginInfo 对象,并为关键字段(尤其是 accountName )赋值。 注意: accountName 的值需要构造为SQL注入载荷 。 将对象序列化为字节数组( info.toByteArray() ),然后进行Base64编码,得到最终的Payload。 步骤三:发送Payload进行利用 将生成的Base64字符串作为请求参数,发送给存在漏洞的应用程序接口。 应用程序会执行上述“漏洞触发流程”,将恶意载荷拼接到SQL语句中,导致注入的SQL代码被执行。 四、 后利用思路 文章简要提到了漏洞利用后的进一步操作: 获取路径 :利用另一个文件读取接口,通过触发报错信息来泄露Web应用程序在服务器上的绝对路径。 写入Webshell :在能够执行系统命令的前提下(通过 xp_cmdshell ),向泄露的绝对路径中写入一个JSP格式的Webshell文件。 控制服务器 :通过访问这个Webshell,攻击者可以获得一个稳定的远程控制通道,进而完全控制服务器。 五、 关键总结与防御建议 关键点总结 漏洞根源 :并非Protobuf本身有漏洞,而是程序在 使用反序列化后的数据时存在不安全操作 (直接拼接SQL字符串)。 技术难点 :漏洞挖掘的难点在于逆向分析复杂的反序列化流程,并准确重构出Protobuf数据结构。一旦突破这个点,漏洞的利用就相对直接。 攻击链 : 构造Protobuf数据 -> 序列化/编码 -> 发送 -> 服务端反序列化 -> 危险数据使用 -> SQL注入 。 防御建议 永远不要信任用户输入 :即使数据经过了复杂的序列化/反序列化过程,其内容仍然是用户可控的。 使用参数化查询(Prepared Statements) :这是防止SQL注入最有效的方法。数据库驱动程序会正确处理参数,确保用户输入不会被解析为SQL代码。 对反序列化操作进行白名单校验 :限制可以反序列化的类,避免反序列化漏洞。 最小权限原则 :数据库连接账户应遵循最小权限原则,避免使用具有 sysadmin 或类似高权限的账户,特别是不要轻易开启 xp_cmdshell 等危险功能。 这份文档详尽地解析了从Protobuf反序列化到SQL注入漏洞的完整知识链。希望它对您的学习有所帮助!