1. 项目概述:一次针对特定资产的高效自动化探索

最近在和一些做安全研究的朋友交流时,大家普遍提到一个痛点:面对海量的互联网资产,尤其是像高校这类目标范围明确、资产类型集中的场景,如何从“大海捞针”转变为“精准撒网”,快速定位可能存在脆弱性的服务端点。传统的全端口扫描加手动验证,效率低下且噪音巨大。而基于已知漏洞(Nday)的自动化验证脚本,如果能与精准的资产发现相结合,就能形成一套高效的“侦查-打击”工作流。我这次分享的,就是基于 GitLab SpringBoot 这两个在高校信息化建设中极为常见的技术栈,构建的一套自动化探测与验证思路,并附上实践中积累的工具清单。

简单来说,这不是一个“万能攻击工具”,而是一套 针对特定技术特征(GitLab、SpringBoot)的自动化资产发现与漏洞验证方法 。它的核心价值在于,当你明确知道目标群体(如高校)大量使用了某类技术(如GitLab做代码管理,SpringBoot开发Web应用)时,你可以利用这些技术的公开特征、默认配置和已知历史漏洞,编写脚本进行批量、快速的初步筛查。这能极大节省手工收集信息、测试验证的时间,将精力聚焦在真正的漏洞分析和深度利用上。整个过程需要在合法授权(如SRC漏洞众测)的范围内进行,所有操作都应遵循安全测试的基本原则。

2. 核心思路与方案设计:从特征识别到漏洞验证

这套方法的整体逻辑链条非常清晰,可以概括为: 资产收集 -> 特征过滤 -> 漏洞验证 。关键在于每一步都尽可能实现自动化,并确保前后环节的数据能无缝流转。

2.1 目标资产特征分析

为什么选择GitLab和SpringBoot作为切入点?因为在教育、科研机构及许多企业的开发环境中,它们的使用率非常高,且存在一些易于远程识别的特征。

  1. GitLab特征

    • 默认端口与路径 :虽然可以自定义,但很多部署会使用默认的80/443端口,以及 /users/sign_in (登录页)、 /explore (探索页面)、 /help (帮助页面)等路径。
    • HTTP响应特征 :页面HTML源码中通常包含 GitLab 字样、 csrf-token 的meta标签、特定的CSS/JS文件路径(如 /assets/webpack/ )。
    • API接口 :未授权或低权限下可能访问的API,如 /api/v4/version (返回版本信息),这是判断版本、关联漏洞的关键。
    • 错误信息 :特定的错误页面或提示信息。
  2. SpringBoot特征

    • 默认错误页面 :最典型的特征是 Whitelabel Error Page ,页面底部会有一行“This application has no explicit mapping for /error”。
    • 监控端点(Actuator) :Spring Boot Actuator模块提供了一系列监控和管理端点,如 /actuator /actuator/health /actuator/env /actuator/heapdump 等。如果配置不当(如未授权访问),这些端点会泄露大量敏感信息,包括配置、环境变量、甚至内存数据。
    • 特定Header :响应头中可能包含 X-Application-Context 等字段。
    • 常见路径 :如 /favicon.ico (特定的Spring Boot默认图标)、 /error 等。

基于这些特征,我们就可以编写或利用工具,从庞大的IP地址或域名列表中,快速筛选出运行着GitLab或SpringBoot服务的资产。

2.2 自动化工作流设计

