https://www.cnblogs.com/bkofyZ/p/17660907.html#comment_form_container

[NISACTF 2022]popchains

什么是序列化

定义:序列化就是将一个对象转换成字符串,反序列化则是将字符串重新恢复成对象

PHP序列化函数:serialize()

PHP反序列化函数:unserialize()

几个常用的魔术方法:

名称 触发时机
__construct() 在对象实例化(创建对象)的时候自动触发
__destruct() 在销毁对象的时候自动触发
__wakeup() 执行unserialize()时,先会调用这个函数
__sleep() 执行serialize()时,先会调用这个函数
__call() 在对象上下文中调用不可访问的方法时触发
__get() 访问私有或不存在的成员属性的时候自动触发
__set() 对私有成员属性进行设置值时自动触发
__isset() 对私有成员属性进行 isset 进行检查时自动触发
__unset() 对私有成员属性进行 unset 进行检查时自动触发
__toString() 把类当作字符串使用时触发
__invoke() 当尝试将对象调用为函数时触发

一般做题顺序

​ 构造pop链,找出它的起点或者终点,再利用魔术方法的触发条件和题目的代码结构,将POP链完善

​ POP链:就是利用魔法方法进行多次跳转后获取敏感数据的一种payload

接下来开始代码审计

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
<?php

echo 'Happy New Year~ MAKE A WISH<br>';

if(isset($_GET['wish'])){
@unserialize($_GET['wish']);
}
else{
$a=new Road_is_Long;
highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/

class Road_is_Long{
public $page;
public $string;
public function __construct($file='index.php'){
$this->page = $file;
}
public function __toString(){
return $this->string->page;
}

public function __wakeup(){
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
echo "You can Not Enter 2022";
$this->page = "index.php";
}
}
}

class Try_Work_Hard{
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

class Make_a_Change{
public $effort;
public function __construct(){
$this->effort = array();
}

public function __get($key){
$function = $this->effort;
return $function();
}
}
/**********************Try to See flag.php*****************************/

(1)找到终点,Try_work_Hard的append()

​ 想到可以利用php伪协议读取数据流,那么怎么触发append()呢,发现下面的__invoke()可以触发append();

(2)接着Try_work_Hard::__invoke()

​ __invoke: 当尝试将对象调用为函数时触发,看了一圈,发现$Make_a_Change类里面的$effort是可控的,且__get方法可以返回可控的函数,那就想办法让$effort为Try_work_Hard的类的对象

(3)接着Make_a_Change()::__get()

__get访问私有或不存在的成员属性的时候自动触发,利用它

1
2
3
public function __toString(){
return $this->string->page;
}

(4)接着Road_is_Long::__toString()

​ 实例化化之后,让它的string指向Make_a_Change的类的对象,

(5)继续Road_is_Long::__wakeup()

​ __toString: 把类当作字符串使用时触发,

因为有protected属性的$var,这反序列化后会带特殊符号%00(url编码),这个你复制粘贴不过来,但是urlencode可以显示出来好复制,不用你之后改