<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Drupal on 诗与胡说</title>
    <link>https://kylingit.com/tags/drupal/index.xml</link>
    <description>Recent content in Drupal on 诗与胡说</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh_cn</language>
    <copyright>Copyright © 2021 Kylinking</copyright>
    <atom:link href="https://kylingit.com/tags/drupal/index.xml" rel="self" type="application/rss+xml" />
    
    <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>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>由PHPGGC理解PHP反序列化漏洞</title>
      <link>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/</link>
      <pubDate>Mon, 08 Oct 2018 14:43:41 +0000</pubDate>
      
      <guid>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/</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;0x01-概述&#34;&gt;0x01 概述&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/ambionics/phpggc&#34;&gt;PHPGGC&lt;/a&gt;是一款能够自动生成主流框架的序列化测试&lt;code&gt;payload&lt;/code&gt;的工具，类似Java中的&lt;a href=&#34;https://github.com/frohoff/ysoserial&#34;&gt;ysoserial&lt;/a&gt;，当前支持的框架包括&lt;code&gt;Doctrine&lt;/code&gt;, &lt;code&gt;Guzzle&lt;/code&gt;, &lt;code&gt;Laravel&lt;/code&gt;, &lt;code&gt;Magento&lt;/code&gt;, &lt;code&gt;Monolog&lt;/code&gt;, &lt;code&gt;Phalcon&lt;/code&gt;, &lt;code&gt;Slim&lt;/code&gt;, &lt;code&gt;SwiftMailer&lt;/code&gt;, &lt;code&gt;Symfony&lt;/code&gt;, &lt;code&gt;Yii&lt;/code&gt; 和 &lt;code&gt;ZendFramework&lt;/code&gt;，可以说是反序列化的武器库了。本文从该工具出发，以Drupal Yaml反序列化漏洞和Typo3反序列化漏洞为例，分析其中的多种利用方式，并介绍一下今年BlackHat议题关于新型php反序列化攻击的部分。&lt;/p&gt;

&lt;h2 id=&#34;0x02-drupal-yaml反序列化漏洞&#34;&gt;0x02 Drupal Yaml反序列化漏洞&lt;/h2&gt;

&lt;h3 id=&#34;一-介绍&#34;&gt;一、介绍&lt;/h3&gt;

&lt;p&gt;关于&lt;code&gt;Drupal&lt;/code&gt;就不过多介绍了，前阵子两个RCE漏洞杀伤力巨大，这次介绍的是去年披露的关于反序列化的漏洞，&lt;a href=&#34;https://nvd.nist.gov/vuln/detail/CVE-2017-6920&#34;&gt;CVE-2017-6920&lt;/a&gt;，官方描述是YAML解析器处理不当导致的一个远程代码执行漏洞&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3 id=&#34;pecl-yaml-parser-unsafe-object-handling-critical-drupal-8-cve-2017-6920&#34;&gt;PECL YAML parser unsafe object handling - Critical - Drupal 8 - CVE-2017-6920&lt;/h3&gt;

&lt;p&gt;PECL YAML parser does not handle PHP objects safely during certain operations within Drupal core. This could lead to remote code execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;详情见&lt;a href=&#34;https://www.drupal.org/forum/newsletters/security-advisories-for-drupal-core/2017-06-21/drupal-core-multiple&#34;&gt;SA-CORE-2017-003&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;二-漏洞分析&#34;&gt;二、漏洞分析&lt;/h3&gt;

&lt;p&gt;先来看一下补丁，diff 8.3.3 和 8.3.4 版本，主要修改点在&lt;code&gt;core/lib/Drupal/Component/Serialization/YamlPecl.php&lt;/code&gt;文件&lt;code&gt;decode&lt;/code&gt;方法&lt;/p&gt;

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

&lt;p&gt;可见在&lt;code&gt;yaml_parse&lt;/code&gt;前进行了&lt;code&gt;ini_set(&#39;yaml.decode_php&#39;, 0);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;用户可控制的参数&lt;code&gt;$raw&lt;/code&gt;直接传给了&lt;code&gt;yaml_parse&lt;/code&gt;函数，而在手册上关于&lt;code&gt;yaml_parse&lt;/code&gt;函数有这么一个注意点：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning&lt;/p&gt;

&lt;p&gt;Processing untrusted user input with yaml_parse() is dangerous if the use of unserialize() is enabled for nodes using the !php/object tag. This behavior can be disabled by using the yaml.decode_php ini setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，如果使用了&lt;code&gt;yaml&lt;/code&gt;标志&lt;code&gt;!php/object&lt;/code&gt;，那么这个内容会通过&lt;code&gt;unserialize()&lt;/code&gt;进行处理，设置&lt;code&gt;yaml.decode_php&lt;/code&gt;则可以禁止，这就是为什么补丁增加了这行代码。&lt;/p&gt;

&lt;p&gt;看一下调用&lt;code&gt;decode()&lt;/code&gt;方法的地方，&lt;code&gt;core/lib/Drupal/Component/Serialization/Yaml.php&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;public static function decode($raw) {
    $serializer = static::getSerializer();
    return $serializer::decode($raw);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在&lt;code&gt;Yaml&lt;/code&gt;类的&lt;code&gt;decode()&lt;/code&gt;方法调用了&lt;code&gt;static::getSerializer()&lt;/code&gt;方法，跟入&lt;/p&gt;

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

&lt;p&gt;可以看到加载了&lt;code&gt;yaml&lt;/code&gt;扩展后就会进入&lt;code&gt;YamlPecl&lt;/code&gt;类，进而调用&lt;code&gt;Yaml::decode()&lt;/code&gt;方法，搜索调用&lt;code&gt;Yaml::decode&lt;/code&gt;并且参数能被控制的地方，在&lt;code&gt;core/modules/config/src/Form/ConfigSingleImportForm.php&lt;/code&gt;的&lt;code&gt;validateForm()&lt;/code&gt;方法：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$data = Yaml::decode($form_state-&amp;gt;getValue(&#39;import&#39;));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;validateForm()&lt;/code&gt;的调用处在&lt;code&gt;http://127.0.0.1/drupal-8.3.3/admin/config/development/configuration/single/import&lt;/code&gt;，&lt;code&gt;decode()&lt;/code&gt;的参数直接从表单获取，于是通过&lt;code&gt;import&lt;/code&gt;将恶意参数传递进去。&lt;/p&gt;

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

&lt;p&gt;现在我们需要找到一个类，使之在被反序列化的时候执行危险函数，常规搜索&lt;code&gt;_destruct&lt;/code&gt;、&lt;code&gt;_tostring&lt;/code&gt;以及&lt;code&gt;_wakeup&lt;/code&gt;方法，在&lt;code&gt;drupal&lt;/code&gt;核心中有这么三个类可以被利用，其中两个在&lt;code&gt;phpggc&lt;/code&gt;工具中已经集成 ，另一个我们手动加入到&lt;code&gt;phpggc&lt;/code&gt;中&lt;/p&gt;

&lt;h4 id=&#34;1-远程代码执行&#34;&gt;1.  远程代码执行&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;vendor/guzzlehttp/psr7/src/FnStream.php&lt;/code&gt; &lt;code&gt;FnStream&lt;/code&gt;类的&lt;code&gt;__destruct()&lt;/code&gt;方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;  public function __destruct()
  {
      if (isset($this-&amp;gt;_fn_close)) {
          call_user_func($this-&amp;gt;_fn_close);
      }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们通过序列化这个类，传递参数&lt;code&gt;_fn_close&lt;/code&gt;为任意php代码，在&lt;code&gt;yaml_parse&lt;/code&gt;的时候反序列化便可以造成一个任意代码执行。&lt;/p&gt;

&lt;p&gt;在PHPGGC中已经内置这个类，查看信息&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1539050117155.png&#34; alt=&#34;1539050117155&#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/1539050287387.png&#34; alt=&#34;1539050287387&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;phpggc&lt;/code&gt;将&lt;code&gt;_fn_close&lt;/code&gt;参数设置为&lt;code&gt;HandlerStack&lt;/code&gt;类，再在&lt;code&gt;HandlerStack&lt;/code&gt;序列化的时候传入可控参数&lt;code&gt;$handler&lt;/code&gt;，而在这个案例中我们不需要额外的&lt;code&gt;HandlerStack&lt;/code&gt;类了，所以对&lt;code&gt;generate()&lt;/code&gt;方法稍加修改，直接构造一个&lt;code&gt;FnStream&lt;/code&gt;类，注意参数是&lt;code&gt;array&lt;/code&gt;类型：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;return new \GuzzleHttp\Psr7\FnStream([
    &#39;close&#39; =&amp;gt; $code
]);
&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/1539050803329.png&#34; alt=&#34;1539050803329&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接着拼接&lt;code&gt;YAML_PHP_TAG&lt;/code&gt;即&lt;code&gt;!php/object&lt;/code&gt;，并且要将字符串转义，注意序列化数据中的空字符，我们将其替换成&lt;code&gt;\0&lt;/code&gt;，最终生成的字符串如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;!php/object &amp;quot;O:24:\&amp;quot;GuzzleHttp\\Psr7\\FnStream\&amp;quot;:2:{s:33:\&amp;quot;\0GuzzleHttp\\Psr7\\FnStream\0methods\&amp;quot;;a:1:{s:5:\&amp;quot;close\&amp;quot;;s:7:\&amp;quot;phpinfo\&amp;quot;;}s:9:\&amp;quot;_fn_close\&amp;quot;;s:7:\&amp;quot;phpinfo\&amp;quot;;}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在&lt;code&gt;http://127.0.0.1/drupal-8.3.3/admin/config/development/configuration/single/import&lt;/code&gt;import序列化后的数据，便可以执行代码&lt;/p&gt;

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

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

&lt;h4 id=&#34;2-任意文件写入&#34;&gt;2.  任意文件写入&lt;/h4&gt;

&lt;p&gt;上面&lt;code&gt;call_user_func()&lt;/code&gt;造成了一个任意代码执行，我们再找到一个&lt;code&gt;file_put_contents()&lt;/code&gt;造成任意文件写入&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php&lt;/code&gt;的&lt;code&gt;FileCookieJar&lt;/code&gt;类&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;__destruct()&lt;/code&gt;调用&lt;code&gt;save()&lt;/code&gt;方法，通过&lt;code&gt;file_put_contents()&lt;/code&gt;写入文件内容，而文件名和文件内容均是我们可以控制的，所以此处可以写入一个shell&lt;/p&gt;

&lt;p&gt;同样地看一下&lt;code&gt;phpggc&lt;/code&gt;中有关&lt;code&gt;FileCookieJar&lt;/code&gt;类的部分：&lt;code&gt;gadgetchains/Guzzle/FW/1/chain.php&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1539052187458.png&#34; alt=&#34;1539052187458&#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/1539052263375.png&#34; alt=&#34;1539052263375&#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/1539052815893.png&#34; alt=&#34;1539052815893&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接着还是拼接和转义，并import数据&lt;/p&gt;

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

&lt;p&gt;写入的是整个json字符串，但是不影响代码执行。&lt;/p&gt;

&lt;h4 id=&#34;3-任意文件删除&#34;&gt;3.  任意文件删除&lt;/h4&gt;

&lt;p&gt;另一个类是&lt;code&gt;WindowsPipes&lt;/code&gt;，路径&lt;code&gt;vendor/symfony/process/Pipes/WindowsPipes.php&lt;/code&gt;，该类可以造成文件删除&lt;/p&gt;

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

&lt;p&gt;在&lt;code&gt;phpggc&lt;/code&gt;中没有内置这个类，于是我们按照这个工具的框架来实现一下，方便理解该工具。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;lib/PHPGGC/GadgetChain.php&lt;/code&gt;已经有&lt;code&gt;TYPE_FD&lt;/code&gt;这个类型，代表&lt;code&gt;file_delete&lt;/code&gt;，那么我们直接在&lt;code&gt;lib/PHPGGC/GadgetChain/&lt;/code&gt;注册一个&lt;code&gt;FileDelete&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php

namespace PHPGGC\GadgetChain;

abstract class FileDelete extends \PHPGGC\GadgetChain
{
    public static $type = self::TYPE_FD;
    public $parameters = [
        &#39;file_name&#39;
    ];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个类就可以作为POP链拿来使用了&lt;/p&gt;

&lt;p&gt;然后在&lt;code&gt;Symfony&lt;/code&gt;目录下创建&lt;code&gt;RMF/1&lt;/code&gt;，并创建&lt;code&gt;chain&lt;/code&gt;和&lt;code&gt;gadgets&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gadgetchains/Symfony/RMF/1/chain.php&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php

namespace GadgetChain\Symfony;

class RMF1 extends \PHPGGC\GadgetChain\FileDelete
{
    public $version = &#39;2.6 &amp;lt;= 2.8.32&#39;;
    public $vector = &#39;__destruct&#39;;
    public $author = &#39;crlf&#39;;
    public $informations = &#39;Remove remote file.&#39;;

    public function generate(array $parameters)
    {
        $input = $parameters[&#39;file_name&#39;];

        return new \Symfony\Component\Process\Pipes\WindowsPipes($input);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;gadgetchains/Symfony/RMF/1/gadgets.php&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php

namespace Symfony\Component\Process\Pipes;


class WindowsPipes
{
    private $files = array();

    public function __construct($input)
    {
        $this-&amp;gt;files = array($input);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们直接生成&lt;code&gt;WindowsPipes&lt;/code&gt;的序列化数据，把文件名作为参数传入，在反序列化的时候自动调用&lt;code&gt;removeFiles()&lt;/code&gt;，实现任意文件删除&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/1539053814915.png&#34; alt=&#34;1539053814915&#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/1539053942995.png&#34; alt=&#34;1539053942995&#34; /&gt;&lt;/p&gt;

&lt;p&gt;import后文件被删除&lt;/p&gt;

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

&lt;h3 id=&#34;四-总结&#34;&gt;四、总结&lt;/h3&gt;

&lt;p&gt;通过这个漏洞对&lt;code&gt;phpggc&lt;/code&gt;工具有了一定了解，我们可以添加自定义&lt;code&gt;POP&lt;/code&gt;链到工具中，用来丰富这个武器库。&lt;/p&gt;

&lt;p&gt;另外针对这个漏洞，文件写入和文件删除都需要知道网站的绝对路径，加上需要登录后才能利用，一定程度上加大了利用难度。&lt;/p&gt;

&lt;h2 id=&#34;0x03-typo3反序列化漏洞&#34;&gt;0x03 Typo3反序列化漏洞&lt;/h2&gt;

&lt;h3 id=&#34;一-介绍-1&#34;&gt;一、介绍&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Typo3&lt;/code&gt;也是一款著名的CMS，但是在国内流行程度不如&lt;code&gt;Wordpress&lt;/code&gt;。这个漏洞是今年&lt;code&gt;BlackHat&lt;/code&gt;大会上由&lt;code&gt;Sam Thomas&lt;/code&gt;分享反序列化漏洞议题时作为案例来讲的，该漏洞由&lt;code&gt;phar://&lt;/code&gt;触发，这是一个新型的反序列化利用方式，日常开发中容易忽略这个风险点，在漏洞利用中也用到了&lt;code&gt;phpggc&lt;/code&gt;这个工具，所以一并学习。&lt;/p&gt;

&lt;p&gt;关于该漏洞的官方描述，可以看&lt;a href=&#34;https://typo3.org/security/advisory/typo3-core-sa-2018-002/&#34;&gt;这里&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;二-phar-介绍&#34;&gt;二、phar://介绍&lt;/h3&gt;

&lt;p&gt;在分析漏洞前先介绍一下&lt;code&gt;phar://&lt;/code&gt;伪协议，直接看php手册的&lt;a href=&#34;http://php.net/manual/en/intro.phar.php&#34;&gt;介绍&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Phar archives are best characterized as a convenient way to group several files into a single file. As such, a phar archive provides a way to distribute a complete PHP application in a single file and run it from that file without the need to extract it to disk. Additionally, phar archives can be executed by PHP as easily as any other file, both on the commandline and from a web server. Phar is kind of like a thumb drive for PHP applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;简单来说&lt;code&gt;phar&lt;/code&gt;就是&lt;code&gt;php&lt;/code&gt;压缩文档。它可以把多个文件归档到同一个文件中，而且不经过解压就能被php访问并执行，与&lt;code&gt;file://&lt;/code&gt; &lt;code&gt;php://&lt;/code&gt;等类似，也是一种流包装器。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;phar&lt;/code&gt;结构由4部分组成&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stub&lt;/code&gt;  phar文件标识，格式为 &lt;code&gt;xxx&amp;lt;?php xxx; __HALT_COMPILER();?&amp;gt;；&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;manifest&lt;/code&gt;  压缩文件的属性等信息，以&lt;strong&gt;序列化&lt;/strong&gt;存储；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contents&lt;/code&gt;  压缩文件的内容；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;signature&lt;/code&gt;  签名，放在文件末尾；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里有两个关键点，一是文件标识，必须以&lt;code&gt;__HALT_COMPILER();?&amp;gt;&lt;/code&gt;结尾，但前面的内容没有限制，也就是说我们可以轻易伪造一个图片文件或者&lt;code&gt;pdf&lt;/code&gt;文件来绕过一些上传限制；二是反序列化，&lt;code&gt;phar&lt;/code&gt;存储的&lt;code&gt;meta-data&lt;/code&gt;信息以序列化方式存储，当文件操作函数通过&lt;code&gt;phar://&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/1539063733837.png&#34; alt=&#34;1539063733837&#34; /&gt;&lt;/p&gt;

&lt;p&gt;图片来自&lt;a href=&#34;https://paper.seebug.org/680/&#34;&gt;seebug&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这中间大多数是常用的函数，在一个系统中使用相当广泛，结合文件伪造，使得通过&lt;code&gt;phar://&lt;/code&gt;解析造成的反序列化攻击变得愈加容易。&lt;/p&gt;

&lt;h3 id=&#34;三-漏洞分析&#34;&gt;三、漏洞分析&lt;/h3&gt;

&lt;p&gt;接下来看一下Typo3中存在漏洞的代码&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;typo3/sysext/core/Classes/Database/SoftReferenceIndex.php&lt;/code&gt;的&lt;code&gt;getTypoLinkParts()&lt;/code&gt;方法&lt;/p&gt;

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

&lt;p&gt;上面说到存在风险的文件操作函数，其中就包括&lt;code&gt;file_exists()&lt;/code&gt;，当传给&lt;code&gt;file_exists()&lt;/code&gt;的参数是&lt;code&gt;phar&lt;/code&gt;压缩文档并通过&lt;code&gt;phar://&lt;/code&gt;伪协议解析时，就会反序列化其中的&lt;code&gt;metadata&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/1539064567894.png&#34; alt=&#34;1539064567894&#34; /&gt;&lt;/p&gt;

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

&lt;p&gt;可以看到通过&lt;code&gt;file_exists()&lt;/code&gt;函数判断文件是否存在即对&lt;code&gt;TestObject&lt;/code&gt;类进行了反序列化。&lt;/p&gt;

&lt;p&gt;那么我们可以构造&lt;code&gt;$splitLinkParam&lt;/code&gt;参数为&lt;code&gt;phar&lt;/code&gt;文件，其中包含恶意代码，传递给&lt;code&gt;file_exists()&lt;/code&gt;函数，便会触发漏洞。&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&#34;https://github.com/s-n-t/phpggc&#34;&gt;pharggc&lt;/a&gt;是在&lt;code&gt;phpggc&lt;/code&gt;的基础上增加了对&lt;code&gt;phar&lt;/code&gt;的支持，能够将序列化后的&lt;code&gt;payload&lt;/code&gt;写入到&lt;code&gt;phar&lt;/code&gt;文件中，通过&lt;code&gt;phar://&lt;/code&gt;解析时触发&lt;code&gt;payload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;我们已经找到可以触发漏洞的地方，但还需要一个类来执行代码，在Typo3中同样存在&lt;code&gt;FnStream&lt;/code&gt;类，所以我们还是使用&lt;code&gt;guzzle/rce1&lt;/code&gt;载荷将数据写入一张图片中&lt;/p&gt;

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

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

&lt;p&gt;然后上传这个附件，接着创建一个页面，将Link设置为&lt;code&gt;phar://&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/1539067380611.png&#34; alt=&#34;1539067380611&#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/1539067384583.png&#34; alt=&#34;1539067384583&#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/1539070664504.png&#34; alt=&#34;1539070664504&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;五-总结及防御&#34;&gt;五、总结及防御&lt;/h3&gt;

&lt;p&gt;这个漏洞利用的是&lt;code&gt;phar&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;限制PHP流包装器的使用(传递)；&lt;/li&gt;
&lt;li&gt;控制文件操作函数的参数，过滤特殊字符，例如 phar:// 等；&lt;/li&gt;
&lt;li&gt;仅在特别请求时才对元数据进行反序列化；&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;本文分析了两个漏洞，并结合&lt;code&gt;phpggc&lt;/code&gt;工具梳理了反序列化漏洞常见的攻击方式，以及如何寻找一条可以利用的POP链，也提到了开发中容易忽略的安全风险，希望能给大家起到帮助。&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&#34;https://paper.seebug.org/334/&#34;&gt;https://paper.seebug.org/334/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://paper.seebug.org/680&#34;&gt;https://paper.seebug.org/680&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/ambionics/phpggc&#34;&gt;https://github.com/ambionics/phpggc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/s-n-t/phpggc&#34;&gt;https://github.com/s-n-t/phpggc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://php.net/manual/en/function.yaml-parse.php&#34;&gt;http://php.net/manual/en/function.yaml-parse.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://php.net/manual/en/intro.phar.php&#34;&gt;http://php.net/manual/en/intro.phar.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.drupal.org/forum/newsletters/security-advisories-for-drupal-core/2017-06-21/drupal-core-multiple&#34;&gt;https://www.drupal.org/forum/newsletters/security-advisories-for-drupal-core/2017-06-21/drupal-core-multiple&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://typo3.org/security/advisory/typo3-core-sa-2018-002/&#34;&gt;https://typo3.org/security/advisory/typo3-core-sa-2018-002/&lt;/a&gt;&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2018-7602 Drupal 内核远程代码执行漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2018-7602-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>Thu, 26 Apr 2018 17:21:11 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2018-7602-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;h4 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h4&gt;

&lt;p&gt;4月25日，Drupal官方发布通告，Drupal Core 存在一个远程代码执行漏洞，影响 7.x 和 8.x 版本。据分析，这个漏洞是&lt;code&gt;CVE-2018-7600&lt;/code&gt;的绕过利用，两个漏洞原理是一样的，通告还称，已经发现了这个漏洞和&lt;code&gt;CVE-2018-7600&lt;/code&gt;的在野利用，详情请看 &lt;a href=&#34;https://www.drupal.org/sa-core-2018-004&#34;&gt;https://www.drupal.org/sa-core-2018-004&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Drupal 6.x，7.x，8.x&lt;/p&gt;

&lt;p&gt;修复版本
Drupal 7.59，Drupal 8.4.8，Drupal 8.5.3&lt;/p&gt;

&lt;h4 id=&#34;0x03-环境搭建&#34;&gt;0x03 环境搭建&lt;/h4&gt;

&lt;p&gt;历史版本
&lt;a href=&#34;https://www.drupal.org/project/drupal/releases&#34;&gt;https://www.drupal.org/project/drupal/releases&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;分析还是以7.57版本为例。跟7600漏洞的7.x版本很相似，只不过入口不一样，可以参考&lt;a href=&#34;http://blog.nsfocus.net/cve-2018-7600-drupal-7-x/&#34;&gt;http://blog.nsfocus.net/cve-2018-7600-drupal-7-x/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;上回漏洞的关键点是让系统缓存一个&lt;code&gt;form_build_id&lt;/code&gt;，这个form存着我们传入的恶意参数，第二个请求从中取出来然后执行。
这次的原理还是一样，触发漏洞还是需要发两个post包，一个存入&lt;code&gt;form_build_id&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/VGL3Y&#34; alt=&#34;delete&#34; /&gt;
请求中每个node即代表一篇文章。
可以看到是会重定向到文章页面的，根据上个漏洞的分析我们猜测，一定还是走到了&lt;code&gt;drupal_redirect_form()&lt;/code&gt;，我们已经知道如果走到&lt;code&gt;drupal_redirect_form()&lt;/code&gt;分支，是不会往数据库缓存&lt;code&gt;form_build_id&lt;/code&gt;的，我们的目的还是让程序不满足一定条件从而不进行表单提交后重定向，所以还是跟着&lt;code&gt;CVE-2018-7600&lt;/code&gt;的套路来走&lt;/p&gt;

&lt;p&gt;从代码层面看一下&lt;/p&gt;

&lt;p&gt;之前的流程还是一样，直接跳到&lt;code&gt;drupal_build_form()&lt;/code&gt;方法第386行&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;drupal_process_form($form_id, $form, $form_state);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;跟入&lt;code&gt;drupal_process_form()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/w0cQZ&#34; alt=&#34;drupal_process_form&#34; /&gt;
还是一样，&lt;code&gt;$form_state[&#39;submitted&#39;]&lt;/code&gt;被设置为true&lt;/p&gt;

&lt;p&gt;回到902行&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if ($form_state[&#39;submitted&#39;] &amp;amp;&amp;amp; !form_get_errors() &amp;amp;&amp;amp; !$form_state[&#39;rebuild&#39;])&lt;/code&gt;
条件被满足，进入这个分支便会执行&lt;code&gt;drupal_redirect_form()&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;而在这一步之前需要经过的判断是&lt;code&gt;_form_element_triggered_scripted_submission()&lt;/code&gt;
所以回到一开始的问题，构造一个&lt;code&gt;_triggering_element_value&lt;/code&gt;使得键值对相等，从而不进行rebuild&lt;/p&gt;

&lt;p&gt;我们传入&lt;code&gt;_triggering_element_name=form_id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/UBCWM&#34; alt=&#34;post&#34; /&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/Rbrm4&#34; alt=&#34;form_id&#34; /&gt;
可以看到条件被满足，&lt;code&gt;$form_state[&#39;submitted&#39;]&lt;/code&gt;没有被设置为true，还是保持默认值false&lt;/p&gt;

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

&lt;p&gt;进入&lt;code&gt;drupal_rebuild_form()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/WdeFV&#34; alt=&#34;drupal_rebuild_form&#34; /&gt;
表单被缓存&lt;/p&gt;

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

&lt;p&gt;然后我们发送第二个post包来取出我们构造好的form，向&lt;strong&gt;&lt;code&gt;file/ajax/actions/cancel/%23options/path&lt;/code&gt;&lt;/strong&gt;发起请求&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/voiue&#34; alt=&#34;post2&#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/luJMk&#34; alt=&#34;file_ajax_upload&#34; /&gt;
最终还是跟入到
&lt;code&gt;$output = drupal_render($form);&lt;/code&gt;
根据前几次的经验，我们还是选择&lt;code&gt;&#39;#post_render&#39;&lt;/code&gt;参数，&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/GV3Rc&#34; alt=&#34;post_render&#34; /&gt;
假如我们能控制这个参数，在&lt;code&gt;drupal_render()&lt;/code&gt;方法里就会把这个参数作为&lt;code&gt;$function&lt;/code&gt;函数名，而传给它的参数则是&lt;code&gt;[%23markup]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;所以问题回到了一开始，我们需要传递什么样的恶意参数，可以让系统直接接收而不经过过滤，还是之前的套路，搜索module下删除文章的相关操作&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/niMWz&#34; alt=&#34;node_form_delete_submit&#34; /&gt;
可以看到&lt;code&gt;node_form_delete_submit()&lt;/code&gt;方法从get方法直接接收参数&lt;code&gt;destination&lt;/code&gt;，与最初分析正常删除文章的参数正是同一个，那么我们就可以利用&lt;code&gt;destination&lt;/code&gt;传进恶意参数&lt;/p&gt;

&lt;p&gt;构造如下
&lt;strong&gt;&lt;code&gt;destination=a?q[%2523post_render][]=passthru%26q[%2523type]=markup%26q[%2523markup]=dir&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;注意此处需要转义百分号，对&lt;code&gt;#&lt;/code&gt;进行二次编码，以绕过&lt;code&gt;CVE-2018-7600&lt;/code&gt;的补丁，不然在取值时会被认为&lt;code&gt;q[&lt;/code&gt;是一个值&lt;/p&gt;

&lt;p&gt;原因：
&lt;code&gt;includes/common.inc&lt;/code&gt;的&lt;code&gt;drupal_parse_url()&lt;/code&gt;方法对url进行了解析，而在url传入到Drupal内部的时候已经经过一层解码，也就是说&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;destination=a?q[%2523post_render][]=passthru%26q[%2523type]=markup%26q[%2523markup]=dir
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在进入Drupal时已经被解码成&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;destination=a?q[%23post_render][]=passthru%26q[%23type]=markup%26q[%23markup]=dir
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后经过&lt;code&gt;parse_url()&lt;/code&gt;方法对url结构进行解析&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;a&lt;/code&gt;参数是次要的，主要是&lt;code&gt;q&lt;/code&gt;参数，因为在&lt;code&gt;drupal_parse_url()&lt;/code&gt;下半部分从q取出值赋给&lt;code&gt;$options[&#39;path&#39;]&lt;/code&gt;，也就是a被覆盖了，这个时候的&lt;code&gt;$options[&#39;path&#39;]&lt;/code&gt;就是我们传入的数组&lt;/p&gt;

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

&lt;p&gt;参数缓存进整个form后通过第二个请求取出，同样经过&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;foreach ($form_parents as $parent) {
    $form = $form[$parent];
  }
&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/Wgz3w&#34; alt=&#34;parent&#34; /&gt;
进入&lt;code&gt;drupal_render()&lt;/code&gt;执行&lt;/p&gt;

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

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

&lt;p&gt;略&lt;/p&gt;

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

&lt;p&gt;7.x的补丁&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/drupal/drupal/commit/080daa38f265ea28444c540832509a48861587d0&#34;&gt;https://github.com/drupal/drupal/commit/080daa38f265ea28444c540832509a48861587d0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/2Zhjq&#34; alt=&#34;patch&#34; /&gt;
其中一个重要操作就是对&lt;code&gt;destination&lt;/code&gt;参数进行了净化&lt;/p&gt;

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

&lt;h4 id=&#34;0x07-总结&#34;&gt;0x07 总结&lt;/h4&gt;

&lt;p&gt;总的来说这个漏洞是CVE-2018-7600的另一个利用点，只是入口方式不一样，最终执行点还是相同的，所以还是那句话，一旦参数可控并且没有经过正确的过滤，就很有可能出问题。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2018-7600 Drupal 7.x 版本代码执行漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2018-7600-drupal-7.x-%E7%89%88%E6%9C%AC%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, 20 Apr 2018 23:05:34 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2018-7600-drupal-7.x-%E7%89%88%E6%9C%AC%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;h4 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h4&gt;

&lt;p&gt;CVE-2018-7600影响范围包括了Drupal 6.x，7.x，8.x版本，前几天8.x版本的PoC出来之后大家都赶紧分析了一波，然后热度似乎慢慢退去了。两天前&lt;a href=&#34;https://github.com/dreadlocked/Drupalgeddon2&#34;&gt;Drupalgeddon2&lt;/a&gt;项目更新了7.x版本的exp，实际环境也出现了利用，下面就简单来看一下&lt;/p&gt;

&lt;p&gt;看到项目上这样写&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drupal &amp;lt; 7.58 ~ user/password URL, attacking triggering_element_name form &amp;amp; #post_render parameter, using PHP&amp;rsquo;s passthru function&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;提示了问题出在&lt;code&gt;user/password&lt;/code&gt;路径下，通过&lt;code&gt;#post_render&lt;/code&gt;传递恶意参数，问题出现在&lt;code&gt;triggering_element_name&lt;/code&gt;表单处理下&lt;/p&gt;

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

&lt;p&gt;我们从三个问题入手，为什么PoC发了两个包，第二次请求为什么要带上一个&lt;code&gt;form_build_id&lt;/code&gt;，以及为什么选择&lt;code&gt;user/password&lt;/code&gt;这个入口&lt;/p&gt;

&lt;p&gt;先分析第一个post，照例还是先看一下Drupal 7的表单处理流程，跟8版本不太一样，但是入口还是相似的。
根据文档描述，当我们提交一个表单(例如找回密码)时，系统会通过&lt;code&gt;form_builder()&lt;/code&gt;方法创建一个form
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/lffx4.jpg&#34; alt=&#34;user/passwd&#34; /&gt;
一系列预处理后，会由&lt;code&gt;drupal_build_form
()&lt;/code&gt;方法创建一个表单，在第386行调用&lt;code&gt;drupal_process_form()&lt;/code&gt;方法，
跟进&lt;code&gt;drupal_process_form()&lt;/code&gt;方法，这时候默认的&lt;code&gt;$form_state[&#39;submitted&#39;]&lt;/code&gt;为false&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/dd7fx.png&#34; alt=&#34;submitted&#34; /&gt;
不满足if条件，&lt;code&gt;$form_state[&#39;submitted&#39;]&lt;/code&gt;被设置为true&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/kqijr.png&#34; alt=&#34;true&#34; /&gt;
于是进入这个分支，最终被&lt;code&gt;drupal_redirect_form&lt;/code&gt;重定向&lt;/p&gt;

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

&lt;p&gt;我们的目的是要让系统缓存一个&lt;code&gt;form_build_id&lt;/code&gt;，以便后面拿出来用。要想form被缓存，就得想办法让&lt;code&gt;if ($form_state[&#39;submitted&#39;] &amp;amp;&amp;amp; !form_get_errors() &amp;amp;&amp;amp; !$form_state[&#39;rebuild&#39;])&lt;/code&gt;不成立，也就是说要使&lt;code&gt;$form_state[&#39;submitted&#39;]&lt;/code&gt;为false
从而进入下面的&lt;code&gt;drupal_rebuild_form&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;那么如何让&lt;code&gt;$form_state[&#39;submitted&#39;]&lt;/code&gt;为false呢？&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;includes/form.inc&lt;/code&gt;第886行
&lt;code&gt;$form = form_builder($form_id, $form, $form_state);&lt;/code&gt;
跟进&lt;code&gt;form_builder&lt;/code&gt;方法，第1987行&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if (!empty($form_state[&#39;triggering_element&#39;][&#39;#executes_submit_callback&#39;])) {
  $form_state[&#39;submitted&#39;] = TRUE;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当&lt;code&gt;$form_state[&#39;triggering_element&#39;][&#39;#executes_submit_callback&#39;]&lt;/code&gt;存在值的时候就为true，那么我们就想办法让这个值为空
往上看第1972行&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if (!$form_state[&#39;programmed&#39;] &amp;amp;&amp;amp; !isset($form_state[&#39;triggering_element&#39;]) &amp;amp;&amp;amp; !empty($form_state[&#39;buttons&#39;])) {
  $form_state[&#39;triggering_element&#39;] = $form_state[&#39;buttons&#39;][0];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果没有设置&lt;code&gt;$form_state[&#39;triggering_element&#39;]&lt;/code&gt;，那么&lt;code&gt;$form_state[&#39;triggering_element&#39;]&lt;/code&gt;就设置为第一个button的值，所以正常传递表单的时候&lt;code&gt;$form_state[&#39;triggering_element&#39;][&#39;#executes_submit_callback&#39;]&lt;/code&gt;就总会有值&lt;/p&gt;

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

&lt;p&gt;现在问题来了，如何构造一个form能够确保&lt;code&gt;$form_state[&#39;triggering_element&#39;][&#39;#executes_submit_callback&#39;]&lt;/code&gt;为空或者说不存在这个数组呢？&lt;/p&gt;

&lt;p&gt;我们注意到第1864行&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if (!empty($element[&#39;#input&#39;])) {
  _form_builder_handle_input_element($form_id, $element, $form_state);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;_form_builder_handle_input_element()&lt;/code&gt;方法对表单先进行了处理，跟进去看一下&lt;/p&gt;

&lt;p&gt;第2144行&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;// Determine which element (if any) triggered the submission of the form and
// keep track of all the clickable buttons in the form for
// form_state_values_clean(). Enforce the same input processing restrictions
// as above.
if ($process_input) {
  // Detect if the element triggered the submission via Ajax.
  if (_form_element_triggered_scripted_submission($element, $form_state)) {
    $form_state[&#39;triggering_element&#39;] = $element;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里&lt;code&gt;$form_state[&#39;triggering_element&#39;]&lt;/code&gt;被设置为&lt;code&gt;$element&lt;/code&gt;，前提是满足&lt;code&gt;_form_element_triggered_scripted_submission()&lt;/code&gt;方法，继续跟入
第2180行&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;function _form_element_triggered_scripted_submission($element, &amp;amp;$form_state) {
  if (!empty($form_state[&#39;input&#39;][&#39;_triggering_element_name&#39;]) &amp;amp;&amp;amp; $element[&#39;#name&#39;] == $form_state[&#39;input&#39;][&#39;_triggering_element_name&#39;]) {
    if (empty($form_state[&#39;input&#39;][&#39;_triggering_element_value&#39;]) || $form_state[&#39;input&#39;][&#39;_triggering_element_value&#39;] == $element[&#39;#value&#39;]) {
      return TRUE;
    }
  }
  return FALSE;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个方法的意思是说如果&lt;code&gt;_triggering_element_value&lt;/code&gt;和&lt;code&gt;$element&lt;/code&gt;的键值都相等的话，返回true
&lt;code&gt;$form_state[&#39;triggering_element&#39;]&lt;/code&gt;赋值为&lt;code&gt;$element&lt;/code&gt;，其中不含&lt;code&gt;[&#39;#executes_submit_callback&#39;]&lt;/code&gt;，一开始的条件就成立了&lt;/p&gt;

&lt;p&gt;根据PoC，我们传入&lt;code&gt;_triggering_element_name=name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/3c6kh.jpg&#34; alt=&#34;element&#34; /&gt;
看到进入这个分支，进入&lt;code&gt;form_set_cache()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/cn1jh.jpg&#34; alt=&#34;&#34; /&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/lskgu.png&#34; alt=&#34;&#34; /&gt;
数据库中插入缓存&lt;code&gt;form_build_id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/odfo8.png&#34; alt=&#34;&#34; /&gt;
成功写入缓存&lt;/p&gt;

&lt;p&gt;接下去来看一下这个缓存有什么用&lt;/p&gt;

&lt;p&gt;分析PoC的第二个包，请求参数是这样&lt;code&gt;q=file/ajax/name/%23value/form_build_id&lt;/code&gt;
&lt;code&gt;form_build_id&lt;/code&gt;即我们上一个写入数据库的缓存表单&lt;/p&gt;

&lt;p&gt;首先请求会进入&lt;code&gt;includes/menu.inc&lt;/code&gt;的&lt;code&gt;menu_get_item()&lt;/code&gt;方法，&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;function menu_get_item($path = NULL, $router_item = NULL) {
  $router_items = &amp;amp;drupal_static(__FUNCTION__);
  if (!isset($path)) {
    $path = $_GET[&#39;q&#39;];
  }
  if (isset($router_item)) {
    $router_items[$path] = $router_item;
  }
  if (!isset($router_items[$path])) {
    // Rebuild if we know it&#39;s needed, or if the menu masks are missing which
    // occurs rarely, likely due to a race condition of multiple rebuilds.
    if (variable_get(&#39;menu_rebuild_needed&#39;, FALSE) || !variable_get(&#39;menu_masks&#39;, array())) {
      if (_menu_check_rebuild()) {
        menu_rebuild();
      }
    }
    $original_map = arg(NULL, $path);

    $parts = array_slice($original_map, 0, MENU_MAX_PARTS);
    $ancestors = menu_get_ancestors($parts);
    $router_item = db_query_range(&#39;SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC&#39;, 0, 1, array(&#39;:ancestors&#39; =&amp;gt; $ancestors))-&amp;gt;fetchAssoc();

    if ($router_item) {
      // Allow modules to alter the router item before it is translated and
      // checked for access.
      drupal_alter(&#39;menu_get_item&#39;, $router_item, $path, $original_map);

      $map = _menu_translate($router_item, $original_map);
      $router_item[&#39;original_map&#39;] = $original_map;
      if ($map === FALSE) {
        $router_items[$path] = FALSE;
        return FALSE;
      }
      if ($router_item[&#39;access&#39;]) {
        $router_item[&#39;map&#39;] = $map;
        $router_item[&#39;page_arguments&#39;] = array_merge(menu_unserialize($router_item[&#39;page_arguments&#39;], $map), array_slice($map, $router_item[&#39;number_parts&#39;]));
        $router_item[&#39;theme_arguments&#39;] = array_merge(menu_unserialize($router_item[&#39;theme_arguments&#39;], $map), array_slice($map, $router_item[&#39;number_parts&#39;]));
      }
    }
    $router_items[$path] = $router_item;
  }
  return $router_items[$path];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;$path&lt;/code&gt;即我们传进去的q参数，经过一系列处理传给&lt;code&gt;menu_get_ancestors()&lt;/code&gt;方法，该方法把path重新组合成一堆router，也就是Drupal处理路由到具体url的传参方式，最终被&lt;code&gt;db_query_range()&lt;/code&gt;带入数据库查询
我们关注查询结果&lt;code&gt;$router_item&lt;/code&gt;的&lt;code&gt;page_callback&lt;/code&gt;值，因为这个值最终会作为参数被带入&lt;code&gt;call_user_func_array()&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($page_callback_result == MENU_SITE_ONLINE) {
  if ($router_item = menu_get_item($path)) {
    if ($router_item[&#39;access&#39;]) {
      if ($router_item[&#39;include_file&#39;]) {
        require_once DRUPAL_ROOT . &#39;/&#39; . $router_item[&#39;include_file&#39;];
      }
      $page_callback_result = call_user_func_array($router_item[&#39;page_callback&#39;], $router_item[&#39;page_arguments&#39;]);
    }
    else {
      $page_callback_result = MENU_ACCESS_DENIED;
    }
  }
  else {
    $page_callback_result = MENU_NOT_FOUND;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/qvqwz.png&#34; alt=&#34;call_user_func_array&#34; /&gt;
到这里就跟8版本的情况有点类似了&lt;/p&gt;

&lt;p&gt;跟入回调函数&lt;code&gt;file_ajax_upload()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/tij37.jpg&#34; alt=&#34;file_ajax_upload&#34; /&gt;
还是一样，把&lt;code&gt;$form_parents&lt;/code&gt;完整取出赋值给&lt;code&gt;$form&lt;/code&gt;，加上一些前缀后缀后最终进入&lt;code&gt;drupal_render()&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/rpwrs.jpg&#34; alt=&#34;passthru&#34; /&gt;&lt;/p&gt;

&lt;p&gt;到目前为止我们分析清楚了为什么PoC要发两次包，以及第二次请求为什么要带上一个&lt;code&gt;form_build_id&lt;/code&gt;，现在来想一想为什么要请求&lt;code&gt;user/password&lt;/code&gt;这个路径呢？
在user这个module下的&lt;code&gt;user_pass()&lt;/code&gt;方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;function user_pass() {
  global $user;

  $form[&#39;name&#39;] = array(
    &#39;#type&#39; =&amp;gt; &#39;textfield&#39;,
    &#39;#title&#39; =&amp;gt; t(&#39;Username or e-mail address&#39;),
    &#39;#size&#39; =&amp;gt; 60,
    &#39;#maxlength&#39; =&amp;gt; max(USERNAME_MAX_LENGTH, EMAIL_MAX_LENGTH),
    &#39;#required&#39; =&amp;gt; TRUE,
    &#39;#default_value&#39; =&amp;gt; isset($_GET[&#39;name&#39;]) ? $_GET[&#39;name&#39;] : &#39;&#39;,
  );
  ...
  return $form;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;看到这里是不是感觉跟8版本很相似，&lt;code&gt;#default_value&lt;/code&gt;从get的&lt;code&gt;name&lt;/code&gt;参数里取值，而name可以作为数组传入，它的属性在下面正好可以被利用，一个巧妙的利用链就串起来了。&lt;/p&gt;

&lt;h4 id=&#34;0x03-总结&#34;&gt;0x03 总结&lt;/h4&gt;

&lt;p&gt;Drupal 7.x的利用比8.x要复杂一些，但触发点和一开始的风险因素还是类似的，一是接收参数过滤不当，而是可控参数进入危险方法。官方补丁把入口处的&lt;code&gt;#&lt;/code&gt;全给过滤了，简单粗暴又有效，估计再利用框架本身的特性想传递进一些数组或元素就很难了。&lt;/p&gt;

&lt;h4 id=&#34;0x04-参考&#34;&gt;0x04 参考&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/dreadlocked/Drupalgeddon2&#34;&gt;https://github.com/dreadlocked/Drupalgeddon2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://research.checkpoint.com/uncovering-drupalgeddon-2/&#34;&gt;https://research.checkpoint.com/uncovering-drupalgeddon-2/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>CVE-2018-7600 Drupal 内核远程代码执行漏洞分析</title>
      <link>https://kylingit.com/blog/cve-2018-7600-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, 13 Apr 2018 23:05:34 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/cve-2018-7600-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;h4 id=&#34;0x01-概述&#34;&gt;0x01 概述&lt;/h4&gt;

&lt;p&gt;&lt;a href=&#34;https://www.drupal.org/sa-core-2018-002&#34;&gt;https://www.drupal.org/sa-core-2018-002&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Drupal 6.x，7.x，8.x&lt;/p&gt;

&lt;p&gt;修复版本
Drupal 7.58，Drupal 8.5.1&lt;/p&gt;

&lt;h4 id=&#34;0x03-环境搭建&#34;&gt;0x03 环境搭建&lt;/h4&gt;

&lt;p&gt;历史版本
&lt;a href=&#34;https://www.drupal.org/project/drupal/releases&#34;&gt;https://www.drupal.org/project/drupal/releases&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&#34;0x04-流程梳理&#34;&gt;0x04 流程梳理&lt;/h4&gt;

&lt;p&gt;先来理清一下Drupal处理表单的情况。更详细的可以看&lt;a href=&#34;http://www.thinkindrupal.com/node/1100&#34;&gt;文档&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drupal提供了一个应用程序接口（API），用来生成、验证和处理HTML表单。表单API将表单抽象为一个嵌套数组，里面包含了属性和值。在生成页面时，表单呈现引擎会在适当的时候将数组呈现出来。&lt;/p&gt;

&lt;p&gt;模块使用关联数组向Drupal描述表单。Drupal的表单引擎负责为要显示的表单生成HTML，并使用三个阶段来安全的处理提交了的表单：验证、提交、重定向。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Drupal比较特殊，它不像大部分cms通过html直接渲染页面，而是把接收的数据交给&lt;code&gt;core/lib/Drupal/Core/Form/FormBuilder.php&lt;/code&gt;的&lt;code&gt;buildForm()&lt;/code&gt;方法处理，&lt;code&gt;buildForm()&lt;/code&gt;经过处理后返回一个结构体(数组)，数组通过引擎生成HTML。&lt;/p&gt;

&lt;p&gt;当我们提交一个表单(例如注册页面)，&lt;code&gt;buildForm()&lt;/code&gt;方法会根据&lt;code&gt;$form_id&lt;/code&gt;取出数据，经过一系列处理后返回一个树形结构，这个结构就是通过数组存储的，就是我们看到的类似&lt;code&gt;[$current_file_count][&#39;#attributes&#39;][&#39;class&#39;][]&lt;/code&gt;的结构，数组每个元素作为一个叶子节点，后续就把整个&lt;code&gt;form&lt;/code&gt;结构渲染出页面。&lt;/p&gt;

&lt;p&gt;当我们在注册页面上传一张图片的时候，&lt;code&gt;form&lt;/code&gt;结构被传给&lt;code&gt;core/modules/file/src/Element/ManagedFile.php&lt;/code&gt;的&lt;code&gt;uploadAjaxCallback()&lt;/code&gt;方法，这个方法用来处理上传文件的情况&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt; public static function uploadAjaxCallback(&amp;amp;$form, FormStateInterface &amp;amp;$form_state, Request $request) {
    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service(&#39;renderer&#39;);

    $form_parents = explode(&#39;/&#39;, $request-&amp;gt;query-&amp;gt;get(&#39;element_parents&#39;));

    // Retrieve the element to be rendered.
    $form = NestedArray::getValue($form, $form_parents);

    // Add the special AJAX class if a new file was added.
    $current_file_count = $form_state-&amp;gt;get(&#39;file_upload_delta_initial&#39;);
    if (isset($form[&#39;#file_upload_delta&#39;]) &amp;amp;&amp;amp; $current_file_count &amp;lt; $form[&#39;#file_upload_delta&#39;]) {
      $form[$current_file_count][&#39;#attributes&#39;][&#39;class&#39;][] = &#39;ajax-new-content&#39;;
    }
    // Otherwise just add the new content class on a placeholder.
    else {
      $form[&#39;#suffix&#39;] .= &#39;&amp;lt;span class=&amp;quot;ajax-new-content&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#39;;
    }

    $status_messages = [&#39;#type&#39; =&amp;gt; &#39;status_messages&#39;];
    $form[&#39;#prefix&#39;] .= $renderer-&amp;gt;renderRoot($status_messages);
    $output = $renderer-&amp;gt;renderRoot($form);

    $response = new AjaxResponse();
    $response-&amp;gt;setAttachments($form[&#39;#attached&#39;]);

    return $response-&amp;gt;addCommand(new ReplaceCommand(NULL, $output));
  }
&lt;/code&gt;&lt;/pre&gt;

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

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

&lt;p&gt;问题就出现在&lt;code&gt;$request-&amp;gt;query-&amp;gt;get(&#39;element_parents&#39;)&lt;/code&gt;这个地方，&lt;code&gt;$form_parents&lt;/code&gt;父节点的值是从&lt;code&gt;get()&lt;/code&gt;取出&lt;code&gt;element_parents&lt;/code&gt;参数传进去的，进入下面的&lt;code&gt;NestedArray::getValue()&lt;/code&gt;方法，&lt;code&gt;getValue()&lt;/code&gt;的作用是接收一个节点，把这个节点下的叶子节点全部遍历出来，再根据叶子节点的&lt;code&gt;key-value&lt;/code&gt;值进行后续操作。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/39ciX&#34; alt=&#34;getValue&#34; /&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/f8qiO&#34; alt=&#34;user_picture&#34; /&gt;&lt;/p&gt;

&lt;p&gt;按理说这样的功能很正常，关键就在于这个&lt;code&gt;element_parents&lt;/code&gt;正是我们可以控制的，也就是说我们可以指定&lt;code&gt;uploadAjaxCallback()&lt;/code&gt;渲染我们给它的参数，而这个参数可以是恶意的。&lt;/p&gt;

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

&lt;p&gt;那么我们传进去什么参数呢？我们先来测试一下，正常注册流程，&lt;code&gt;mail&lt;/code&gt;参数传进去一个数组的话会怎么样&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/KJE50&#34; alt=&#34;mail&#34; /&gt;
可以看到我们构造的“子节点”被存储在&lt;code&gt;mail-value&lt;/code&gt;下，如果要取出这个值就得让上面提到的&lt;code&gt;getValue()&lt;/code&gt;接收这个参数，所以我们构造&lt;code&gt;element_parents=account/name/%23value&lt;/code&gt;，这样子&lt;code&gt;getValue()&lt;/code&gt;就会遍历出我们构造的参数&lt;/p&gt;

&lt;p&gt;现在参数已经能够传进去了，那么在哪里执行呢？继续往下跟&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$current_file_count = $form_state-&amp;gt;get(&#39;file_upload_delta_initial&#39;);
if (isset($form[&#39;#file_upload_delta&#39;]) &amp;amp;&amp;amp; $current_file_count &amp;lt; $form[&#39;#file_upload_delta&#39;]) {
	$form[$current_file_count][&#39;#attributes&#39;][&#39;class&#39;][] = &#39;ajax-new-content&#39;;
}
// Otherwise just add the new content class on a placeholder.
else {
	$form[&#39;#suffix&#39;] .= &#39;&amp;lt;span class=&amp;quot;ajax-new-content&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#39;;
}

$status_messages = [&#39;#type&#39; =&amp;gt; &#39;status_messages&#39;];
$form[&#39;#prefix&#39;] .= $renderer-&amp;gt;renderRoot($status_messages);
$output = $renderer-&amp;gt;renderRoot($form);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到经过&lt;code&gt;getValue()&lt;/code&gt;遍历出来的叶子节点(就是此时的&lt;code&gt;form&lt;/code&gt;)被传进&lt;code&gt;$renderer-&amp;gt;renderRoot()&lt;/code&gt;方法，跟进去看一下&lt;/p&gt;

&lt;p&gt;&lt;code&gt;core/lib/Drupal/Core/Render/Renderer.php&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  public function render(&amp;amp;$elements, $is_root_call = FALSE) {
...
    try {
      return $this-&amp;gt;doRender($elements, $is_root_call);
    }
    catch (\Exception $e) {
      // Mark the ::rootRender() call finished due to this exception &amp;amp; re-throw.
      $this-&amp;gt;isRenderingRoot = FALSE;
      throw $e;
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;调用&lt;code&gt;doRender()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos.ap-beijing.myqcloud.com/images/6Htxw&#34; alt=&#34;doRender&#34; /&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 (isset($elements[&#39;#post_render&#39;])) {
    foreach ($elements[&#39;#post_render&#39;] as $callable) {
        if (is_string($callable) &amp;amp;&amp;amp; strpos($callable, &#39;::&#39;) === FALSE) {
            $callable = $this-&amp;gt;controllerResolver-&amp;gt;getControllerFromDefinition($callable);
        }
        $elements[&#39;#children&#39;] = call_user_func($callable, $elements[&#39;#children&#39;], $elements);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接收的第一个参数&lt;code&gt;$elements[&#39;#post_render&#39;]&lt;/code&gt;作为函数，第二个参数&lt;code&gt;$elements[&#39;#children&#39;]&lt;/code&gt;作为参数，在上面被赋值&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if (!$theme_is_implemented &amp;amp;&amp;amp; isset($elements[&#39;#markup&#39;])) {
    $elements[&#39;#children&#39;] = Markup::create($elements[&#39;#markup&#39;] . $elements[&#39;#children&#39;]);
}
&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/jHlV8&#34; alt=&#34;call_user_func&#34; /&gt;&lt;/p&gt;

&lt;p&gt;回头看一下这处&lt;code&gt;call_user_func_array&lt;/code&gt;，这里的&lt;code&gt;$callable&lt;/code&gt;和&lt;code&gt;$args&lt;/code&gt;两个参数实际上也是可控的，通过&lt;code&gt;#lazy_builder&lt;/code&gt;属性传进来，checkpoint的分析报告正是分析了这个地方&lt;/p&gt;

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

&lt;h4 id=&#34;0x06-总结&#34;&gt;0x06 总结&lt;/h4&gt;

&lt;p&gt;关注这个漏洞也是好长时间了，当时粗略看了一下，因为补丁直接对入口进行了过滤，要找到真正触发的地方太难了，所以也迟迟不见PoC出来。checkpoint的分析报告出来后好好跟了一遍，不得不感叹人家真厉害(逃&amp;hellip;&lt;/p&gt;

&lt;p&gt;这个漏洞关键点有两个，一个是&lt;code&gt;uploadAjaxCallback&lt;/code&gt;里&lt;code&gt;$form_parents&lt;/code&gt;由get直接传进参数，这里就存在风险；
另一处&lt;code&gt;call_user_func&lt;/code&gt;两个参数均可控，两者结合造成一个严重的远程代码执行漏洞，看分析报告如何一步步构造利用链，可谓是十分精彩了。&lt;/p&gt;

&lt;h4 id=&#34;0x07-参考&#34;&gt;0x07 参考&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://research.checkpoint.com/uncovering-drupalgeddon-2/&#34;&gt;https://research.checkpoint.com/uncovering-drupalgeddon-2/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/a2u/CVE-2018-7600&#34;&gt;https://github.com/a2u/CVE-2018-7600&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
  </channel>
</rss>