整个自动化流程可以借助一个简单的脚本来串联,其核心步骤如下:

  1. 输入 :准备一个目标IP或域名列表。对于高校SRC,可以通过子域名爆破、证书透明度日志、搜索引擎语法(如 site:.edu.cn )等方式获取初始列表。
  2. HTTP探测与特征匹配 :使用并发HTTP请求库(如Python的 aiohttp httpx )对列表中的目标进行快速访问。针对每个目标:
    • 访问根路径 / 以及GitLab、SpringBoot的特定特征路径(如 /users/sign_in /actuator )。
    • 分析响应状态码、响应头、HTML正文内容。
    • 通过预定义的正则表达式或关键字(如“GitLab”、“Whitelabel Error Page”、“X-Application-Context”)进行匹配。
  3. 结果分类与存储 :将匹配成功的目标按照服务类型(GitLab、SpringBoot)分类,并记录其URL、识别到的特征、版本信息(如果可能)等,保存到文件或数据库中。
  4. 漏洞验证(Nday脚本执行) :这是“扫荡”的关键。针对分类后的目标列表,调用相应的Nday验证脚本。
    • 对于GitLab :根据从 /api/v4/version 或其他途径获取的版本号,匹配已知的公开漏洞(CVE)。例如,针对特定版本的远程代码执行(RCE)、信息泄露漏洞,编写或使用现有的PoC(概念验证)脚本进行批量测试。
    • 对于SpringBoot :重点测试Actuator端点的未授权访问。如果发现 /actuator/env 可访问,则检查其中是否泄露了数据库密码、API密钥等。对于已知的Spring Framework或Spring Boot的RCE漏洞(如Spring4Shell、Spring Cloud Function SpEL注入),使用对应的验证脚本进行测试。
  5. 报告生成 :将漏洞验证的结果(成功/失败、目标URL、漏洞类型、可能的风险等级)进行整理,生成结构化的报告(如JSON、CSV或HTML格式),便于后续提交和审计。

注意 :漏洞验证脚本(Nday PoC)的使用必须格外谨慎。务必在获得明确授权的目标上测试,并且最好在隔离的测试环境中先验证脚本的准确性和安全性,避免对目标系统造成意外破坏或触发告警。

3. 关键工具链与脚本实现细节

工欲善其事,必先利其器。下面我详细拆解每个环节可以用到的工具和自编写脚本的关键部分。

3.1 资产发现与特征识别工具

这个阶段的目标是“广撒网,快识别”。

  1. 子域名枚举 :获取目标高校(如 xxx.edu.cn )的域名资产。

    • subfinder / assetfinder / amass :被动的子域名收集工具,利用各类公开数据源。
    • ksubdomain :一款高效的子域名爆破工具,速度极快。
    • 实操心得 :通常我会组合使用。先用 subfinder 进行被动收集,再用 ksubdomain 对常见字典进行爆破。将结果去重合并,得到一个初步的域名列表。
  2. HTTP探测与特征扫描

    • httpx :本项目中的核心工具之一。它不仅能快速探测HTTP服务,还内置了强大的 内容匹配(Matcher) 条件过滤(Filter) 功能。
    # 示例:使用httpx批量探测,并匹配GitLab和SpringBoot特征
    cat domains.txt | httpx -silent -title -status-code -tech-detect -match-string "GitLab" -o gitlab_targets.txt
    cat domains.txt | httpx -silent -title -status-code -match-string "Whitelabel Error Page" -o springboot_targets.txt
    
    • nuclei :虽然主打漏洞扫描,但其模板中的 http 协议部分可以用于精准识别技术栈。社区有大量识别GitLab、SpringBoot及其特定版本的模板。
    # 使用nuclei的technologies检测模板进行识别
    nuclei -l domains.txt -t technologies/ -o tech_results.txt
    
    • 自编写Python脚本 :当需要更定制化的匹配逻辑时,自编脚本更灵活。下面是一个简化的示例框架:
    import aiohttp
    import asyncio
    from urllib.parse import urljoin
    import re
    
    async def check_springboot(session, url):
        try:
            async with session.get(urljoin(url, '/actuator'), timeout=5, ssl=False) as resp:
                text = await resp.text()
                if resp.status == 200 and 'actuator' in text:
                    return ('SpringBoot Actuator', url)
                # 检查错误页面
            async with session.get(urljoin(url, '/nonexistpath'), timeout=5, ssl=False) as resp:
                if resp.status == 404 and 'Whitelabel Error Page' in await resp.text():
                    return ('SpringBoot Default Error', url)
        except:
            pass
        return None
    
    async def check_gitlab(session, url):
        try:
            async with session.get(urljoin(url, '/api/v4/version'), timeout=5, ssl=False) as resp:
                if resp.status == 200:
                    data = await resp.json()
                    return (f'GitLab {data.get("version")}', url)
            async with session.get(urljoin(url, '/users/sign_in'), timeout=5, ssl=False) as resp:
                text = await resp.text()
                if 'GitLab' in text and 'sign_in' in text:
                    return ('GitLab Login Page', url)
        except:
            pass
        return None
    
    async def main(domains):
        async with aiohttp.ClientSession() as session:
            tasks = []
            for domain in domains:
                base_url = f"http://{domain}" # 也可尝试https
                tasks.append(check_springboot(session, base_url))
                tasks.append(check_gitlab(session, base_url))
            results = await asyncio.gather(*tasks)
            for result in results:
                if result:
                    print(f"[+] Found: {result[0]} - {result[1]}")
    
    # 读取目标列表并运行
    with open('domains.txt', 'r') as f:
        target_list = [line.strip() for line in f if line.strip()]
    asyncio.run(main(target_list))
    
    • 注意事项 :异步请求虽然快,但要注意控制并发量,避免对目标造成过大压力或被封禁IP。建议设置合理的延迟( asyncio.sleep )和使用连接池限制。

