2025强网杯web ezphp分享
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 | <?php |
执行$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 | <?php |
接下来就是上传文件的问题了,完整解题步骤推荐看看这篇文章。
