File size: 13,938 Bytes
3a5cf48 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | #!/usr/bin/python
# coding: utf-8
# Date 2022/3/29
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: lkq <lkq@bt.cn>
# Java 漏洞扫描
# -------------------------------------------------------------------
import json,time,os
import public
from projectModel.base import projectBase
import zipfile
import hashlib
'''
规则库支持两种类型的规则 class 和hash256
第一种就是class 通过class文件来判断是否存在漏洞
第二种就是hash256 通过hash256来判断是否存在漏洞 对比的是jar包的hash256值
增加漏洞的规则操作如下
1.在vlu目录下创建json文件
2.文件内容如下:
{
"Name": "Fastjson", #漏洞的名称 如果有CVE编号的话就是CVE编号
"product":"fastjson-", #这个模块的关键词
"types":"class", #这个是这个模块的类型 class 或者是hash256
"class":["com/alibaba/fastjson/util/TypeUtils.class"], # 这个是这个模块的class文件 建议是比较好识别的,每个版本的class文件都不一样
"vlu_list": [
{
"CVE": "FastJson-1.2.47,FastJson-1.2.68,FastJson-1.2.80", #这个是这个模块的漏洞编号
"com/alibaba/fastjson/util/TypeUtils.class": "9311585152b2064de8162bc7e933f17f51428115fa08843f0d7b523497072992", #这个是这个模块的class文件的hash值
"MavenVersion": "1.2.10" #这个是这个模块的版本号
"hash256": "b843e17b7b4f748257b1f9e765326dc040c44bc669a4643371b365dd13ef4e6a" # 这个是这个模块的hash256值
}
}
3.增加到cve-database.json中 将这个CVE的信息添加到这个文件中 如上述的FastJson-1.2.47的信息
{
"CVE": "FastJson-1.2.47", #这个是这个模块的漏洞编号
"CVSS": 9.8, #这个是这个模块的漏洞的危险等级
"DESC": "FASTJSON 1.2.47 及以下版本存在 RCE 漏洞,利用条件较低,危害较大" #这个是这个模块的漏洞的描述
}
'''
class main(projectBase):
__config_file = '/www/server/panel/config/java_cve_scanning.json'
__path='/www/server/panel/class/projectModel/vlu'
__vlu_name=[] #存储配置文件中的product name 这个当作遍历jar的时候的关键字
__vlu_name_class={} # 存储配置文件中的product name 和对应的class文件
__vlu_list={} #存储所有的组件的匹配信息
__cve_list={} #存储所有的cve信息
def _get_config(self):
if not os.path.exists(self.__path):
os.makedirs(self.__path)
#遍历path 一层目录
for file in os.listdir(self.__path):
#判断是否为json
if file.endswith('.json'):
#判断是否为cve-database.json
if file == 'cve-database.json':
with open(os.path.join(self.__path,file),'r') as f:
self.__cve_list=json.load(f)
else:
with open(os.path.join(self.__path,file),'r') as f:
try:
vlu_list=json.load(f)
except:
continue
if 'product' not in vlu_list:
continue
if 'class' not in vlu_list:
continue
if 'vlu_list' not in vlu_list:
continue
self.__vlu_name.append(vlu_list['product'])
self.__vlu_name_class[vlu_list['product']]=vlu_list['class']
self.__vlu_list[vlu_list['product']]=vlu_list['vlu_list']
def __check_auth(self):
try:
from pluginAuth import Plugin
plugin_obj = Plugin(False)
plugin_list = plugin_obj.get_plugin_list()
if int(plugin_list['ltd']) > time.time():
return True
return False
except:
return False
def is_spring_boot_jar(self,jar):
"""
判断JAR包是否为Spring Boot项目
Args:
jar: 已打开的ZipFile对象
Returns:
bool: 是否为Spring Boot项目
"""
file_list = jar.namelist()
# 检查Spring Boot特有的标志
spring_boot_indicators = [
# 检查Spring Boot启动类
"org/springframework/boot/loader/",
# 检查Spring Boot特有目录结构
"BOOT-INF/classes/",
"BOOT-INF/lib/"
]
# 检查MANIFEST.MF文件内容是否包含Spring Boot特有的属性
has_spring_boot_manifest = False
if "META-INF/MANIFEST.MF" in file_list:
manifest_content = jar.read("META-INF/MANIFEST.MF").decode('utf-8', errors='ignore')
spring_boot_manifest_indicators = [
"Spring-Boot-Version:",
"Spring-Boot-Classes:",
"Spring-Boot-Lib:",
"Main-Class: org.springframework.boot.loader."
]
for indicator in spring_boot_manifest_indicators:
if indicator in manifest_content:
has_spring_boot_manifest = True
break
# 检查其他Spring Boot特有文件
for indicator in spring_boot_indicators:
for file_name in file_list:
if file_name.startswith(indicator):
return True
return has_spring_boot_manifest
def get_spring_boot_libs(self,jar):
"""
获取Spring Boot项目中的所有库文件
Args:
jar: 已打开的ZipFile对象
Returns:
list: 库文件列表
"""
lib_files = []
for file_name in jar.namelist():
if file_name.startswith("BOOT-INF/lib/") and file_name.endswith(".jar"):
lib_files.append(file_name)
return lib_files
def calculate_sha256(self,data):
"""
计算数据的SHA-256哈希值
Args:
data: 二进制数据
Returns:
str: SHA-256哈希值的十六进制表示
"""
sha256_hash = hashlib.sha256()
sha256_hash.update(data)
return sha256_hash.hexdigest()
def get_jar_class_hash256(self,main_jar, shiro_jars, target_classes):
"""
main_jar : 主JAR文件路径
从主JAR中读取嵌套JAR的字节数据,直接在内存中操作,无需解压。
Args:
main_jar: 已打开的主JAR文件
shiro_jars: Shiro相关JAR文件列表
target_classes : 目标Class文件列表
Returns:
dict: 包含目标Class文件SHA-256哈希值的字典
"""
# 定义要查找的Class文件
class_hashes = {}
# 分析每个Shiro JAR(直接在内存中操作)
for lib_file, jar_name in shiro_jars:
# 从主JAR中读取嵌套JAR的字节数据
nested_jar_data = main_jar.read(lib_file)
from io import BytesIO
nested_jar_bytes = BytesIO(nested_jar_data)
# 直接从内存中打开嵌套JAR
try:
with zipfile.ZipFile(nested_jar_bytes) as shiro_jar:
for target_class in target_classes:
normalized_class = target_class
if not normalized_class.endswith(".class"):
normalized_class += ".class"
try:
class_data = shiro_jar.read(normalized_class)
sha256 = self.calculate_sha256(class_data)
class_hashes[normalized_class] = sha256
class_hashes["lib_path"] = lib_file
except KeyError:
continue
except zipfile.BadZipFile:
continue
except Exception as e:
continue
return class_hashes
def get_jar_hash256(self,main_jar_path):
"""
Args:
main_jar_path: 主JAR文件的路径
"""
list_hash={}
if not os.path.exists(main_jar_path):
return list_hash
try:
# 打开主JAR文件
with zipfile.ZipFile(main_jar_path, 'r') as main_jar:
# 检查是否为Spring Boot项目
is_spring_boot = self.is_spring_boot_jar(main_jar)
if not is_spring_boot:
return list_hash
lib_files = self.get_spring_boot_libs(main_jar)
for lib_name in lib_files:
for vlu_name in self.__vlu_name:
if vlu_name in lib_name:
shiro_jars = []
target_classes = []
for class_name in self.__vlu_name_class[vlu_name]:
target_classes.append(class_name)
jar_name = os.path.basename(lib_name)
shiro_jars.append((lib_name, jar_name))
class_hashes = self.get_jar_class_hash256(main_jar, shiro_jars, target_classes)
list_hash[vlu_name]=class_hashes
except zipfile.BadZipFile:
return list_hash
except Exception as e:
return list_hash
return list_hash
def get_jar_vulnerabilities(self,get):
"""
获取JAR漏洞信息
Args:
get: 请求参数
"""
self._get_config()
# __vlu_name = [] # 存储配置文件中的product name 这个当作遍历jar的时候的关键字
# __vlu_name_class = {} # 存储配置文件中的product name 和对应的class文件
# __vlu_list = {} # 存储所有的组件的匹配信息
# __cve_list = {} # 存储所有的cve信息
get_vlu_hash=self.get_jar_hash256(get.path)
# if len(get_vlu_hash)==0:
# return public.returnMsg(False, '未发现相关漏洞')
tmp_vlu_list={}
if len(get_vlu_hash) > 0:
for i in get_vlu_hash:
if i in self.__vlu_list:
for j in self.__vlu_list[i]:
#获取get_vlu_hash[i] 的keys 判断j 中是否存在
for k in get_vlu_hash[i]:
if k in j:
# 判断hash值是否相等
if get_vlu_hash[i][k]==j[k]:
#通过遍历CVE 来确定这个项目中存在的漏洞
cve_list=j["CVE"].split(',')
for cv in cve_list:
if cv not in tmp_vlu_list:
tmp_vlu_list[cv]={}
tmp_vlu_list[cv]["CVE"]=cv
tmp_vlu_list[cv]["class"]=k
tmp_vlu_list[cv]["hash256"]=j[k]
tmp_vlu_list[cv]["lib"]=get_vlu_hash[i]["lib_path"]
tmp_vlu_list[cv]["jar"]=get.path
tmp_vlu_list[cv]["cvss"]=0.0
tmp_vlu_list[cv]["desc"]=""
tmp_vlu_list[cv]["product"]=False
# if len(tmp_vlu_list)==0:
# return public.returnMsg(False, '未发现相关漏洞')
if len(tmp_vlu_list) > 0:
for i in tmp_vlu_list:
for j in self.__cve_list:
if i ==j["CVE"]:
tmp_vlu_list[i]["cvss"]=j["CVSS"]
tmp_vlu_list[i]["desc"]=j["DESC"]
return tmp_vlu_list
def scan_sites_vlu_list(self,get):
infos=public.M("sites").where("project_type=?","Java").select()
result={"count":0,"time":time.time(),"list":[],"pay":self.__check_auth()}
for i in infos:
if not os.path.exists(i["path"]):
continue
get.path=i["path"]
#判断是否为jar
if not i["path"].endswith('.jar'):
continue
vlu_list=self.get_jar_vulnerabilities(get)
if len(vlu_list)>0:
site_item = {"title": i["name"], "list": []}
for vlu_key in vlu_list:
site_item["list"].append(vlu_list[vlu_key])
result["count"]+=len(vlu_list)
result["list"].append(site_item)
return public.returnMsg(True, result)
def sync_update_rule(self,get):
"更新规则库"
url="https://download.bt.cn/btwaf_rule/vlu/"
import requests
result={}
try:
data_list=requests.get(url+"/java_list.json").json()
for i in data_list:
data=requests.get(url+i).json()
result[i]=data
except:
return public.returnMsg(False, '获取规则库失败')
#判断result 是否为空
if len(result)==0:
return public.returnMsg(False, '获取规则库失败')
for i in result:
with open(os.path.join(self.__path,i),'w') as f:
f.write(json.dumps(result[i]))
return public.returnMsg(True, '更新规则库成功') |