3.2 Nday漏洞验证脚本集成

识别出目标后,就需要“精确打击”。这里通常需要集成或编写具体的PoC脚本。

  1. GitLab Nday漏洞示例 :以某个历史RCE漏洞(CVE-2021-22205)为例。该漏洞存在于GitLab的ExifTool组件中,未授权或低权限用户可能通过上传特定图片实现命令执行。网络上存在公开的PoC脚本。

    • 集成思路 :将PoC脚本函数化,接受目标URL作为参数。在资产识别阶段,如果识别出GitLab且版本在受影响范围内,则自动调用该函数进行验证。
    • 关键点 :PoC脚本需要处理网络请求、Payload生成、结果判断。务必使用 try-except 包裹,确保单个目标失败不影响整体任务。验证成功后,不应执行真实有害命令,通常以执行 id whoami 或睡眠命令来证明漏洞存在。
  2. SpringBoot Nday漏洞示例 :以Actuator未授权访问导致的信息泄露为例。这严格来说不一定是Nday,而是错误配置,但同样具有高风险。

    • 验证脚本 :编写脚本批量访问 /actuator/env ,并解析返回的JSON,提取其中可能存在的敏感关键词(如 password secret key )。
    import json
    import re
    
    def check_actuator_env(url):
        # 假设url是识别出的SpringBoot应用根地址
        env_url = url.rstrip('/') + '/actuator/env'
        # 发送请求获取响应文本 resp_text
        # ...
        try:
            env_data = json.loads(resp_text)
            properties = env_data.get('propertySources', [])
            sensitive_info = []
            for prop in properties:
                for key, value in prop.get('properties', {}).items():
                    if any(kw in key.lower() for kw in ['pass', 'secret', 'key', 'token']):
                        sensitive_info.append(f"{key}: {value}")
            return sensitive_info
        except json.JSONDecodeError:
            return None
    
    • 对于RCE漏洞(如Spring4Shell) :需要集成更复杂的Payload生成和发送逻辑。通常使用公开的、经过验证的PoC脚本。 重要原则:仅作验证,不进行实际破坏。
  3. 工具化管理 :对于多个不同的Nday脚本,建议建立一个简单的调度框架。可以创建一个JSON或YAML配置文件,定义漏洞名称、影响组件、检测函数路径、目标类型(GitLab/SpringBoot)等。主调度程序根据资产识别结果,自动选择并调用对应的检测函数。

3.3 效率优化与资源管理

当目标量达到数千甚至上万时,效率和管理变得至关重要。

  1. 并发控制 :使用 asyncio aiohttp 实现高并发HTTP请求。但必须设置信号量( asyncio.Semaphore )来限制最大并发数,例如50-100,避免本地网络资源耗尽或触发目标速率限制。
  2. 断点续传 :将任务列表和已完成结果持久化(如保存到SQLite数据库或JSON文件)。每次运行前读取进度,跳过已处理的目标。这在大规模扫描中非常实用。
  3. 结果去重与聚合 :同一个IP可能对应多个域名(虚拟主机),同一个漏洞可能在多个路径上验证成功。需要对最终结果进行去重和聚合,按“IP:端口 - 漏洞类型”进行归并,使报告更清晰。
  4. 日志记录 :详细的日志对于排查问题至关重要。记录每个目标的处理状态(成功、失败、超时)、识别到的特征、漏洞验证的详细请求和响应(可脱敏)。建议使用 logging 模块,并输出到文件。

