0x01 前言
在Java代码审计中,需要对引用的第三方组件进行审计,采用的审计方式经过了三个阶段:
- 前期:一个个收集引用的第三方组件和版本号,继而一个个询问度娘
- 中期:了解并收集使用范围较广、影响危害较大的组件漏洞,有争对性的进行排查
- 后期:利用第三方插件或者自己编写工具利用第三方接口来获取组件漏洞信息
需求分析,实现包括但不局限于以下功能:
- 根据pom.xml或者第三方组件所在目录,自动解析获取引用的第三方组件和版本号
- 生成的结果可以导出为excel表格,内容包括漏洞名称、目标组件版本、漏洞影响范围、漏洞等级、漏洞CVE编号、漏洞披露时间、是否容易利用、参考链接等信息
- 会检查完整的依赖关系
在使用了几款插件后发现都不太能满足个人需求后,遂决定自己编写工具来实现。
0x02 概念了解
-
Maven
maven是一个用于构建和管理Java项目的工具。Maven多以运行插件的方式来达成执行目标。 常用的插件有maven-compiler-plugin、maven-clean-plugin、maven-dependency-plugin等等。
Maven在版本管理时候可以使用几个特殊的字符串 SNAPSHOT,LATEST,RELEASE。比如"1.0-SNAPSHOT"。各个部分的含义和处理逻辑如下说明:
- SNAPSHOT:这个版本一般用于开发过程中,表示不稳定的版本。
- LATEST:指某个特定构件的最新发布,这个发布可能是一个发布版,也可能是一个snapshot版,具体看哪个时间最后。
- RELEASE:指最后一个发布版
-
POM
项目对象模型。作为Maven工程配置文件,它是一个Maven项目的XML表示,默认保存在pom.xml文件中。在maven工程中,我们依靠在pom.xml文件进行配置完成jar包管理工作。
-
Dependency
依赖项声明了当前项目所依赖的其他项目。
Maven 通过 groupId、 artifactId 与 version 三个向量来定位Maven仓库其jar包所在的位置,并把对应的jar包引入到工程中来。
jar包下载流程如下:
0x03 工具编写
- 1. 利用beautifulsoup4模块解析pom.xml文件,简单示例代码:
def get_third_party_components_information(xmlfile):
with open(xmlfile, 'r', encoding='utf-8') as rfile:
data = rfile.read()
if str(xmlfile).endswith('xml'):
third_party_list, target_third_party_list = [], []
soup = Bp(data, 'xml')
datas = soup.find_all("dependency")
for single_data in datas:
groupId = str(single_data.groupId).replace('<groupId>', '').replace('</groupId>', '')
artifactId = str(single_data.artifactId).replace('<artifactId>', '').replace('</artifactId>', '')
third_party_list.append(f'{groupId}:{artifactId}')
version = str(single_data.version).replace('<version>', '').replace('</version>', '')
if re.search(r'[0-9\.]{3,}', version):
target_third_party_list.append(f'{groupId}:{artifactId}:{version}')
elif version.startswith('${'):
version = version.lstrip('${').rstrip('}')
version = str(soup.find_all(version)[0]).replace(version, '').replace('</', '').replace('<', '').replace('>', '')
target_third_party_list.append(f'{groupId}:{artifactId}:{version}')
else:
target_third_party_list.append(f'{groupId}:{artifactId}:[]')
- 2. 利用第三方漏洞库查询组件漏洞:Vulnerability DB | Snyk
利用xpath定位获取想要的数据,如漏洞名称、影响的组件及版本号、披露时间、漏洞编号、漏洞等级等相关信息
- 3. 利用 retrying 模块,重试访问第三方漏洞库过程中一些可能运行失败的程序段,最大次数为3
from retrying import retry
@retry(stop_max_attempt_number=3)
def get_data(target_url, target_third_party):
......
- 4. 设置自适应宽度,标题行背景颜色和字体格式,将结果写入excel表格
- 5. 利用 PySimpleGUI 模块编写简单图形界面
def generate_gui():
global page_num, results, third_party_list, target_third_party_list
sg.theme('DarkAmber')
layout = [
[sg.Text('选项①:请指定pom.xml或者.classpath文件:')],
[sg.In(size=(50, 1), enable_events=False, key='file'), sg.FileBrowse('Choose File')],
[sg.Text('选项②:请选择第三方组件所在目录 (/WEB-INF/lib)')],
[sg.In(size=(50, 1), enable_events=False, key='file_path'), sg.FolderBrowse(button_text='Choose Folder')],
[sg.Text('请选择输出路径:(默认输出路径为桌面)')],
[sg.InputText(os.getcwd(), key='folder', size=(50, 1)), sg.FolderBrowse(button_text='Choose Folder',target='folder')],
[sg.Frame(layout=[
[sg.Radio('选项一', "RADIO1", key='one', default=True, size=(17, 1)), sg.Radio('选项二', "RADIO1", key='two', size=(17, 1))],
[sg.Checkbox('检查完整的依赖关系 (仅限pom.xml)', key='all', default=False)]], title='Options', title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='需在对应选项上选择文件或目录')],
[sg.Button('Run', border_width=2, size=(10, 1), auto_size_button=True, pad=((190, 0), (20, 0)))]
]
window = sg.Window('Third_party_components_scanner By:book4yi', layout)
window.DisableClose = False
工具演示使用: