Python驱动Sqlmap实现OA系统SQL注入自动化检测实战
1. 项目概述:为什么需要自动化检测OA系统的SQL注入风险
最近在和一些做安全运维的朋友聊天,发现一个挺普遍的现象:很多企业,尤其是传统行业,内部部署了大量的OA、ERP这类办公系统,像泛微、用友、致远这些牌子大家都不陌生。这些系统承载着公司核心的流程和数据,但安全状况却常常让人捏把汗。朋友提到,他们最头疼的就是应对各种安全扫描和渗透测试需求,手动去测一个像泛微e-Bridge这样的系统,光是找入口点、构造测试用例就能耗上大半天,效率低不说,还容易遗漏。
这让我想起了几年前自己干安全评估的苦日子。那时候测试一个Web系统,SQL注入是必查项,拿着Sqlmap这个神器,却总要一遍遍手动输入URL、设置参数、调整Payload,测试不同的注入点就像在重复机械劳动。尤其是面对OA系统,它往往有几十上百个功能页面,每个页面又有多个参数,纯手工操作几乎是个不可能完成的任务。于是,我就琢磨着,能不能用Python写个脚本,把Sqlmap给“包装”起来,让它能自动识别目标系统的页面和参数,然后智能地、批量化地去跑注入测试?这个想法最终落地成了今天要分享的这套方法。
简单来说,这个项目就是用Python脚本驱动Sqlmap,实现对泛微e-Bridge这类典型OA系统的自动化SQL注入漏洞扫描。它解决的核心痛点是 效率 和 覆盖率 。你不再需要手动一个个URL去测试,脚本会自动爬取目标站点的链接,提取表单和URL参数,然后生成针对性的Sqlmap命令进行扫描。这对于需要定期进行安全自查的运维人员、或是承接了大量系统安全测试项目的安全工程师来说,无疑是一把利器。即使你Python刚入门,只要跟着步骤走,也能搭建起这套自动化检测环境,将自己的工作效率提升好几个数量级。
2. 核心思路与技术选型:为什么是Python+Sqlmap
在决定技术方案时,我主要考虑了三个维度:工具的成熟度、集成的灵活性以及最终方案的可维护性。市面上当然也有一些商业的或开源的Web漏洞扫描器,但它们要么太“重”,配置复杂,要么不够灵活,无法针对OA系统特有的URL结构和参数进行深度定制。
首先,为什么选择Sqlmap? 这是毋庸置疑的。在SQL注入检测领域,Sqlmap是事实上的行业标准。它支持几乎所有的数据库类型(MySQL、Oracle、SQL Server、PostgreSQL等),检测算法强大,能自动识别WAF并尝试绕过,并且提供了极其丰富的参数来精细控制扫描行为。它的稳定性和检出率经过了无数实战的检验。我们的目标不是重新造轮子,而是如何更高效地使用这个“超级轮子”。
其次,为什么用Python来驱动? 这里有几个关键原因:
- 强大的库生态 :Python有
requests、BeautifulSoup、lxml这样的库,可以非常方便地实现网页爬取、HTML解析和表单提取,这是我们自动化发现测试目标的基础。 - 灵活的进程控制 :Python的
subprocess模块可以完美地调用并控制像Sqlmap这样的命令行工具,我们能捕获其输出、解析结果,并根据结果决定后续流程。 - 易于编写和调试 :Python语法简洁,能快速实现业务逻辑。我们可以很方便地添加日志、错误处理、并发控制等功能,让脚本更健壮。
- 广泛的社区支持 :遇到任何问题,几乎都能在社区找到解决方案或思路。
整体的工作流程 可以概括为以下几步,这也是我们脚本的核心逻辑:
- 目标侦察 :脚本访问OA系统的登录页或首页,爬取所有链接,并递归探索,绘制出网站的地图。
- 参数提取 :对每一个发现的URL,分析其GET参数和POST表单,找出所有可能的用户输入点,比如
id=123、keyword=搜索词这些。 - 任务生成 :将每个URL及其参数组合,格式化成Sqlmap能识别的命令。例如,对于一个搜索接口
http://oa.com/search.php?kw=test,我们会生成类似sqlmap -u “http://oa.com/search.php?kw=test” --batch --level=3 --risk=2的命令。 - 自动化扫描 :脚本依次或并发地执行这些Sqlmap命令。这里需要处理好Sqlmap的输出,捕获关键信息,如是否找到注入点、注入类型、数据库类型等。
- 结果汇总 :将所有扫描结果收集起来,生成一份清晰的报告,列出存在风险的URL、参数、漏洞类型和利用建议。
这个方案的优势在于,它将渗透测试工程师的经验(知道OA系统哪些功能点高危)和自动化工具的效能结合了起来。你只需要提供一个起始URL,剩下的脏活累活都交给脚本。
注意 :此方法仅用于授权的安全测试。未经授权对任何系统进行扫描是非法行为。请在拥有明确书面授权的前提下,在测试环境或自己的实验环境中进行。
3. 环境准备与工具部署:搭建你的自动化检测工作台
工欲善其事,必先利其器。在开始写代码之前,我们需要把环境搭建好。这个过程不复杂,但一些细节配置决定了后续脚本能否顺畅运行。
3.1 Python环境与依赖库安装
我推荐使用Python 3.7及以上版本。安装Python后,我们需要通过pip安装几个核心库。
# 安装HTTP请求库,用于爬取页面
pip install requests
# 安装HTML解析库,用于提取链接和表单
pip install beautifulsoup4
# 可选但推荐:安装lxml解析器,速度更快
pip install lxml
# 用于处理URL和解析
pip install urllib3
除了这些,可能还会用到 argparse (处理命令行参数)、 logging (记录日志)、 concurrent.futures (实现并发)等,这些都是Python的标准库,无需额外安装。
一个常见的坑是网络环境导致的pip安装慢或失败。建议配置国内的镜像源,例如清华源或阿里源。可以在用户目录下创建或修改 pip.conf 文件:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn
3.2 Sqlmap的安装与配置
Sqlmap是一个开源项目,直接从GitHub克隆是最佳方式,这样可以随时 git pull 更新到最新版。
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git
cd sqlmap
克隆后,你可以直接运行 python sqlmap.py 来测试是否安装成功。我习惯为sqlmap创建一个软链接或者将其路径加入系统环境变量,这样在任何目录下都能方便地调用。在Linux/Mac下可以这样做:
# 假设sqlmap克隆在 /opt/tools/ 目录下
sudo ln -s /opt/tools/sqlmap/sqlmap.py /usr/local/bin/sqlmap
在Windows下,可以将 sqlmap.py 所在的目录添加到系统的PATH环境变量中。
关于Sqlmap的配置 :Sqlmap本身有一个 sqlmap.conf 配置文件,但通常我们不需要修改它。我们的脚本会通过命令行参数来控制Sqlmap的行为。不过,了解几个关键参数对于后续编写脚本至关重要:
--batch: 非交互模式,所有提示都选择默认值,这是自动化必需的。--level和--risk: 控制测试的深度和风险等级。对于OA系统,我通常从--level=2 --risk=2开始,如果时间充裕再提高。--threads: 设置并发线程数,可以加快扫描速度,但要注意对目标服务器的压力。--output-dir: 指定结果输出目录,方便我们脚本统一收集报告。
3.3 项目目录结构规划
一个好的目录结构能让代码更清晰,后期维护也更方便。我建议这样组织你的项目文件夹:
oa_sqlmap_auto/
├── src/ # 源代码目录
│ ├── __init__.py
│ ├── crawler.py # 爬虫模块,负责爬取链接和表单
│ ├── sqlmap_runner.py # Sqlmap执行模块,负责生成命令并运行
│ ├── report_generator.py # 报告生成模块
│ └── main.py # 主程序入口
├── config/ # 配置文件目录
│ └── target_list.txt # 待扫描的目标URL列表
├── logs/ # 日志目录
│ └── scan_20231027.log
├── results/ # 扫描结果目录
│ ├── target_oa_com/ # 按目标划分的结果
│ │ ├── sqlmap_output/
│ │ └── summary.json
│ └── final_report.html
├── requirements.txt # Python依赖列表
└── README.md # 项目说明
在 main.py 中,我们会协调各个模块的工作。 crawler.py 专注于数据采集, sqlmap_runner.py 专注于调用外部工具, report_generator.py 专注于结果呈现。这种分而治之的思想让每个模块的职责单一,易于调试和扩展。
4. 核心模块一:智能爬虫设计与实现
爬虫模块是整个自动化流程的“眼睛”。它的任务不是漫无目的地爬取整个互联网,而是有针对性地、高效地发现目标OA系统(如泛微e-Bridge)中所有可能包含用户输入参数的URL。
4.1 爬虫策略制定:如何高效且友好地“探索”
对于OA系统,我们不能像对待普通资讯站那样暴力爬取。需要制定更智能的策略:
- 种子URL设置 :通常以系统的登录页面(如
http://oa.company.com/login.jsp)或门户首页作为起点。有些系统的接口可能藏在更深路径下,所以有时也需要把一些常见的功能入口页(如/notice/,/workflow/)也作为种子。 - 爬取深度控制 :设置一个合理的最大深度(比如3-5),防止爬虫陷入无穷的链接循环中。OA系统的页面层级通常不会太深。
- 域名限制 :只爬取与种子URL同域名的链接,避免爬虫跑到外站去。
- 去重机制 :使用集合(Set)来存储已访问和待访问的URL,避免重复爬取和请求。
- 速率限制 :在循环中增加
time.sleep(0.5)这样的短暂延迟,避免请求过快对目标服务器造成过大压力,这既是道德考量,也能防止被WAF或防火墙封禁IP。 - 识别并忽略静态资源 :通过文件扩展名(如
.jpg,.png,.css,.js)过滤掉图片、样式表、脚本等文件,专注于动态页面(.jsp,.php,.do,.action等)。
4.2 页面解析与参数提取:从HTML中挖出“输入框”
爬虫下载到页面后,最关键的一步是解析HTML,提取出所有可能的注入点。这里主要分两类:URL中的GET参数和页面中的POST表单。
提取GET参数 相对简单。当我们爬取到一个链接,比如 http://oa.com/viewDocument.jsp?docId=1001&type=pdf ,我们需要将其拆解。Python的 urllib.parse 库里的 urlparse 和 parse_qs 函数是干这个的能手。
from urllib.parse import urlparse, parse_qs
url = “http://oa.com/viewDocument.jsp?docId=1001&type=pdf”
parsed_url = urlparse(url)
query_params = parse_qs(parsed_url.query)
print(query_params) # 输出:{‘docId’: [‘1001’], ‘type’: [‘pdf’]}
这样,我们就知道这个URL有两个参数 docId 和 type 。
提取POST表单 则需要对HTML进行解析。这里 BeautifulSoup 大显身手。我们需要找到所有的 <form> 标签,然后提取其 action 属性(提交地址)和 method 属性(是否为POST)。接着,遍历表单内的所有 <input> 、 <select> 、 <textarea> 标签,获取它们的 name 属性,这就是参数名。
from bs4 import BeautifulSoup
import requests
html_content = requests.get(‘http://oa.com/login.jsp’).text
soup = BeautifulSoup(html_content, ‘lxml’)
forms = soup.find_all(‘form’, method=’post’) # 找POST方法的表单
for form in forms:
action_url = form.get(‘action’)
if not action_url.startswith(‘http’):
# 处理相对路径,拼接成完整URL
action_url = urllib.parse.urljoin(base_url, action_url)
inputs = form.find_all([‘input’, ‘textarea’, ‘select’])
param_names = [inp.get(‘name’) for inp in inputs if inp.get(‘name’)]
print(f”表单提交到:{action_url}, 参数有:{param_names}“)
一个实操心得 :很多OA系统(包括泛微)会使用大量的JavaScript动态生成内容或提交请求。简单的静态HTML解析可能抓不到这些“隐藏”的接口。一个进阶的方法是结合使用 selenium 这类浏览器自动化工具,让脚本能执行JS,等页面完全加载后再获取HTML源码。但这会大大增加复杂性和运行时间,需要根据实际情况权衡。对于初步扫描,静态分析通常能覆盖大部分传统功能点。
4.3 递归爬取与数据存储
爬虫需要以种子URL为起点,像波浪一样一层层扩散出去。我们可以使用一个队列(Queue)来实现广度优先搜索(BFS)。基本流程如下:
- 将种子URL放入待爬队列。
- 从队列中取出一个URL,下载页面,解析。
- 提取该页面的所有链接(
<a>标签的href属性)。 - 对这些链接进行过滤(同域名、非静态资源、未访问过),然后加入待爬队列。
- 提取该页面的GET参数和POST表单信息,存入一个列表或字典中,作为后续扫描的目标。
- 重复步骤2-5,直到队列为空或达到设定的最大深度/数量。
爬取到的目标数据(URL和参数)可以暂时保存在内存中的列表里,也可以直接写入一个文件(如 targets.json ),供下一个模块读取。我更喜欢用JSON格式保存,因为它结构清晰,易于读写。
import json
targets = [
{
“url”: “http://oa.com/search.do”,
“method”: “GET”,
“params”: [“keyword”, “page”]
},
{
“url”: “http://oa.com/login.jsp”,
“method”: “POST”,
“params”: [“username”, “password”, “verifyCode”]
}
]
with open(‘config/discovered_targets.json’, ‘w’) as f:
json.dump(targets, f, indent=4)
5. 核心模块二:Sqlmap任务调度与执行引擎
有了目标列表,接下来就是如何高效、可靠地调用Sqlmap去扫描每一个目标。这个模块是自动化流程的“双手”。
5.1 命令动态生成:为每个目标“量体裁衣”
我们不能对所有的目标都使用同一套Sqlmap参数。需要根据目标的特性动态生成最合适的命令。基础命令模板如下:
base_cmd = [
‘python’, ‘/path/to/sqlmap.py’, # 或直接 ‘sqlmap’ 如果已在PATH中
‘-u’, target_url,
‘–batch’,
‘–level’, ‘2’,
‘–risk’, ‘2’,
‘–threads’, ‘5’,
‘–output-dir’, output_dir
]
关键参数定制:
- HTTP方法 :如果目标是POST表单,我们需要添加
–data参数。例如,对于登录表单,命令会变成:
这里的一个技巧是,cmd = base_cmd + [‘–data’, ‘username=test&password=test&verifyCode=1234’]–data中的参数值可以先用一个无害的测试值(如test,1)填充。Sqlmap会在扫描过程中自动替换这些值为Payload。 - 指定参数 :如果一个URL有多个参数(如
?id=1&type=view),但根据经验只有id参数可能存在注入,我们可以用-p参数指定只扫描它:-p id。这能显著缩短扫描时间。 - Cookie与Session :很多OA系统需要登录后才能访问内部功能。我们的爬虫可能只能爬到登录页。因此,脚本需要支持传入一个有效的Cookie字符串。Sqlmap参数是
–cookie=”SESSIONID=abc123…”。我们可以先手动登录系统,从浏览器开发者工具中复制Cookie,作为脚本的输入。 - 结果输出 :
–output-dir非常重要。我们为每个目标或每次扫描任务指定一个独立的目录,这样Sqlmap生成的详细日志、报告和数据文件就不会混在一起。
5.2 进程调用与输出捕获:与Sqlmap“对话”
在Python中,我们使用 subprocess.Popen 来调用Sqlmap。不能只用 os.system ,因为我们需要实时捕获Sqlmap的输出,以了解扫描进度,并判断是否成功发现了注入漏洞。
import subprocess
import sys
def run_sqlmap(cmd_list, target_name):
“””执行sqlmap命令并捕获输出”””
print(f”[*] 开始扫描目标:{target_name}“)
print(f”[*] 执行命令:{‘ ‘.join(cmd_list)}“)
# 启动进程
process = subprocess.Popen(
cmd_list,
stdout=subprocess.PIPE, # 捕获标准输出
stderr=subprocess.PIPE, # 捕获错误输出
text=True, # 以文本模式处理
bufsize=1, # 行缓冲
universal_newlines=True
)
# 实时打印输出
while True:
output = process.stdout.readline()
if output == ‘’ and process.poll() is not None:
break
if output:
# 这里可以解析输出,例如检测到 ‘sqlmap identified the following injection point(s)’ 关键字
print(output.strip())
if “injection point(s)” in output:
# 发现注入点!可以记录到结果中
log_injection_found(target_name, output)
# 同样可以处理 stderr
err = process.stderr.readline()
if err:
print(f”[ERROR] {err.strip()}“)
return_code = process.poll()
return return_code
一个重要的注意事项 :Sqlmap在 –batch 模式下,当确认存在注入点时,会尝试进一步获取数据库名、表名等数据。这个过程可能很长。我们的脚本需要足够有耐心,不要轻易超时中断进程。可以考虑为每个目标设置一个全局超时(例如30分钟),但对于大型参数或复杂注入点的深入利用,时间可能更长,需要根据实际情况调整。
5.3 并发扫描控制:提升效率的双刃剑
为了加快扫描速度,我们很自然地会想到并发。Python的 concurrent.futures 库的 ThreadPoolExecutor 可以方便地实现多线程扫描。
from concurrent.futures import ThreadPoolExecutor, as_completed
def scan_all_targets(target_list, max_workers=3):
“””使用线程池并发扫描多个目标”””
results = {}
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交任务
future_to_target = {executor.submit(scan_single_target, target): target for target in target_list}
# 获取完成的任务结果
for future in as_completed(future_to_target):
target = future_to_target[future]
try:
result = future.result()
results[target[‘url’]] = result
print(f”[+] {target[‘url’]} 扫描完成,结果:{result}“)
except Exception as exc:
print(f”[-] {target[‘url’]} 扫描产生异常:{exc}“)
results[target[‘url’]] = ‘Error’
return results
但是,并发是一把双刃剑:
- 优点 :极大提升整体扫描速度,特别是当目标数量很多时。
- 缺点与风险 :
- 对目标服务器压力大 :多个Sqlmap进程同时发送大量畸形测试请求,很可能导致目标OA服务响应变慢甚至直接宕机,这在生产环境是灾难性的。 务必在测试环境或获得充分授权且业务低峰期进行!
- 自身资源消耗 :每个Sqlmap进程都会消耗相当的内存和CPU。并发数 (
max_workers) 需要根据你自己机器的性能来设定,通常不建议超过5。 - 结果干扰 :如果多个线程同时扫描同一个主机的不同端口或路径,可能会触发对方的WAF或IP封锁策略,导致后续扫描失败。
我的经验是 :对于内部测试,可以将并发数设为2-3。同时,在脚本中为每个扫描任务加入随机延迟( time.sleep(random.uniform(1, 5)) ),让请求看起来更“像”人工操作,降低被防御机制识别的风险。
6. 核心模块三:结果解析与报告生成
扫描完成后,我们会得到一堆Sqlmap输出的文本和文件。手动从海量信息里找漏洞报告是痛苦的最后一公里。自动化报告生成模块就是来解决这个问题的。
6.1 解析Sqlmap输出:从日志中提取“黄金”
Sqlmap扫描成功后,会在指定的 –output-dir 下生成一系列文件,其中对我们最有价值的是:
{target}.log: 详细的运行日志。{target}.csv: 以CSV格式存储的扫描结果摘要。{target}.json: 结构化的JSON格式结果(需要Sqlmap使用–json参数启动)。
最可靠的方式是解析JSON文件。如果生成命令中加入了 –json 参数,Sqlmap会输出一个结构化的JSON文件,里面清晰地列出了注入点、技术细节、数据库信息等。
import json
def parse_sqlmap_json(json_file_path):
with open(json_file_path, ‘r’, encoding=‘utf-8’) as f:
data = json.load(f)
results = []
# 结构可能因Sqlmap版本略有不同,需要适配
if ‘success’ in data and data[‘success’] and ‘data’ in data:
for item in data[‘data’]:
if ‘title’ in item and ‘payload’ in item:
vuln_info = {
‘type’: item.get(‘title’, ‘N/A’),
‘parameter’: item.get(‘parameter’, ‘N/A’),
‘payload’: item.get(‘payload’, ‘N/A’),
‘dbms’: item.get(‘dbms’, ‘N/A’) # 数据库类型
}
results.append(vuln_info)
return results
如果没有JSON文件,我们就需要去解析日志文件或标准输出。这比较麻烦,需要寻找关键的模式行,例如:
[INFO] testing ‘AND boolean-based blind – WHERE or HAVING clause’[CRITICAL] all tested parameters do not appear to be injectablesqlmap identified the following injection point(s)
通过正则表达式匹配这些行,可以提取出漏洞类型和受影响的参数。
6.2 生成可视化报告:让结果一目了然
将解析后的数据生成一份漂亮的报告,是交付成果的关键。我们可以生成HTML报告,方便在浏览器中查看和分享。
报告内容至少应包括:
- 扫描概览 :目标URL、扫描时间、总测试点数、发现漏洞数。
- 漏洞详情列表 :以表格形式展示每个漏洞的详细信息。 | 序号 | 漏洞URL | 风险参数 | 注入类型 | 数据库类型 | 风险等级 | | :— | :— | :— | :— | :— | :— | | 1 |
http://oa.com/view.jsp|id| Boolean-based blind | MySQL | 高危 | | 2 |http://oa.com/search.do|keyword| Error-based | Oracle | 中危 | - 复现建议 :针对每个漏洞,提供简单的复现步骤或Payload示例。
- 修复建议 :给出通用的修复方案,如使用参数化查询(Prepared Statement)、对输入进行严格的类型检查和过滤等。
我们可以使用Jinja2模板引擎来生成HTML,也可以直接用Python的字符串格式化拼装一个简单的HTML。这里给出一个简单的示例:
def generate_html_report(vuln_list, scan_info):
html_template = “”“
<!DOCTYPE html>
<html>
<head><title>SQL注入扫描报告</title><style>table {border-collapse: collapse; width: 100%;} th, td {border: 1px solid #ddd; padding: 8px; text-align: left;} th {background-color: #f2f2f2;}</style></head>
<body>
<h1>SQL注入自动化扫描报告</h1>
<p><strong>扫描目标:</strong>{target_url}</p>
<p><strong>扫描时间:</strong>{scan_time}</p>
<p><strong>发现漏洞总数:</strong>{vuln_count}</p>
<hr>
<h2>漏洞详情</h2>
<table>
<tr><th>序号</th><th>URL</th><th>参数</th><th>类型</th><th>数据库</th><th>风险</th></tr>
{vuln_rows}
</table>
</body>
</html>
”“”
vuln_rows = “”
for i, vuln in enumerate(vuln_list, 1):
risk = “高危” if “blind” in vuln[‘type’].lower() or “error” in vuln[‘type’].lower() else “中危”
vuln_rows += f“<tr><td>{i}</td><td>{vuln[‘url’]}</td><td>{vuln[‘param’]}</td><td>{vuln[‘type’]}</td><td>{vuln[‘dbms’]}</td><td>{risk}</td></tr>\n”
final_html = html_template.format(
target_url=scan_info[‘target’],
scan_time=scan_info[‘time’],
vuln_count=len(vuln_list),
vuln_rows=vuln_rows
)
with open(‘final_report.html’, ‘w’, encoding=‘utf-8’) as f:
f.write(final_html)
print(“[*] HTML报告已生成:final_report.html”)
除了HTML,生成一份纯文本的摘要或Markdown格式的报告,方便集成到其他工作流(如发邮件、提交到JIRA等)中,也是很好的实践。
7. 实战演练:针对泛微e-Bridge系统的扫描案例
理论讲得再多,不如一次实战。我们假设目标是一个测试环境下的泛微e-Bridge系统(版本号不重要,思路通用)。记住, 所有操作必须在授权范围内进行 。
7.1 目标分析与特征识别
泛微e-Bridge通常采用J2EE架构,页面后缀多为 .jsp 或 .do 。常见的功能模块和可能存在注入的点包括:
- 门户与新闻 :
/notice/noticeView.jsp?id=xxx - 文档中心 :
/doc/documentView.do?docId=xxx - 工作流程 :
/workflow/requestView.jsp?requestId=xxx, 以及流程查询页面。 - 通讯录与人员查询 :
/hr/employeeSearch.do?keyWord=xxx - 内部邮件 :
/mail/mailList.do?folderId=xxx
我们的爬虫种子URL可以设置为系统的登录页,比如 http://test-oa.weaver.com.cn/login.jsp 。在爬取时,我们可以让爬虫优先关注带有 id , key , type , page 等常见参数名的链接。
7.2 配置脚本并启动扫描
假设我们已经写好了脚本 main.py ,它接受一个目标URL作为参数。
- 准备Cookie(如果需要) :如果目标系统需要登录,我们先手动登录,从浏览器F12开发者工具的“网络”(Network)标签页,复制任意一个请求头中的
Cookie值。 - 创建配置文件 :在
config/目录下创建一个targets.txt,里面写上目标URL,一行一个。或者更简单,直接通过命令行参数传入。 - 运行爬虫 :
脚本会开始爬取,并将发现的潜在注入点保存到python src/main.py –mode crawl –url http://test-oa.weaver.com.cn/login.jsp –cookie “JSESSIONID=ABCDEFG123456…” –depth 3discovered_targets.json。 - 启动自动化扫描 :
这里我设置了2个并发线程,开始对之前发现的所有目标进行扫描。屏幕上会滚动显示各个Sqlmap进程的输出。python src/main.py –mode scan –target-file config/discovered_targets.json –output-dir results/scan_01 –threads 2
7.3 扫描过程监控与结果分析
在扫描过程中,我们需要关注终端输出:
- 看到
[INFO] testing ‘XXX’表示正在测试某种注入技术。 - 看到
[CRITICAL] all tested parameters do not appear to be injectable表示当前目标点安全。 - 最激动人心的 是看到
sqlmap identified the following injection point(s),后面会紧跟详细的注入类型、后台数据库等信息。
扫描全部结束后,进入 results/scan_01 目录,每个子目录对应一个扫描目标,里面会有Sqlmap生成的所有文件。同时,脚本根目录下会生成 final_report.html 。
打开报告,我们可能会发现类似这样的结果:
- 漏洞1 :
http://test-oa.weaver.com.cn/notice/noticeView.jsp?id=123,参数id存在基于布尔的盲注,数据库为MySQL。 - 漏洞2 :
http://test-oa.weaver.com.cn/hr/employeeSearch.do,POST参数keyWord存在基于错误的注入,数据库为Oracle。
报告会给出风险等级和复现用的Payload。例如,对于漏洞1,Payload可能是 id=123 AND 1=1 和 id=123 AND 1=2 返回页面不同。
8. 常见问题、优化思路与避坑指南
在实际使用这套自动化脚本的过程中,你肯定会遇到各种各样的问题。下面是我踩过的一些坑和对应的解决方案。
8.1 扫描失败或效率低下问题排查
-
Sqlmap卡住或进度缓慢 :
- 原因 :可能遇到了WAF(Web应用防火墙)的强力拦截,或者目标服务器响应极慢。
- 解决 :尝试降低扫描强度。在生成命令时,使用
–level=1 –risk=1。可以增加–delay=1参数,让每个请求间隔1秒,降低请求频率。如果明确知道有WAF(如云WAF),可以尝试Sqlmap的–tamper参数使用脚本绕过,例如–tamper=space2comment。
-
爬虫登录态无法保持 :
- 原因 :OA系统登录后可能有复杂的Session验证机制,或者有动态Token。
- 解决 :单纯使用Cookie可能不够。需要分析登录流程。有时需要先模拟登录,从登录成功的响应中获取Session。这可能需要用到
requests.Session()对象来保持会话,并将得到的cookies自动传递给后续的爬取和Sqlmap扫描命令。
-
误报与漏报 :
- 原因 :自动化工具不可避免的问题。OA系统某些页面可能返回统一的错误页面,导致Sqlmap误判为注入点。或者某些注入点需要特定的前置条件(如必须先提交某个表单)。
- 解决 : 自动化扫描的结果必须经过人工复核! 对于报告中的每个“漏洞”,手动用Burp Suite或浏览器插件(如HackBar)去验证一下。对于漏报,可能需要调整爬虫策略,发现更多隐藏接口,或者为Sqlmap提供更精确的测试参数。
8.2 脚本性能与稳定性优化
-
超时与重试机制 :网络是不稳定的。在
run_sqlmap函数中,应该为subprocess.Popen设置超时。如果超时,可以记录该目标扫描失败,并可以选择重试一次。try: output, errors = process.communicate(timeout=1800) # 30分钟超时 except subprocess.TimeoutExpired: process.kill() print(f”[-] 扫描 {target_name} 超时,已终止”) return ‘Timeout’ -
状态持久化 :如果扫描的目标非常多,脚本运行可能中断(比如断电、网络故障)。我们应该实现断点续扫功能。可以在扫描每个目标前,在一个状态文件(如
status.json)中标记“进行中”,完成后标记“完成”或“失败”。重启脚本时,先读取状态文件,跳过已完成的目标。 -
资源清理 :Sqlmap在扫描过程中可能会产生临时文件。虽然我们指定了
–output-dir,但也要注意定期清理过期的结果目录,防止磁盘被占满。
8.3 高级功能扩展方向
当基本功能跑通后,可以考虑以下方向让脚本更强大:
- 与其他工具联动 :将本脚本作为更大自动化流程的一环。例如,先用Nmap或Masscan做端口扫描,发现Web服务;然后用本脚本做SQL注入专项扫描;最后将结果自动导入到漏洞管理平台(如OpenVAS, DefectDojo)。
- 指纹识别 :在爬虫阶段,不仅爬链接,还尝试识别目标OA系统的具体类型和版本(如泛微e-Bridge 9.0)。这可以通过检查特定文件、特定HTTP响应头、HTML中的关键字来实现。识别出系统后,可以调用针对该版本已知漏洞的专用测试模块(如果有的话)。
- 参数智能过滤 :不是所有参数都值得测试。比如,
page=1这种分页参数,注入概率极低;__VIEWSTATE这种ASP.NET的视图状态参数,测试它毫无意义。可以在脚本中加入一个“黑名单”或“启发式规则”,在生成任务前就过滤掉这些低价值目标,进一步提升效率。 - 图形化界面(GUI) :使用PyQt或Tkinter为脚本做一个简单的图形界面,方便不熟悉命令行的同事使用。可以设置目标URL、选择扫描模式、查看实时日志和最终报告。
这套Python+Sqlmap的自动化检测方案,其核心价值在于将安全工程师从重复劳动中解放出来,让他们能更专注于漏洞的深度利用、成因分析和修复方案设计。它不是一个可以完全替代人工的“银弹”,而是一个强大的“辅助轮”。真正专业的安全测试,永远离不开测试者的经验、判断和创造性思维。希望这个分享能给你带来启发,在实际工作中打造出更适合自己场景的自动化安全工具。
更多推荐
所有评论(0)