Web渗透—PHP反序列化????????课程学习分享(课程非本人制作,仅提供学习分享)
靶场下载地址:GitHub - mcc0624/php_ser_Class: php反序列化靶场课程,基于课程制作的靶场
序列化serialize()函数会检查类中是否存在一个魔术方法__sleep()。
如果存在,该方法会先被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
如果该方法未返回任何内容,则NULL被序列化,并产生一个E_NOTICE级别的错误。
<?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'); //__sleep()执行返回需要序列化的变量名,过滤掉password变量
}
}
$user?=?new?User('a',?'b',?'c');
echo?serialize($user); //serialize()只序列化sleep返回的变量
?>
触发时机:序列化serialize()之前
功能:对象被序列化之前触发,返回需要被序列化存储的成员变量,删除不必要的属性
参数:成员属性(return array)
返回值:需要被序列化存储的成员属性('username','nickname')
通过审计代码,发现通过benben参数传入的值会赋值传给$cmd,而当执行序列化前会先执行__sleep(),只返回username成员属性,通过system()函数包含,所以可以直接传入命令进行执行。
unserialize()会检查是否存在一个__wakeup()方法。
如果存在,则会先调用__wakeup()方法,预先准备对象需要的资源。
预先准备对象资源,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。
__wakeup()在反序列化unserialize()之前;__destruct()在反序列化unserialize()之后。
<?php
class?User?{
const?SITE?=?'uusama';
public?$username;
public?$nickname;
private?$password;
private?$order;
public?function?__wakeup(){
$this->password?=?$this->username; //反序列化之前触发wakeup,给password赋值
}
}
$user_ser?=?'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}'; //字符串中没有定义password
var_dump(unserialize($user_ser)); //userialize()结果包含password的值
?>
触发时机:反序列化unserialize()之前
功能:(无)
参数:(无)
返回值:(无)(包含password的赋值)
解题代码:
<?php
class User {
public $username = "whoami";
}
$user_ser = new User();
echo serialize($user_ser);
?>
输出结果:
O:4:"User":1:{s:8:"username";s:6:"whoami";}
通过审计代码,发现通过benben参数传入值会直接赋值给$user_ser,并反序列化$user_ser,所以我们需要向benben参数内传入序列化值。而反序列化被调用前会先执行__wakeup(),调用username成员属性,通过system()函数包含,所以我们要通过序列化给username成员变量赋值传入系统命令。
表达方式错误导致魔术方法触发(把对象当成字符串调用时触发)
常用于构造POP链接
<?php
class?User?{
var?$benben?=?"this?is?test!!";
public?function?__toString(){
return?'格式不对,输出不了!';
}
}
$test?=?new?User()?; //把类User实例化宁赋值给$test,此时$test是个对象
print_r($test); //调用对象可以使用print_r或者var_dump
echo?$test;
?>
触发时机:把对象当成字符串调用
功能:(无)
参数:(无)
返回值:(无)
如果使用echo或者print只能调用字符串的方式去调用对象,即把对象当成字符串使用,此时自动触发toString()
格式表达错误导致魔术方法触发(把对象当成函数调用时触发)
<?php
class?User?{
var?$benben?=?"this?is?test!!";
public?function?__invoke(){
echo??'它不是个函数!';
}
}
$test?=?new?User()?; //把类User实例化并赋值给$test为对象
echo?$test?->benben; //正常输出对象里的值benben
echo?$test()?->benben; //加()是把test当成函数test()来调用,此时触发invoke()
?>
触发时机:把对象当成函数调用
功能:(无)
参数:(无)
返回值:(无)