4. 实战流程与操作记录

假设我们现在要对某高校域( example.edu.cn )进行授权测试。以下是模拟的完整操作流程记录。

4.1 第一阶段:资产收集与初步识别

  1. 子域名收集

    subfinder -d example.edu.cn -silent | tee subfinder.txt
    assetfinder --subs-only example.edu.cn | tee assetfinder.txt
    # 合并去重
    cat subfinder.txt assetfinder.txt | sort -u > all_subs.txt
    

    最终得到 all_subs.txt ,假设有1500条子域名记录。

  2. HTTP服务探测与特征初筛

    # 使用httpx快速找出存活的HTTP/HTTPS服务,并初步筛选
    cat all_subs.txt | httpx -silent -ports 80,443,8080,8443 -title -status-code -tech-detect -o alive_hosts.txt
    # 从存活主机中筛选出疑似GitLab和SpringBoot的目标
    cat alive_hosts.txt | grep -i gitlab > potential_gitlab.txt
    cat alive_hosts.txt | grep -i spring > potential_springboot.txt
    # 使用更精确的nuclei模板复核
    nuclei -l alive_hosts.txt -t technologies/gitlab-detection.yaml -o confirmed_gitlab.txt
    nuclei -l alive_hosts.txt -t technologies/springboot-detection.yaml -o confirmed_springboot.txt
    

    经过这一步,我们得到了两个相对精准的目标列表: confirmed_gitlab.txt (约20个) 和 confirmed_springboot.txt (约50个)。

4.2 第二阶段:深度特征提取与版本识别

对于上一步得到的目标,进行更深入的交互,获取精确版本信息,为漏洞验证做准备。

  1. GitLab版本获取

    # 读取confirmed_gitlab.txt,逐行访问/api/v4/version
    import requests
    for url in open('confirmed_gitlab.txt'):
        url = url.strip()
        try:
            resp = requests.get(f"{url}/api/v4/version", timeout=5, verify=False)
            if resp.status_code == 200:
                version = resp.json().get('version')
                print(f"{url} - GitLab Version: {version}")
                # 将URL和版本号存入数据库或文件
        except Exception as e:
            print(f"{url} - Error: {e}")
    

    运行后,我们得到类似 https://gitlab.example.edu.cn - GitLab Version: 13.12.2 的结果。记录下每个目标的版本。

  2. SpringBoot Actuator端点探测

    # 定义常见的Actuator端点
    actuator_endpoints = ['/actuator', '/actuator/env', '/actuator/health', '/actuator/heapdump', '/actuator/metrics']
    for url in open('confirmed_springboot.txt'):
        url = url.strip()
        for endpoint in actuator_endpoints:
            full_url = url.rstrip('/') + endpoint
            try:
                resp = requests.get(full_url, timeout=5, verify=False)
                if resp.status_code == 200:
                    print(f"[+] Found accessible: {full_url}")
                    # 如果是/env端点,可以进一步解析存储
                    if 'env' in endpoint and 'application/json' in resp.headers.get('Content-Type', ''):
                        # 解析并存储敏感信息(略)
                        pass
            except:
                continue
    

    此步骤会输出所有可未授权访问的Actuator端点,这是后续漏洞利用和信息收集的关键入口。

4.3 第三阶段:Nday漏洞批量验证

