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来驱动? 这里有几个关键原因:

  1. 强大的库生态 :Python有 requests BeautifulSoup lxml 这样的库,可以非常方便地实现网页爬取、HTML解析和表单提取,这是我们自动化发现测试目标的基础。
  2. 灵活的进程控制 :Python的 subprocess 模块可以完美地调用并控制像Sqlmap这样的命令行工具,我们能捕获其输出、解析结果,并根据结果决定后续流程。
  3. 易于编写和调试 :Python语法简洁,能快速实现业务逻辑。我们可以很方便地添加日志、错误处理、并发控制等功能,让脚本更健壮。
  4. 广泛的社区支持 :遇到任何问题,几乎都能在社区找到解决方案或思路。

整体的工作流程 可以概括为以下几步,这也是我们脚本的核心逻辑:

  1. 目标侦察 :脚本访问OA系统的登录页或首页,爬取所有链接,并递归探索,绘制出网站的地图。
  2. 参数提取 :对每一个发现的URL,分析其GET参数和POST表单,找出所有可能的用户输入点,比如 id=123 keyword=搜索词 这些。
  3. 任务生成 :将每个URL及其参数组合,格式化成Sqlmap能识别的命令。例如,对于一个搜索接口 http://oa.com/search.php?kw=test ,我们会生成类似 sqlmap -u “http://oa.com/search.php?kw=test” --batch --level=3 --risk=2 的命令。
  4. 自动化扫描 :脚本依次或并发地执行这些Sqlmap命令。这里需要处理好Sqlmap的输出,捕获关键信息,如是否找到注入点、注入类型、数据库类型等。
  5. 结果汇总 :将所有扫描结果收集起来,生成一份清晰的报告,列出存在风险的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系统,我们不能像对待普通资讯站那样暴力爬取。需要制定更智能的策略:

  1. 种子URL设置 :通常以系统的登录页面(如 http://oa.company.com/login.jsp )或门户首页作为起点。有些系统的接口可能藏在更深路径下,所以有时也需要把一些常见的功能入口页(如 /notice/ , /workflow/ )也作为种子。
  2. 爬取深度控制 :设置一个合理的最大深度(比如3-5),防止爬虫陷入无穷的链接循环中。OA系统的页面层级通常不会太深。
  3. 域名限制 :只爬取与种子URL同域名的链接,避免爬虫跑到外站去。
  4. 去重机制 :使用集合(Set)来存储已访问和待访问的URL,避免重复爬取和请求。
  5. 速率限制 :在循环中增加 time.sleep(0.5) 这样的短暂延迟,避免请求过快对目标服务器造成过大压力,这既是道德考量,也能防止被WAF或防火墙封禁IP。
  6. 识别并忽略静态资源 :通过文件扩展名(如 .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)。基本流程如下:

  1. 将种子URL放入待爬队列。
  2. 从队列中取出一个URL,下载页面,解析。
  3. 提取该页面的所有链接( <a> 标签的 href 属性)。
  4. 对这些链接进行过滤(同域名、非静态资源、未访问过),然后加入待爬队列。
  5. 提取该页面的GET参数和POST表单信息,存入一个列表或字典中,作为后续扫描的目标。
  6. 重复步骤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

但是,并发是一把双刃剑:

  • 优点 :极大提升整体扫描速度,特别是当目标数量很多时。
  • 缺点与风险
    1. 对目标服务器压力大 :多个Sqlmap进程同时发送大量畸形测试请求,很可能导致目标OA服务响应变慢甚至直接宕机,这在生产环境是灾难性的。 务必在测试环境或获得充分授权且业务低峰期进行!
    2. 自身资源消耗 :每个Sqlmap进程都会消耗相当的内存和CPU。并发数 ( max_workers ) 需要根据你自己机器的性能来设定,通常不建议超过5。
    3. 结果干扰 :如果多个线程同时扫描同一个主机的不同端口或路径,可能会触发对方的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 injectable
  • sqlmap identified the following injection point(s)

通过正则表达式匹配这些行,可以提取出漏洞类型和受影响的参数。

6.2 生成可视化报告:让结果一目了然

将解析后的数据生成一份漂亮的报告,是交付成果的关键。我们可以生成HTML报告,方便在浏览器中查看和分享。

报告内容至少应包括:

  1. 扫描概览 :目标URL、扫描时间、总测试点数、发现漏洞数。
  2. 漏洞详情列表 :以表格形式展示每个漏洞的详细信息。 | 序号 | 漏洞URL | 风险参数 | 注入类型 | 数据库类型 | 风险等级 | | :— | :— | :— | :— | :— | :— | | 1 | http://oa.com/view.jsp | id | Boolean-based blind | MySQL | 高危 | | 2 | http://oa.com/search.do | keyword | Error-based | Oracle | 中危 |
  3. 复现建议 :针对每个漏洞,提供简单的复现步骤或Payload示例。
  4. 修复建议 :给出通用的修复方案,如使用参数化查询(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作为参数。

  1. 准备Cookie(如果需要) :如果目标系统需要登录,我们先手动登录,从浏览器F12开发者工具的“网络”(Network)标签页,复制任意一个请求头中的 Cookie 值。
  2. 创建配置文件 :在 config/ 目录下创建一个 targets.txt ,里面写上目标URL,一行一个。或者更简单,直接通过命令行参数传入。
  3. 运行爬虫
    python src/main.py –mode crawl –url http://test-oa.weaver.com.cn/login.jsp –cookie “JSESSIONID=ABCDEFG123456…” –depth 3
    
    脚本会开始爬取,并将发现的潜在注入点保存到 discovered_targets.json
  4. 启动自动化扫描
    python src/main.py –mode scan –target-file config/discovered_targets.json –output-dir results/scan_01 –threads 2
    
    这里我设置了2个并发线程,开始对之前发现的所有目标进行扫描。屏幕上会滚动显示各个Sqlmap进程的输出。

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 扫描失败或效率低下问题排查

  1. Sqlmap卡住或进度缓慢

    • 原因 :可能遇到了WAF(Web应用防火墙)的强力拦截,或者目标服务器响应极慢。
    • 解决 :尝试降低扫描强度。在生成命令时,使用 –level=1 –risk=1 。可以增加 –delay=1 参数,让每个请求间隔1秒,降低请求频率。如果明确知道有WAF(如云WAF),可以尝试Sqlmap的 –tamper 参数使用脚本绕过,例如 –tamper=space2comment
  2. 爬虫登录态无法保持

    • 原因 :OA系统登录后可能有复杂的Session验证机制,或者有动态Token。
    • 解决 :单纯使用Cookie可能不够。需要分析登录流程。有时需要先模拟登录,从登录成功的响应中获取Session。这可能需要用到 requests.Session() 对象来保持会话,并将得到的cookies自动传递给后续的爬取和Sqlmap扫描命令。
  3. 误报与漏报

    • 原因 :自动化工具不可避免的问题。OA系统某些页面可能返回统一的错误页面,导致Sqlmap误判为注入点。或者某些注入点需要特定的前置条件(如必须先提交某个表单)。
    • 解决 自动化扫描的结果必须经过人工复核! 对于报告中的每个“漏洞”,手动用Burp Suite或浏览器插件(如HackBar)去验证一下。对于漏报,可能需要调整爬虫策略,发现更多隐藏接口,或者为Sqlmap提供更精确的测试参数。

8.2 脚本性能与稳定性优化

  1. 超时与重试机制 :网络是不稳定的。在 run_sqlmap 函数中,应该为 subprocess.Popen 设置超时。如果超时,可以记录该目标扫描失败,并可以选择重试一次。

    try:
        output, errors = process.communicate(timeout=1800) # 30分钟超时
    except subprocess.TimeoutExpired:
        process.kill()
        print(f”[-] 扫描 {target_name} 超时,已终止”)
        return ‘Timeout’
    
  2. 状态持久化 :如果扫描的目标非常多,脚本运行可能中断(比如断电、网络故障)。我们应该实现断点续扫功能。可以在扫描每个目标前,在一个状态文件(如 status.json )中标记“进行中”,完成后标记“完成”或“失败”。重启脚本时,先读取状态文件,跳过已完成的目标。

  3. 资源清理 :Sqlmap在扫描过程中可能会产生临时文件。虽然我们指定了 –output-dir ,但也要注意定期清理过期的结果目录,防止磁盘被占满。

8.3 高级功能扩展方向

当基本功能跑通后,可以考虑以下方向让脚本更强大:

  1. 与其他工具联动 :将本脚本作为更大自动化流程的一环。例如,先用Nmap或Masscan做端口扫描,发现Web服务;然后用本脚本做SQL注入专项扫描;最后将结果自动导入到漏洞管理平台(如OpenVAS, DefectDojo)。
  2. 指纹识别 :在爬虫阶段,不仅爬链接,还尝试识别目标OA系统的具体类型和版本(如泛微e-Bridge 9.0)。这可以通过检查特定文件、特定HTTP响应头、HTML中的关键字来实现。识别出系统后,可以调用针对该版本已知漏洞的专用测试模块(如果有的话)。
  3. 参数智能过滤 :不是所有参数都值得测试。比如, page=1 这种分页参数,注入概率极低; __VIEWSTATE 这种ASP.NET的视图状态参数,测试它毫无意义。可以在脚本中加入一个“黑名单”或“启发式规则”,在生成任务前就过滤掉这些低价值目标,进一步提升效率。
  4. 图形化界面(GUI) :使用PyQt或Tkinter为脚本做一个简单的图形界面,方便不熟悉命令行的同事使用。可以设置目标URL、选择扫描模式、查看实时日志和最终报告。

这套Python+Sqlmap的自动化检测方案,其核心价值在于将安全工程师从重复劳动中解放出来,让他们能更专注于漏洞的深度利用、成因分析和修复方案设计。它不是一个可以完全替代人工的“银弹”,而是一个强大的“辅助轮”。真正专业的安全测试,永远离不开测试者的经验、判断和创造性思维。希望这个分享能给你带来启发,在实际工作中打造出更适合自己场景的自动化安全工具。

更多推荐