Ysoserial Click1 Gadget 利用链深度分析
1. 概述
Click1 gadget是ysoserial工具中的一个利用链,基于Apache Click框架和Commons-Collections库的反序列化漏洞。该利用链通过精心构造的序列化对象,能够在反序列化过程中实现远程代码执行(RCE)。
2. 利用链核心组件
2.1 Source点: PriorityQueue
java.util.PriorityQueue是此利用链的起点,在反序列化过程中会调用其comparator属性的compare方法。
2.2 Sink点: TemplatesImpl
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl是最终的恶意代码执行点,通过其getOutputProperties()或newTransformer()方法触发恶意类加载。
2.3 关键桥梁: ColumnComparator
org.apache.click.control.Column$ColumnComparator替代了Commons-Collections中的TransformingComparator,用于连接Source和Sink点。
3. 利用链详细分析
3.1 反序列化触发流程
- PriorityQueue反序列化:触发
readObject()方法 - 调用comparator.compare():使用
ColumnComparator作为比较器 - ColumnComparator.compare():调用
column.getProperty() - PropertyUtils.getValue():通过反射调用目标方法
- 触发TemplatesImpl.getOutputProperties():最终导致恶意代码执行
3.2 ColumnComparator.compare方法分析
public int compare(Object row1, Object row2) {
this.ascendingSort = this.column.getTable().isSortedAscending() ? 1 : -1;
Object value1 = this.column.getProperty(row1);
Object value2 = this.column.getProperty(row2);
if (value1 instanceof Comparable && value2 instanceof Comparable) {
return !(value1 instanceof String) && !(value2 instanceof String)
? ((Comparable)value1).compareTo(value2) * this.ascendingSort
: this.stringCompare(value1, value2) * this.ascendingSort;
} else if (value1 != null && value2 != null) {
return value1.toString().compareToIgnoreCase(value2.toString()) * this.ascendingSort;
} else if (value1 != null && value2 == null) {
return 1 * this.ascendingSort;
} else {
return value1 == null && value2 != null ? -1 * this.ascendingSort : 0;
}
}
关键点在于column.getProperty(row1)调用,传入的row1就是构造好的恶意TemplatesImpl对象。
3.3 getProperty方法调用链
-
Column.getProperty(Object row):
public Object getProperty(Object row) { return this.getProperty(this.getName(), row); } -
Column.getProperty(String name, Object row):
public Object getProperty(String name, Object row) { if (row instanceof Map) { // 处理Map逻辑... } else { if (this.methodCache == null) { this.methodCache = new HashMap(); } return PropertyUtils.getValue(row, name, this.methodCache); } }由于
TemplatesImpl不是Map的子类,会进入PropertyUtils.getValue()调用。 -
PropertyUtils.getValue():
public static Object getValue(Object source, String name, Map cache) { String basePart = name; String remainingPart = null; if (source instanceof Map) { return ((Map)source).get(name); } else { int baseIndex = name.indexOf("."); if (baseIndex != -1) { basePart = name.substring(0, baseIndex); remainingPart = name.substring(baseIndex + 1); } Object value = getObjectPropertyValue(source, basePart, cache); return remainingPart != null && value != null ? getValue(value, remainingPart, cache) : value; } } -
PropertyUtils.getObjectPropertyValue():
private static Object getObjectPropertyValue(Object source, String name, Map cache) { PropertyUtils.CacheKey methodNameKey = new PropertyUtils.CacheKey(source, name); Method method = null; try { method = (Method)cache.get(methodNameKey); if (method == null) { method = source.getClass().getMethod(ClickUtils.toGetterName(name)); cache.put(methodNameKey, method); } return method.invoke(source); } catch (NoSuchMethodException var13) { // 尝试其他方法名格式... } }关键点:
ClickUtils.toGetterName(name)会将name转换为getter方法名。 -
ClickUtils.toGetterName():
public static String toGetterName(String property) { HtmlStringBuffer buffer = new HtmlStringBuffer(property.length() + 3); buffer.append("get"); buffer.append(Character.toUpperCase(property.charAt(0))); buffer.append(property.substring(1)); return buffer.toString(); }此方法将属性名转换为标准的getter方法名(首字母大写,前面加"get")。
3.4 触发恶意代码执行
通过控制Column的name属性,可以调用TemplatesImpl的任意getter方法。选择getOutputProperties()是因为:
getOutputProperties()是TemplatesImpl的公共方法- 它内部会调用
newTransformer() newTransformer()会触发恶意类的初始化
TemplatesImpl.getOutputProperties()实现:
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
} catch (TransformerConfigurationException e) {
return null;
}
}
4. 利用链构造要点
-
Column.name控制:通过
Column构造函数设置name为"outputProperties"public Column(String name) { if (name == null) { throw new IllegalArgumentException("Null name parameter"); } else { this.name = name; } } -
PriorityQueue构造:
- 设置comparator为
ColumnComparator实例 - 队列元素包含恶意构造的
TemplatesImpl对象
- 设置comparator为
-
TemplatesImpl准备:
- 包含恶意字节码
- 设置必要的属性使其能正确初始化
5. 防御措施
- 升级Apache Click框架到安全版本
- 使用反序列化过滤器,限制可反序列化的类
- 替换默认的Java反序列化机制
- 对来自不可信源的序列化数据进行严格验证
6. 总结
Click1 gadget利用链展示了如何通过精心构造的序列化对象,利用框架中的反射机制和特性,最终实现远程代码执行。理解这类利用链有助于开发者更好地防范反序列化漏洞,并设计更安全的系统架构。