0x01 前言
在Java代碼審計中,需要對引用的第三方組件進行審計,采用的審計方式經(jīng)過了三個階段:
- 前期:一個個收集引用的第三方組件和版本號,繼而一個個詢問度娘
- 中期:了解并收集使用范圍較廣、影響危害較大的組件漏洞,有爭對性的進行排查
- 后期:利用第三方插件或者自己編寫工具利用第三方接口來獲取組件漏洞信息
需求分析,實現(xiàn)包括但不局限于以下功能:
- 根據(jù)pom.xml或者第三方組件所在目錄,自動解析獲取引用的第三方組件和版本號
- 生成的結(jié)果可以導出為excel表格,內(nèi)容包括漏洞名稱、目標組件版本、漏洞影響范圍、漏洞等級、漏洞CVE編號、漏洞披露時間、是否容易利用、參考鏈接等信息
- 會檢查完整的依賴關系
在使用了幾款插件后發(fā)現(xiàn)都不太能滿足個人需求后,遂決定自己編寫工具來實現(xiàn)。
0x02 概念了解
-
Maven
maven是一個用于構(gòu)建和管理Java項目的工具。Maven多以運行插件的方式來達成執(zhí)行目標。 常用的插件有maven-compiler-plugin、maven-clean-plugin、maven-dependency-plugin等等。
Maven在版本管理時候可以使用幾個特殊的字符串 SNAPSHOT,LATEST,RELEASE。比如"1.0-SNAPSHOT"。各個部分的含義和處理邏輯如下說明:
- SNAPSHOT:這個版本一般用于開發(fā)過程中,表示不穩(wěn)定的版本。
- LATEST:指某個特定構(gòu)件的最新發(fā)布,這個發(fā)布可能是一個發(fā)布版,也可能是一個snapshot版,具體看哪個時間最后。
- RELEASE:指最后一個發(fā)布版
-
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定位獲取想要的數(shù)據(jù),如漏洞名稱、影響的組件及版本號、披露時間、漏洞編號、漏洞等級等相關信息
- 3. 利用 retrying 模塊,重試訪問第三方漏洞庫過程中一些可能運行失敗的程序段,最大次數(shù)為3
from retrying import retry
@retry(stop_max_attempt_number=3)
def get_data(target_url, target_third_party):
......
- 4. 設置自適應寬度,標題行背景顏色和字體格式,將結(jié)果寫入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

工具演示使用:
