Files
aaPanel/class_v2/log_analysis_v2.py
T
Jack a24f2e23c6 Update to 7.25.0
1. Added Mail Marketing Automation trigger tasks
2. Added Mail Marketing Groups Import, Export, Merge
3. Added Mail Marketing Subscribers to support paste import
4. Added Mail Marketing Template Import Export Duplicate
5. Added Mail server add timed automatic reply
6. Added HTTPS Protection function for Website (disables automatic HTTP to HTTPS redirection when enabled)
7. Optimize Website Interface Button Integration
8. Optimize the response speed of WP Toolkit interface
2025-03-13 17:52:26 +08:00

301 lines
12 KiB
Python

# coding: utf-8
# +-------------------------------------------------------------------
# | aaPanel
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 aaPanel(www.aapanel.com) All rights reserved.
# +-------------------------------------------------------------------
# | Author: lkq <lkq@aapanel.com>
# |
# | 日志分析工具
# +-------------------------------------------------------------------
import os
import time
import public
from public.validate import Param
class log_analysis:
path = '/www/server/panel/script/'
log_analysis_path = '/www/server/panel/script/log_analysis.sh'
def __init__(self):
if not os.path.exists(self.path + '/log/'): os.makedirs(self.path + '/log/')
if not os.path.exists(self.log_analysis_path):
log_analysis_data = r'''help(){
echo "Usage: ./action.sh [options] [FILE] [OUTFILE] "
echo "Options:"
echo "xxx.sh san_log [FILE] Get the log list with the keywords xss|sql|mingsense information|php code execution in the successful request [OUTFILE] 11"
echo "xxx.sh san [FILE] Get list of logs with sql keyword in successful request [OUTFILE] 11 "
}
if [ $# == 0 ]
then
help
exit
fi
if [ ! -e $2 ]
then
echo -e "$2: log file does not exist"
exit
fi
if [ ! -d "log" ]
then
mkdir log
fi
echo "[*] Starting ..."
if [ $1 == "san_log" ]
then
echo "1">./log/$3
echo "Start getting xss cross-site scripting attack logs..."
grep -E ' (200|302|301|500|444|403|304) ' $2 | grep -i -E "(javascript|data:|alert\(|onerror=|%3Cimg%20src=x%20on.+=|%3Cscript|%3Csvg/|%3Ciframe/|%3Cscript%3E).*?HTTP/1.1" >./log/$3xss.log
echo "Analysis logs have been saved to./log/$3xss.log"
echo "Scan to attack count: "`cat ./log/$3xss.log |wc -l`
echo "20">./log/$3
echo "Start getting sql injection attack logs..."
echo "Analysis logs have been saved to./log/$3sql.log"
grep -E ' (200|302|301|500|444|403) ' $2 | grep -i -E "(from.+?information_schema.+|select.+(from|limit)|union(.*?)select|extractvalue\(|case when|extractvalue\(|updatexml\(|sleep\().*?HTTP/1.1" > ./log/$3sql.log
echo "Scan to attack count: "`cat ./log/$3sql.log |wc -l`
echo "40">./log/$3
echo -e "Start getting related logs such as file traversal/code execution/scanner information/configuration files"
grep -E ' (200|302|301|500|444|403) ' $2 | grep -i -E "(\.\.|WEB-INF|/etc|\w\{1,6\}\.jsp |\w\{1,6\}\.php|\w+\.xml |\w+\.log |\w+\.swp |\w*\.git |\w*\.svn |\w+\.json |\w+\.ini |\w+\.inc |\w+\.rar |\w+\.gz |\w+\.tgz|\w+\.bak |/resin-doc).*?HTTP/1.1" >./log/$3san.log
echo "Analysis logs have been saved to./log/$3san.log"
echo "Scan to attack count: "`cat ./log/$3san.log |wc -l`
echo "50">./log/$3
echo -e "Start getting the php code execution scan log"
grep -E ' (200|302|301|500|444|403) ' $2 | grep -i -E "(gopher://|php://|file://|phar://|dict://data://|eval\(|file_get_contents\(|phpinfo\(|require_once\(|copy\(|\_POST\[|file_put_contents\(|system\(|base64_decode\(|passthru\(|\/invokefunction\&|=call_user_func_array).*?HTTP/1.1" >./log/$3php.log
echo "Analysis logs have been saved to./log/$3php.log"
echo "Scan to attack count: "`cat ./log/$3php.log |wc -l`
echo "60">./log/$3
echo -e "The number and value of the most visited ip is being counted"
# cat $2|awk -F" " '{print $1}'|sort|uniq -c|sort -nrk 1 -t' '|head -100
awk '{print $1}' $2 |sort|uniq -c |sort -nr |head -100 >./log/$3ip.log
echo "80">./log/$3
echo -e "The number and value of the url of the most visited request interface is being counted"
awk '{print $7}' $2 |sort|uniq -c |sort -nr |head -100 >./log/$3url.log
echo "100">./log/$3
elif [ $1 == "san" ]
then
echo "1">./log/$3
echo "Start getting xss cross-site scripting attack logs..."
grep -E ' (200|302|301|500|444|403|304) ' $2 | grep -i -E "(javascript|data:|alert\(|onerror=|%3Cimg%20src=x%20on.+=|%3Cscript|%3Csvg/|%3Ciframe/|%3Cscript%3E).*?HTTP/1.1" >./log/$3xss.log
echo "Analysis logs have been saved to./log/$3xss.log"
echo "Scan to attack count: "`cat ./log/$3xss.log |wc -l`
echo "20">./log/$3
echo "Start getting sql injection attack logs..."
echo "Analysis logs have been saved to./log/$3sql.log"
grep -E ' (200|302|301|500|444|403) ' $2 | grep -i -E "(from.+?information_schema.+|select.+(from|limit)|union(.*?)select|extractvalue\(|case when|extractvalue\(|updatexml\(|sleep\().*?HTTP/1.1" > ./log/$3sql.log
echo "Scan to attack count: "`cat ./log/$3sql.log |wc -l`
echo "40">./log/$3
echo -e "Start getting related logs such as file traversal/code execution/scanner information/configuration files"
grep -E ' (200|302|301|500|444|403) ' $2 | grep -i -E "(\.\.|WEB-INF|/etc|\w\{1,6\}\.jsp |\w\{1,6\}\.php|\w+\.xml |\w+\.log |\w+\.swp |\w*\.git |\w*\.svn |\w+\.json |\w+\.ini |\w+\.inc |\w+\.rar |\w+\.gz |\w+\.tgz|\w+\.bak |/resin-doc).*?HTTP/1.1" >./log/$3san.log
echo "Analysis logs have been saved to./log/$3san.log"
echo "Scan to attack count: "`cat ./log/$3san.log |wc -l`
echo "60">./log/$3
echo -e "Start getting the php code execution scan log"
grep -E ' (200|302|301|500|444|403) ' $2 | grep -i -E "(gopher://|php://|file://|phar://|dict://data://|eval\(|file_get_contents\(|phpinfo\(|require_once\(|copy\(|\_POST\[|file_put_contents\(|system\(|base64_decode\(|passthru\(|\/invokefunction\&|=call_user_func_array).*?HTTP/1.1" >./log/$3php.log
echo "Analysis logs have been saved to./log/$3php.log"
echo "Scan to attack count: "`cat ./log/$3php.log |wc -l`
echo "100">./log/$3
else
help
fi
echo "[*] shut down"
'''
public.WriteFile(self.log_analysis_path, log_analysis_data)
def get_log_format(self, path):
'''
@获取日志格式
'''
f = open(path, 'r')
data = None
for i in f:
data = i.split()
break
f.close()
if not data: return False
if not public.check_ip(data[0]): return False
if len(data) < 6: return False
return True
def log_analysis(self, get):
'''
分析日志
@param path:需要分析的日志
@return 返回具体的分析结果
@ 需要使用异步的方式进行扫描
'''
# 校验参数
try:
get.validate([
Param('action').String(),
Param('path').String(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
path = get.path
log_path = public.Md5(path)
serverType = public.get_webserver()
if serverType == "nginx":
pass
elif serverType == 'apache':
#path = path.strip("-access_log") + '-access_log'
pass
elif serverType == 'openlitespeed':
# path = path.strip("_ols.access_log") + '_ols.access_log'
# return public.ReturnMsg(False, 'openlitespeed is not supported yet')
return public.fail_v2('openlitespeed is not supported yet')
# public.print_log("path1:{}".format(path))
# public.print_log("serverType:{}".format(serverType))
if not os.path.exists(path): return public.return_message(-1, 0, public.lang("No log file"))
if os.path.getsize(path) > 9433107294: return public.return_message(-1, 0, public.lang("The log file is too large!"))
if os.path.getsize(path) < 10: return public.return_message(-1, 0, public.lang("log is empty"))
# public.print_log("log_path{}".format(log_path))
# public.print_log("self.log_analysis_path{}".format(self.log_analysis_path))
# public.print_log("path{}".format(path))
if self.get_log_format(path):
public.ExecShell(
"cd %s && bash %s san_log %s %s &" % (self.path, self.log_analysis_path, path, log_path))
else:
public.ExecShell("cd %s && bash %s san %s %s &" % (self.path, self.log_analysis_path, path, log_path))
speed = self.path + '/log/' + log_path+".time"
public.WriteFile(speed,str(time.time())+"[]"+time.strftime('%Y-%m-%d %X',time.localtime())+"[]"+"0")
return public.return_message(0, 0, public.lang("Start scan successful"))
def speed_log(self, get):
'''
扫描进度
@param path:扫描的日志文件
@return 返回进度
'''
# 校验参数
try:
get.validate([
Param('path').String(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
path = get.path.strip()
log_path = public.Md5(path)
speed = self.path + '/log/' + log_path
if os.path.getsize(speed) < 1: return public.return_message(-1, 0, public.lang("log is empty"))
if not os.path.exists(speed): return public.return_message(-1, 0, public.lang("The directory was not scanned"))
try:
data = public.ReadFile(speed)
data = int(data)
if data==100:
time_data,start_time,status=public.ReadFile(self.path + '/log/' + log_path+".time").split("[]")
public.WriteFile(speed+".time",str(time.time()-float(time_data)) + "[]" + start_time + "[]" + "1")
return public.return_message(0,0, data)
except:
return public.return_message(0, 0, 0)
def get_log_count(self, path, is_body=False):
count = 0
if is_body:
if not os.path.exists(path): return ''
data = ''
with open(path, 'r') as f:
for i in f:
count += 1
data = data.replace('<', '&lt;').replace('>', '&gt;') + i.replace('<', '&lt;').replace('>', '&gt;')
if count >= 300: break
return data
else:
if not os.path.exists(path): return count
with open(path, 'rb') as f:
for i in f:
count += 1
return count
def get_result(self, get):
'''
扫描结果
@param path:扫描的日志文件
@return 返回结果
'''
path = get.path.strip()
log_path = public.Md5(path)
speed = self.path + '/log/' + log_path
result = {}
if os.path.exists(speed):
result['is_status'] = True
else:
result['is_status'] = False
if os.path.exists(speed+".time"):
time_data, start_time, status = public.ReadFile(self.path + '/log/' + log_path + ".time").split("[]")
if status == '1' or start_time==1:
result['time']=time_data
result['start_time']=start_time
else:
result['time'] = "0"
result['start_time'] = "2022/2/22 22:22:22"
if 'time' not in result:
result['time'] = "0"
result['start_time'] = "2022/2/22 22:22:22"
result['xss'] = self.get_log_count(speed + 'xss.log')
result['sql'] = self.get_log_count(speed + 'sql.log')
result['san'] = self.get_log_count(speed + 'san.log')
result['php'] = self.get_log_count(speed + 'php.log')
result['ip'] = self.get_log_count(speed + 'ip.log')
result['url'] = self.get_log_count(speed + 'url.log')
return public.return_message(0,0,result)
def get_detailed(self, get):
# 校验参数
try:
get.validate([
Param('path').String(),
Param('type').String(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
path = get.path.strip()
log_path = public.Md5(path)
speed = self.path + '/log/' + log_path
type_list = ['xss', 'sql', 'san', 'php', 'ip', 'url']
if get.type not in type_list: return public.return_message(-1, 0, public.lang("Type mismatch"))
if not os.path.exists(speed + get.type + '.log'): return public.return_message(-1, 0, public.lang("Record does not exist"))
return public.return_message(0,0,self.get_log_count(speed + get.type + '.log', is_body=True))