根据前两步收集的精准信息,启动对应的漏洞验证模块。

  1. GitLab漏洞验证 :假设我们有一个针对 GitLab 13.12.2 的RCE PoC脚本 poc_gitlab_cve_2021_xxxx.py 。编写一个调度脚本:

    import subprocess
    import json
    
    # 读取带版本的GitLab目标列表
    targets = []
    with open('gitlab_with_version.json', 'r') as f: # 假设是JSON格式存储
        targets = json.load(f) # [{'url':'...', 'version':'13.12.2'}, ...]
    
    for target in targets:
        url = target['url']
        version = target['version']
        # 判断版本是否在受影响范围内 (这里需要根据具体CVE定义)
        if is_affected_version(version):
            print(f"[*] Testing {url} with CVE-2021-XXXX...")
            # 调用PoC脚本,传入目标URL
            # 通常PoC脚本会返回True/False或具体结果
            result = subprocess.run(['python3', 'poc_gitlab_cve_2021_xxxx.py', '-u', url],
                                     capture_output=True, text=True, timeout=30)
            if 'Vulnerable' in result.stdout or 'Command executed' in result.stdout:
                print(f"[!] VULNERABLE: {url}")
                log_vulnerability(url, 'CVE-2021-XXXX', result.stdout)
            else:
                print(f"[-] Not vulnerable: {url}")
    
  2. SpringBoot漏洞验证

    • 信息泄露 :直接使用第二阶段收集的 /actuator/env 数据进行分析即可。
    • RCE漏洞验证 :例如针对 Spring Cloud Function SpEL 漏洞。同样,准备一个PoC脚本 poc_spring_cloud_spel.py
    # 对confirmed_springboot.txt列表中的每个目标进行测试
    for url in open('confirmed_springboot.txt'):
        url = url.strip()
        print(f"[*] Testing {url} for Spring Cloud SpEL RCE...")
        result = subprocess.run(['python3', 'poc_spring_cloud_spel.py', '-u', url],
                                 capture_output=True, text=True, timeout=20)
        if 'Vulnerable' in result.stdout:
            print(f"[!] VULNERABLE: {url}")
            log_vulnerability(url, 'Spring Cloud SpEL RCE', result.stdout)
    

    重要提醒 :在实际授权测试中,PoC脚本中的命令执行部分应替换为无害的验证命令,如执行 whoami id ,或者仅仅通过DNSLOG、HTTP请求外带的方式来证明漏洞存在,避免对业务造成影响。

4.4 第四阶段:结果整理与报告输出

所有验证完成后,将日志中记录的漏洞结果进行整理。

  1. 数据清洗 :去重(同一漏洞在不同路径触发),合并同一目标的多个漏洞。
  2. 风险评级 :根据漏洞类型(RCE > 信息泄露 > 未授权访问)和影响的业务重要性进行初步评级。
  3. 报告生成 :生成一份简洁明了的CSV或Markdown报告。
    # 高校SRC自动化扫描报告 - example.edu.cn
    
    | 目标URL | 服务类型 | 漏洞类型 | CVE/编号 | 风险等级 | 简要描述 | 验证Payload/截图 |
    | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
    | https://gitlab.xx.example.edu.cn | GitLab 13.12.2 | 未授权RCE | CVE-2021-22205 | 高危 | 通过上传恶意图片可执行系统命令 | [截图链接] |
    | http://api.xy.example.edu.cn | SpringBoot 2.3.x | Actuator未授权访问 | 配置错误 | 中危 | /actuator/env 泄露数据库密码 | `spring.datasource.password=******` |
    | http://dev.zz.example.edu.cn | SpringBoot 2.6.x | Spring Cloud SpEL注入 | CVE-2022-22963 | 高危 | 可远程执行SpEL表达式 | 通过DNSLOG验证成功 |
    
    这份报告就是提交给SRC平台的有效凭证。

5. 常见问题、排查技巧与避坑指南

在实际操作中,你会遇到各种各样的问题。下面是我踩过的一些坑和总结的经验。

