ThinkPHP5.0.2RCE分析
浏览 719 | 评论 0 | 字数 5467
硝基苯
2022年01月29日
  • 环境

    1.thinkPHP5.0.2完整版
    2.需要开启兼容模式(默认开启)
    3.该payload无需开启debug模式

    前期知识

    ReflectionFunction::invokeArgs将返回被调用函数的结果
    74508-w7whkph07yq.png
    tp中的命名空间用\
    \think\cache\driver\File类为例,think就是一个根命名空间,其对应的初始命名空间目录就是系统的类库目录(thinkphp/library/think),我们可以简单的理解一个根命名空间对应了一个类库包
    93707-vlyz0ibxo8b.png

    复现过程

    ?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami为例分析
    因为windows会将\为关键字,导致无法触发命名空间,所以采用?s的方法进行
    62956-csnixu78v7v.png
    进入start.php
    19705-1bb902sfmes.png
    think/app.phpapp类中调用run()
    检测URL路由
    90291-8zq8r2vc937.png
    跟进routeCheck
    61179-tqxtyr5gp3.png
    跟进path
    85826-awgs94juxve.png
    步入pathinfo
    可以看到其做了check检测url中是否具有兼容模式得参数
    59862-qctc931t4kd.png
    application/config.php中,我们发现var_pathinfo
    23305-3npve89167d.png
    所以我们获取到了pathinfo
    28170-nlndse00jzg.png
    获取到后回到App.phprouteCheck
    获得的path需要进行URL调度,但是路由调度中存在一些规则返回了false
    95165-b1rw5a56wm.png
    47886-i3ihy0jch5d.png
    10929-ylpwge6ghs8.png
    因为返回了false,所以进行此步骤
    解析URL地址
    80574-cjo6bw8pjnr.png
    63248-jftlpa8gie8.png
    最终分割开
    15441-l6we1l6nk3m.png
    07262-50n7zx7nfem.png
    run()中调用exec
    81378-9ubh8gfokxw.png
    跟进exec。这里就是算正式开始访问调用了
    当type为module时,调用module()方法
    12051-au8rzh2q2td.png
    module用于执行模块
    通过路由解析,模块为index
    58113-0o0ubkpshz4s.png
    调用init来初始化模块
    68351-83ovgb9pn95.png
    初始化步骤跳过
    继续往下,获得控制器名"think\app",操作名"invokefunction"
    07915-074u2docqxzi.png
    随后到think/Loader.php中Loader类的 controller
    40417-6epla8df624.png
    调用getModuleAndClass()方法
    59960-ad4i98do5et.png
    00761-z7bvyk1ljmb.png
    因为name为think\app,所以进入if语句
    32697-40fei1s857p.png
    该语句 $module = Request::instance()->module();表示进入request类先到instance然后到module

    注:如果不是\则通过正常截取/得到对应的模块和控制器名称,最终得到应用类的类名
    getModuleAndClass最终$name为"think\app",$class为"thinkapp"

    跳出getModuleAndClass,继续回到controller中,$class必须实例化过,所以进入invokeClass中
    67431-113yiraigr.png
    进入app的invokeClass中,实例化$class
    78281-0clg3prr6zb9.png
    回到module中,instance为名字为think\App的类,action为"invokefunction", ReflectionMethod 第一个参数为类,第二个为方法
    82719-meqa1bx3jff.png
    05765-tt8eqlxhlnq.png
    通过注释我们也可得知,该操作获得操作名为invokeFunction
    随后调用invokeMethod
    16827-5peesgojbzy.png
    blindParams用于参数绑定(获取请求的变量)
    function=call_user_func_array&vars[0]=system&vars[1][]=whoam
    44553-fau0xi5wxnp.png
    bindParams将获取到的值存入args
    68569-kxcn2z4cw8j.png
    返回
    29756-anf06kdhx1c.png
    随后开始写入日志
    24483-gzwz27f3n5e.png
    在日志写入完成后,return $reflect->invokeArgs(isset($class) ? $class : null, $args);
    用到了前面铺垫的知识点,返回被调用函数的结果。$class为类,args为参数。
    $reflect为"invokeFunction",将class类、args传入
    //这里一步骤其实就是常规的调用方法
    步入到invokeFunction中,反射调用call_user_func_array函数,args为[‘system’,’whoami’]
    因为invokeFunction需要functionvars参数,所以传参的参数名要一致,否则
    41298-r6hpjp1903.png
    94974-nftol8blz2m.png
    回显出结果后,回到之前的exec中。可以看到,命令已经执行完毕
    04013-ipg42cml2y.png
    execreturn出$data
    最终回显到页面上
    48713-whx4wg06xwr.png

    总结

    • tp的路由方式:模块名/控制器名/操作名
    • \是命名空间的一个标志
    • invokeArgs会反射调用函数,但前提是要实例化函数,采用了reflectionFunction去实例化。而产生这些的函数在app.php的invokefunction之中
      17069-vjpsk0rivha.png

    而参数可控,所以能命令执行。

    • 总体来说,就是利用命名空间的特性,调用了不该访问且不安全的模块中的控制器,进而造成的漏洞

    思考的问题

    1.RCE触发是调用了/thinkphp/library/think/app.php,thinkapp可以过class_check。当我复制出app123.php去打时,发现并未实例化该页面,并不能调用到其中的控制器。那是哪一步实例化的app.php?
    34034-26prp1fu3qzh.png
    学习过程中发现,在application下,只要符合命名空间规范和定义,即可自动实例化

    2.payload中传递的参数名改变为什么会有影响?答:我们在跟进中发现一个方法叫blindParams,其可以将参数名和对应的参数直接对应起来,而后面函数需要用到相关参数时,变量名得一致
    3.能否不用兼容模式,直接用其原本的PATHINFO路由方式?答:可以,但是PATHINFO在部分服务器中并不支持,例如笔者的phpstudy起的环境,会将其视为一个完整的文件访问的路径,从而返回404

    本文作者:硝基苯
    本文链接:https://www.c6sec.com/index.php/archives/482/
    最后修改时间:2022-01-30 00:23:15
    本站未注明转载的文章均为原创,并采用 CC BY-NC-SA 4.0 授权协议,转载请注明来源,谢谢!
    评论已关闭
    评论列表
    暂无评论