Thinkphp 5.2反序列化利用链学习


安装环境

这里直接使用N1CTF2019 sql manager的源码

我还是直接在sublime中运行think文件进行调试

利用链

5.2保留了5.1利用链中的__destruct()__toString() 不过到think/Request.php这里已经没有了__call()需要重找一条新链

利用链I

跟到think/model/concern/Conversion.php中的__toString() toArray中: 调用了think/model/concern/Attribute中的getAttr() 跟到getAttr() 调用getValue(),而getValue()中的$value = $closure($value, $this->data); 若其中参数都是可控的,则可任意代码执行,接下来逐一分析各个参数 $closure$closure = $this->withAttr[$fieldName];而来 $fieldName = $this->getRealFieldName($name); $namegetAttr($name)传入的也就是最开始toArray()中的$data数组中的键值 $data = array_merge($this->data, $this->relation);可控 $this->dataAttribute类中的private $data 这里注意键名不可为数字如['123' => "233"]or["123" => "233"] 因为这样反序列化时会当成a:1:{i:123;s:3:"233";}$data就会变为

之后控制

即可

利用链II

thinkphp v5.2.x 反序列化利用链挖掘这篇文章中提到了另一种和利用链I类似的方法,只不过不是直接执行符合条件的函数,而是通过tp自带的SerializableClosure调用 \Opis\Closure可用于序列化匿名函数,使得匿名函数同样可以进行序列化操作。这意味着我们可以序列化一个匿名函数,然后交由上述的$closure($value, $this->data)调用执行 这里用到了__invoke():将对象当作函数来使用时,会自动调用该方法 func_get_args():返回一个包含函数参数列表的数组

这样是可以成功执行phpinfo() 可以看到这里的$closure就是我们传入的值

利用链III

遵循5.1的套路,还是利用__call() 不过这里有条件:需要上传一个route.php结尾的文件且文件位置已知 开头说到think/Request.php这里已经没有__call()了,需要重找一条新链 这里是利用vendor/topthink/framework/src/think/Db.php中的__call() 发现可以控制new一个新类 think\Url中的__construct()正好可以利用 getRuntimePath()也可控 这里我们配置

存在/tmp/test/testroute.php文件,这样即可import这个文件,达成代码执行

 

POCs

POC I

POC II

这里须在Model中改$data$withAttr,POC I中在AttributeModel中都行

POC III

参考链接

1.sql manager writeup 2.thinkphp v5.2.x 反序列化利用链挖掘