5.1 资产识别阶段

  • 问题1:扫描速度慢,超时多。

    • 排查 :网络延迟、目标防火墙策略、本地并发过高导致资源竞争。
    • 解决
      1. 调整超时时间:将默认的3-5秒调高至8-10秒,特别是对教育网目标。
      2. 限制并发数:将 httpx 或自编脚本的并发数( -threads -c )从几百降低到50-100。
      3. 使用优质代理池(如有):分散请求源IP。
    • 心得 :速度和质量需要权衡。在教育网环境下,盲目追求高并发会导致大量误报(超时被误判为不存在)。先用小规模测试找到合适的并发和超时参数。
  • 问题2:误报率高,把非目标识别成了GitLab/SpringBoot。

    • 排查 :匹配规则过于宽泛。例如,网页中恰好有“GitLab”这个词,但其实是技术博客文章。
    • 解决
      1. 多重特征校验 :不要只依赖一个关键词。例如,识别GitLab,可以组合判断:页面包含 GitLab 字样 + 存在 /assets/application-*.css 路径 + 页面标题为 Sign in · GitLab 。使用正则表达式匹配更精确的模式。
      2. 检查特定文件 :尝试访问 /favicon.ico ,计算其MD5,与已知的GitLab或SpringBoot默认图标MD5进行比对。这是一个非常准确的指纹。
      3. 使用专业工具复核 :将初步结果用 nuclei 的官方技术检测模板再跑一遍,进行二次过滤。

5.2 漏洞验证阶段

  • 问题3:PoC脚本在测试环境成功,但对真实目标失败。

    • 排查
      1. 网络环境 :目标是否存在WAF、IPS等防护设备?使用 curl -v 或脚本打印详细请求响应,查看是否被拦截(返回403、429等状态码,或包含 cloudflare waf 等字样)。
      2. 版本差异 :PoC可能针对特定小版本,目标系统可能已打补丁或版本略有不同。
      3. 路径差异 :SpringBoot的Actuator根路径可能被修改(如 /management ),GitLab的API路径可能被前置代理改写。
    • 解决
      1. WAF绕过 :尝试对Payload进行简单的编码(如URL编码、Unicode编码)、更改请求方法(GET变POST)、添加冗余Header、分块传输等常见绕过技巧。有些PoC工具内置了这些功能。
      2. 路径探测 :如果标准的 /actuator 访问不到,可以尝试用字典爆破常见的Actuator端点路径。
      3. 手工验证 :选取一个失败的目标,完全手工复现PoC的每一步,用Burp Suite拦截查看,定位差异点。
  • 问题4:验证脚本触发目标告警,IP被封锁。

    • 预防
      1. 降低频率 :在脚本的每个请求之间加入随机延迟(如 time.sleep(random.uniform(1, 3)) )。
      2. 分散扫描 :不要短时间内对一个目标或一个C段发起大量请求。
      3. 使用合法代理 :在获得授权的前提下,询问是否可以使用指定的测试IP,或了解其速率限制策略。
    • 应对 :一旦发现被封锁,立即停止对该目标的所有扫描。如果是临时封锁,等待一段时间(如30分钟)后再用极低的频率尝试。

5.3 工具与工程化

  • 问题5:工具链复杂,管理混乱。

    • 解决 :尝试使用成熟的自动化扫描框架,如 nuclei 配合自定义模板。你可以为GitLab和SpringBoot的各种漏洞编写详细的 nuclei 模板,它内置了并发控制、重试、结果输出等功能。这样只需要维护模板库,而不需要管理一堆独立的Python脚本。
    • 心得 :初期可以用脚本快速验证想法,但当流程固定后,将其转化为 nuclei 模板或 xray 插件等标准化形式,更利于协作和复用。
  • 问题6:结果数据量大,难以分析。

    • 解决 :一定要在项目开始就设计好结构化的数据存储。强烈推荐使用SQLite或轻量级数据库。设计简单的表结构,例如:
      CREATE TABLE targets (id INTEGER PRIMARY KEY, url TEXT, service_type TEXT, version TEXT, identified_at DATETIME);
      CREATE TABLE vulnerabilities (id INTEGER PRIMARY KEY, target_id INTEGER, vuln_type TEXT, cve_id TEXT, proof TEXT, found_at DATETIME);
      
      这样,你可以轻松地用SQL查询“所有存在GitLab RCE漏洞的目标”,或者“某个IP上发现的所有漏洞”。

最后再分享一个关键心态 :自动化脚本是为了提高效率,但它不能完全替代人工判断。特别是漏洞验证环节,一个成功的PoC返回,必须由人工进行最终确认,理解漏洞的上下文和实际影响。自动化发现的是“可能性”,而安全研究员的价值在于确认“真实性”并评估“危害性”。永远保持对工具的审慎态度,和对技术的深入理解,才是安全研究的立身之本。

更多推荐