前言
每次日站,最烦人的就是检查渗透测试报告了,尤其是碰到复测的站点,一不留神可能就写错报告了,导致要多次检查报告内容。就花了点时间学习了下“python-docx库”来实现用python去检查渗透测试报告。
实现逻辑
上交的报告是word版的,首先从文件名中读取出测试站点的名字、网址、测试的版本、测试时间,将其作为一个依据,然后去报告中再次获取相关信息,并进行比对。
这里主要是用到了“python-docx库”的两个功能,一个是读取word文档全文。
代码如下:
from docx import Document
doc = Document(r"C:\Users\asuka\Desktop\test\11.docx")
print(doc.paragraphs)
for paragraph in doc.paragraphs:
print(paragraph.text)
另一个是读取word中的表格,代码如下:
from docx import Document
from openpyxl import Workbook
doc = Document(r"G:\6Tipdm\7python办公自动化\concat_word\test2.docx")
t0 = doc.tables[0]
workbook = Workbook()
sheet = workbook.active
for i in range(len(t0.rows)):
list1 = []
for j in range(len(t0.columns)):
list1.append(t0.cell(i,j).text)
sheet.append(list1)
workbook.save(filename = r"G:\6Tipdm\7python办公自动化\concat_word\来自word中的表.xlsx")
有了上述的两个功能,就足够了。
代码设计
需要安装一下“python-docx库”:pip install python-docx
from docx import Document
from colorama import init, Fore
import os
import re
# 获取word文档封面的日期
def feng_mian():
print('=========分析1:封面中的内容=========')
feng_mian_title = word_file.split('渗透测试报告')[0].strip()
if feng_mian_title == file_web_name:
print(Fore.GREEN + '[√] 封面标题正确:' + feng_mian_title)
else:
print(Fore.RED + '[X] 封面标题与文件名称不一致' + '\t[info] 文件名称:' + file_web_name + ',封面标题:' + feng_mian_title)
feng_mian_time = word_file.split('公司名字【手动替换不予展示】')[-1].split('文件修订记录')[0].strip()
if fileyear + '年' + filemonth + '月' + filedata + '日' == feng_mian_time:
print(Fore.GREEN + '[√] 封面日期正确:' + feng_mian_time)
else:
print(Fore.RED + '[X] 封面日期与文件日期不一致' + '\t[info] 文件日期:' + filetime + ',封面日期:' + feng_mian_time)
# 获取文件修订记录的日期
def date_of_revision():
print('=========分析2:文件修订记录=========')
tables = doc.tables # 获取文件中的表格集
table = tables[1] # 获取文件中的第2个表格:文件修订记录表格
# for i in range(1, len(table.rows)): # 从表格第二行开始循环读取表格数据
# result = table.cell(i, 0).text + "" + table.cell(i, 1).text + table.cell(i, 2).text + \
# table.cell(i, 3).text + table.cell(i, 4).text
# # cell(i,0)表示第(i+1)行第1列数据,以此类推
# print(result)
date_of_revision_time = table.cell(1, 4).text
if filetime == date_of_revision_time:
print(Fore.GREEN + '[√] 文件修订记录变更日期正确:' + date_of_revision_time)
else:
print(
Fore.RED + '[X] 文件修订记录变更日期与文件日期不一致' + '\t[info] 文件日期:' + filetime + ',文件修订记录变更日期:' + date_of_revision_time)
# 测试对象与测试结果
def test_objects_and_test_results():
print('=========分析3:测试对象与测试结果=========')
tables = doc.tables # 获取文件中的表格集
table = tables[2] # 获取文件中的第2个表格:文件修订记录表格
# 判断检测日期
if fileyear in table.cell(2, 1).text and filemonth in table.cell(2, 1).text and filedata in table.cell(2, 1).text:
print(Fore.GREEN + '[√] 检测时间正确:' + table.cell(2, 1).text)
else:
print(Fore.RED + '[X] 检测时间与文件日期不一致' + '\t[info] 文件日期:' + filetime + ',检测时间:' + table.cell(2, 1).text)
# 判断检测版本
# print(table.cell(3, 1).text) # 获取检测结果
word_content_version = table.cell(3, 1).text.split('通过本次渗透测试工作')[0].strip().split('渗透测试')[0] + '渗透测试'
if file_version in word_content_version:
print(Fore.GREEN + '[√] 测试结果中的版本正确:' + word_content_version)
else:
print(
Fore.RED + '[X] 测试结果中的版本与文件的版本不一致' + '\t[info] 文件版本:' + file_version + ',测试结果中的版本:' + word_content_version)
# 判断测试结果日期
test_result_time = table.cell(3, 1).text.split('【手动隐藏部门名称】')[-1].strip()
if fileyear + '年' + filemonth + '月' + filedata + '日' == test_result_time:
print(Fore.GREEN + '[√] 测试结果中的日期正确:' + test_result_time)
else:
print(Fore.RED + '[X] 测试结果中的日期与文件日期不一致' + '\t[info] 文件日期:' + filetime + ',测试结果中的日期:' + test_result_time)
# 提取测试结果中的漏洞
risk_all1 = table.cell(3, 1).text.split('共发现')[-1].split('个可能潜在')[0]
risk_high1 = table.cell(3, 1).text.split('高风险级别的问题有')[-1].split('个,中风险级')[0]
risk_mid1 = table.cell(3, 1).text.split('中风险级别的问题有')[-1].split('个,低风险')[0]
risk_low1 = table.cell(3, 1).text.split('低风险为')[-1].split('个')[0]
if int(risk_all1) == int(risk_low1) + int(risk_mid1) + int(risk_high1):
print(Fore.GREEN + '[√] 测试结果中的漏洞数量自身吻合:' +
'漏洞总数:' + risk_all1 + ',高危:' + risk_high1 + ',中危:' + risk_mid1 + ',低危:' + risk_low1)
else:
print(Fore.RED + '[X] 测试结果中的漏洞数量自身异常' +
'\t[info] 漏洞总数:' + risk_all1 + ',高危:' + risk_high1 + ',中危:' + risk_mid1 + ',低危:' + risk_low1)
return risk_all1, risk_high1, risk_mid1, risk_low1
# 判断测试范围
def test_range():
print('=========分析4:渗透测试范围=========')
tables = doc.tables # 获取文件中的表格集
table = tables[4] # 获取文件中的第4个表格:渗透测试范围
# 判断网站名称
if file_web_name == table.cell(1, 1).text:
print(Fore.GREEN + '[√] 渗透测试范围的网站名称正确:' + table.cell(1, 1).text)
else:
message = '[X] 渗透测试范围的网站名称与文件中的名称不一致' + \
'\t[info] 文件名中的网站名称:' + file_web_name + ',渗透测试范围的网站名称:' + table.cell(1, 1).text
print(Fore.RED + message)
# 判断网站地址
flag = 1
for i in file_web_path_list:
if i not in table.cell(1, 2).text:
message1 = '[X] 渗透测试范围的网站地址与文件中地址不一致' + \
'\t[info] 文件名中的网站地址:' + file_web_path + ',渗透测试范围的网站地址:' + table.cell(1, 2).text
print(Fore.RED + message1)
flag = 0
break
if flag:
print(Fore.GREEN + '[√] 渗透测试范围的网站地址正确:' + table.cell(1, 2).text)
# 获取渗透测试记录
def word_content_test_records():
print('=========分析5:渗透测试记录=========')
tables = doc.tables # 获取文件中的表格集
table = tables[5] # 获取文件中的第5个表格:渗透测试记录
# print(len(table.rows)) # 获取表格的的行数
# print(len(table.columns)) # 获取表格的列数
result = ''
for i in range(1, len(table.rows)):
for j in range(1, len(table.columns)):
result = result + table.cell(i, j).text + '\t'
result = result + '\n'
word_content_test_records_version = result.rsplit('P', 1)[-1].split(".")[0].replace("'", '')
if file_version == word_content_test_records_version:
print(Fore.GREEN + '[√] 渗透测试记录的次数正确:第' + word_content_test_records_version + '次测试')
else:
print(
Fore.RED + '[X] 渗透测试记录的次数与文件名称中的版本号不符' + '\t[info] 文件名称中的版本号:' + file_version + ',渗透测试记录:' + word_content_test_records_version + '\n' + result)
# 分析风险等级概况
def risk_level_describe():
print('=========分析6:风险等级概况=========')
describe_all = word_file.split('在渗透测试范围内共发现')[-1].split('个可能潜在的安全问题')[0] # 获取漏洞总数
describe_high = word_file.split('其中高风险级别的问题有')[-1].split('个,中风险级别的问题有')[0] # 获取高危漏洞
describe_mid = word_file.split('个,中风险级别的问题有')[-1].split('个,低风险为')[0] # 获取中危漏洞
describe_low = word_file.split('个,低风险为')[-1].split('按照风险等级分类如下图所示')[0].split('个。')[0] # 获取低危漏洞
if int(describe_all) == int(describe_high) + int(describe_mid) + int(describe_low):
print(Fore.GREEN + '[√] 风险等级概况中的漏洞数量自身吻合:' +
'漏洞总数:' + describe_all + ',高危:' + describe_high + ',中危:' + describe_mid + ',低危:' + describe_low)
else:
print(Fore.RED + '[X] 测试结果中的漏洞数量自身异常' +
'\t[info] 漏洞总数:' + describe_all + ',高危:' + describe_high + ',中危:' + describe_mid + ',低危:' + describe_low)
if describe_all == test_risk_all and test_risk_high == describe_high and describe_mid == test_risk_mid and describe_low == test_risk_low:
print(Fore.GREEN + '[√] 风险等级概况中的漏洞数量 与 测试结果中的漏洞数量吻合')
else:
print(Fore.RED + '[X] 风险等级概况中的漏洞数量 与 测试结果中的漏洞数量不匹配')
print(
Fore.RED + '\t测试结果中的漏洞数量:漏洞总数:' + test_risk_all + ',高危:' + test_risk_high + ',中危:' + test_risk_mid + ',低危:' + test_risk_low)
return describe_all, describe_high, describe_mid, describe_low
# 分析漏洞详细列表
def detailed_list_of_vulnerabilities():
print('=========分析7:漏洞详细列表=========')
tables = doc.tables # 获取文件中的表格集
table = tables[6] # 获取文件中的第6个表格:漏洞详细列表
# print(len(table.rows)) # 获取表格的的行数
# print(len(table.columns)) # 获取表格的列数
# 提取出“漏洞详细列表”中的“名称(地址)”中的地址,判断网站名称
reg = "[^\u4e00-\u9fa5]" # 提取出所有的中文字符,即可得到目标的名字
name_and_uri = table.cell(1, 1).text
target_name = re.sub(reg, '', name_and_uri)
if file_web_name == target_name:
print(Fore.GREEN + '[√] 漏洞详细列表的网站名字正确:' + target_name)
else:
print(Fore.RED + '[X] 漏洞详细列表的网站名字 与 文件名称中的名字不符' + '\t文件名称中的名字:' + file_web_name + ',漏洞详细列表的网站名字' + target_name)
# 判断网站地址
flag = 1
target_uri = name_and_uri.replace(target_name, '').strip()
for i in file_web_path_list:
if i not in target_uri:
message1 = '[X] 漏洞详细列表的网站地址与文件中地址不一致' + \
'\t[info] 文件名中的网站地址:' + file_web_path + ',漏洞详细列表的网站地址:' + target_uri
print(Fore.RED + message1)
flag = 0
break
if flag:
print(Fore.GREEN + '[√] 漏洞详细列表的网站地址正确:' + target_uri)
# 分析漏洞数量与危险级别
form_all = 0
form_high = 0
form_mid = 0
form_low = 0
result111 = ''
for i in range(1, len(table.rows)):
for j in range(2, len(table.columns)):
result111 = result111 + table.cell(i, j).text + '\t' # 用来拼接获取“漏洞详细列表”
# 判断是否是“危险级别”的那列
if table.cell(i, j).text == '高':
form_high = form_high + int(table.cell(i, j - 1).text)
if table.cell(i, j).text == '中':
form_mid = form_mid + int(table.cell(i, j - 1).text)
if table.cell(i, j).text == '低':
form_low = form_low + int(table.cell(i, j - 1).text)
result111 = result111 + '\n'
form_all = form_high + form_mid + form_low
form_all = str(form_all)
form_high = str(form_high)
form_mid = str(form_mid)
form_low = str(form_low)
print(Fore.GREEN + '[√] 漏洞详细列表中的漏洞数量:' +
'漏洞总数:' + form_all + ',高危:' + form_high + ',中危:' + form_mid + ',低危:' + form_low)
# 比对“漏洞详细列表”和"测试结果中的漏洞数量"
if form_all == test_risk_all and form_high == test_risk_high and form_mid == test_risk_mid and form_low == test_risk_low:
print(Fore.GREEN + '[√] 漏洞详细列表中的漏洞数量 与 测试结果中的漏洞数量吻合')
else:
print(Fore.RED + '[X] 漏洞详细列表中的漏洞数量 与 测试结果中的漏洞数量不匹配')
print(
Fore.RED + '\t漏洞详细列表中的漏洞数量:漏洞总数:' + form_all + ',高危:' + form_high + ',中危:' + form_mid + ',低危:' + form_low)
print(
Fore.RED + '\t测试结果中的漏洞数量:漏洞总数:' + test_risk_all + ',高危:' + test_risk_high + ',中危:' + test_risk_mid + ',低危:' + test_risk_low)
# 比对“漏洞详细列表”和"风险等级概况中的漏洞数量"
if form_all == out_describe_all and form_high == out_describe_high and form_mid == out_describe_mid and form_low == out_describe_low:
print(Fore.GREEN + '[√] 漏洞详细列表中的漏洞数量 与 风险等级概况中的漏洞数量吻合')
else:
print(Fore.RED + '[X] 漏洞详细列表中的漏洞数量 与 风险等级概况中的漏洞数量不匹配')
print(
Fore.RED + '\t漏洞详细列表中的漏洞数量:漏洞总数:' + form_all + ',高危:' + form_high + ',中危:' + form_mid + ',低危:' + form_low)
print(
Fore.RED + '\t风险等级概况中的漏洞数量:漏洞总数:' + out_describe_all + ',高危:' + out_describe_high + ',中危:' + out_describe_mid + ',低危:' + out_describe_low)
print('漏洞详情列表如下:')
print(result111)
# 检查“安全漏洞详细分析”
# 思路:提取“漏洞详细列表”中的内容,去“安全漏洞详细分析”中检索
def detailed_vulnerability_analysis():
print('=========分析8:安全漏洞详细分析=========')
tables = doc.tables # 获取文件中的表格集
table = tables[6] # 获取文件中的第6个表格:漏洞详细列表
for i in range(1, len(table.rows)):
vulnerability_information = table.cell(i, 2).text + '(' + table.cell(i, 4).text + ')'
if vulnerability_information in word_file:
print(Fore.GREEN + '[√] 漏洞详细列表中的漏洞 在 安全漏洞详细分析中找到:' + vulnerability_information)
else:
print(Fore.RED + '[√] 漏洞详细列表中的漏洞 在 安全漏洞详细分析中没找到:' + vulnerability_information)
'''
正文内容
'''
print('''渗透测试报告专用检查脚本(仅支持docx格式文档)
注意事项:
1. 运行脚本时,docx文档最好是关闭状态,否则有可能报错
2. 如果出现报错,建议文件“另存为”或重命名(报错原因多是由于上传VSS导致文件属性变化导致的)
3. 脚本能够穿透目标目录,检查出所有docx文档''')
input_path = input('请输入文件所在目录,或者按回车键选择当前脚本所在路径:')
if len(input_path) == 0:
input_path = os.getcwd()
else:
if os.path.isdir(input_path):
pass
else:
pass
# exit('无效路径,程序自动结束!')
docx_list = [] # 用来存储所有docx后缀的渗透测试报告文档的绝对路劲
init(autoreset=True)
for current_folder, list_folders, files in os.walk(input_path):
for f in files: # 用来遍历所有的文件,只取文件名,不取路径名
if f.endswith('docx'): # 判断文件是不是docx类型的
path_f = current_folder + '\\' + f # 给出文档的的绝对路径
docx_list.append(path_f)
for file in docx_list:
print(Fore.YELLOW + '[+] 开始测试:' + os.path.basename(file))
doc = Document(file)
word_file = '' # 用来存储word中的文字内容
# 从文件名中提取如日期,如:20220427
matches = re.findall(r"\d{8}", file)
if len(matches) == 1:
matches = str(matches).replace("['", '').replace("']", '')
else:
exit('文件名称中日期有误:' + str(matches))
filetime = matches
fileyear = filetime[0:4]
filemonth = filetime[4:6]
filedata = filetime[6:8]
# 从文件名中提取出测试的版本
file_version = file.split('(P')[-1].split('.0)')[0]
# 从文件名中提取出网站名称
file_web_name = os.path.basename(file).split('(', 1)[0]
# 从文件名中提取出网站地址
file_web_path = file.split('(', 1)[-1].split(')', 1)[0]
file_web_path_list = file_web_path.split('_')
# print(file_web_path_list)
# 读取到word文件中的内容,存储到 word_file 中
for paragraph in doc.paragraphs:
# print(paragraph.text)
word_file = word_file + paragraph.text + '\n'
feng_mian()
date_of_revision()
test_risk_all, test_risk_high, test_risk_mid, test_risk_low = test_objects_and_test_results()
test_range()
word_content_test_records()
out_describe_all, out_describe_high, out_describe_mid, out_describe_low = risk_level_describe()
detailed_list_of_vulnerabilities()
detailed_vulnerability_analysis()
print(Fore.YELLOW + '[-] 测试结束'+'\n')
# os.system('pause') # 在脚本中运行时可以删除此行
实现效果如下: