流程
一、PHP伪协议
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
php.ini 中的两个重要选项
1、file://
2、php://
PHP 中几个比较重要的伪协议:
3、php://input
php://input 协议还可以用来生成一句话木马,这里假设我们用的之前的直接包含的php代码:
4、php://output
5、php://filter/
php://filter/ead
php://read/write
6、zlib
7、data://
8、总结
二、实战
例题一
这是一个文件上传的页面可以上传一个文件上去
上传后发现失败了,那就说明不是文件上传的题目,我们在url地址栏发现了一个op参数和一个key参数,我们用文件审查查看一下源代码
点开后,看到一个op的参数,看到这个好参数我们会想到可能存在文件包含,比如我们改成upload
就变成了上传文件的页面,这里我们可以猜到通过op的参数包含不同的文件
这里我们用filter协议经过base64编码home文件后,得到一串base64编码
通过解码后面我们就看到了源码home的源码:
这个时候我们就可以读取不同的页面了
接着我们读完upload和common后,并没有发现我们想要的flag
我们猜测flag应该就在当前目录,直接试试打印flag文件的编码试试:
页面有了反应,我们直接解码出了flag
例题二
打开题目后出现了以下文字,查看源代码的时候发现了注释:
我们发现注释里面只需要让user参数等于admin就可以获取文件包含权限,我们可以用php://input 使user的值等于admin,把参数写在表单里,再给file文件传入我们想要包含的文件class.php,经过base64 编码后传入,如下:
就成功包含出了class.php,解码后又发现了一个f1a9.php文件,我们再读一下
传入之后发现并没有回显出来,猜测应该是被过滤了:
我们只能回去研究一下class.php里面是什么代码:
<?
error_reporting(E_ALL & ~E_NOTICE);
class Read{
public $file;
public function __toString(){
if(isset($this->file)){
echo file_get_contents($this->file);
}
return "_toString was called!";
}
}
?>
那么我们需要用序列化调用我 _tostring函数,并且需要把file函数覆盖为f1a9.php
我们先去读一下index.php看看有没有什么收获:
<?
error_reporting(E_ALL & ~E_NOTICE);
$user = $_GET['user'];
$file = $_GET['file'];
$pass = $_GET['pass'];
if(isset($user)&&(file_get_contents($user,'r')=="admin")){
echo "hello admin!<br>";
if(preg_match('/f1a9/',$file)){
exit();
}else{
include($file);
$pass = unserialize($pass);
echo $pass;
}
}else{
echo "you are not admin !";
}
?>
我们看到index.php里面有一个正则的过滤,防止我们去读文件里面的内容,else里面有我们需要的包含函数
这个时候我们需要包含class.php里面的代码,相当于class.php里面的代码就存在于index.php里面了,再去给class.php 里的file变量赋值为f1a9.php,序列化函数后
获得以下值:
string(41) "O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}"
传入pass变量,这个过程给file的变量赋值修改成了f1a9.php,pass变量被echo 调用回显时自动调用了魔术变量_tostring(当有涉及到字符串操作时会自动调用):
完整payload如下:
http://192.168.23.129/?user=php://input&file=class.php&pass=O:4:%22Read%22:1:{s:4:%22file%22;s:8:%22f1a9.php%22;}
执行,最后我们成功输出了flag:
例题三
我们访问目标网站后显示的内容:
通过分析我们需要传入两个参数,一个text,一个file。且这两个参数用正则匹配都不能有flag字眼,且忽略大小写,但是我们又需要text中传入I wanna the flag,那么我们需要用到 data:// — 数据(RFC 2397),把我们需要传入的内容经过一次base64编码后传入就不就被正则匹配到,再通过data://协议把base64编码解码回I wanna the flag:
text=data:text/html;base64,SSB3YW5uYSB0aGUgZmxhZw==
传入后,我们看到我们传入的值没有被过滤掉,被成功echo出来了:
这个时候我们给file传入config.zip:
出现了一堆乱码,仔细观察我们发现,其中有一个 $tip 变量,我们通过 phar:// — PHP 归档 协议来查看一下里面的内容
phar://
在php文件部署的环境中,为了方便php程序的部署我们会把全部文件都归档为一个文件,以方便移动、安装, phar文件有三种格式:tar归档、zip归档、phar归档,前两种执行需要php安装Phar 扩展支持,我们这里需要打开的文件是zip,刚好是phar支持的文件类型,我们只需要给file参数传入:
file=phar://config.zip/tip
得到一个./c0nf111g.php文件,通过php://filter读取文件内容:
text=data:text/html;base64,SSB3YW5uYSB0aGUgZmxhZw==&file=php://filter/read=convert.b ase64-encode/resource=./c0nf111g.php
出现了经过base64编码后的源码,解密后代码如下:
通过分析,我们可以传入的两个参数是 GET 协议里的 1chunq1u 和 POST 协议里的 name ,1chunq1u 里面的参数被正则过滤了,因为我们需要用伪协议php://filter来打开文件就需要传入fl444444ag,不能想之前那样用data:// 编码后绕过,这个时候我们看到代码前几行,有session字样,说明服务器是打开了session服务的,我们再次打开网站看看有没有PHPSESSID的cookie:
cookie里有PHPSESSID的值,说明服务器确实打开了session服务,我们把思路改为session包含,通过POST协议传入 name ,往name参数里面写php代码,去包含我们的fl444444ag,那么我们首先得先找到session文件的路径,常见的session路径如下:
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
session文件的名字就是sess_ + PHPSESSID,其中PHPSESSID就是我们之前cookie里面的值,那么我们得到文件名字为sess_a6jeut6g1ab6p44rjcj5gui767
,那么我们先找到session路径,先通过给 1chunq1u 传入/var/lib/php/sess_j7ed5o696slcn99opjnrn45ik0
看看能不能打开session文件来判定路径对不对:
1chunq1u=/var/lib/php/sess_a6jeut6g1ab6p44rjcj5gui767
传入后,发现页面并没有什么反应,说明我们的路径不对,那么我们再换一个路径尝试:
1chunq1u=/var/lib/php/sessions/sess_a6jeut6g1ab6p44rjcj5gui767
这次我们成功打开了session文件,接着我们继续看session那里的代码,发现只要参数name里面只要有值就会被写入session文件,那么我们通过POST给name传入我们想要执行的参数就可以包含session文件中的php代码:
name=<?php include 'fl444444ag'; ?>
传入后发现原本的guset的字符不见了,且name后面的“ ”里面的空的,按理说我们应该成功包含了fl444444ag文件,怎么会是空的呢:
我们把name里的参数改为<?php phpinfo(); ?>
试试:
发现能够正常被执行,那么就说明fl444444ag文件很有可能不在当前目录下,我们尝试用php代码运行liunx命令打印输出一下当前目录下面的文件有哪些<?php system('ls'); ?>
:
发现确实没有fl444444ag文件,使用<?php system('find / -name "*fl444444ag*"'); ?>
通过用liunx自带的find功能来查找一下文件:
最后成功找到了文件,在根目录下,使用<?php system(‘cat /fl444444ag’); ?>
来输出一下文件里面的内容:
最后成功获得flag!