基于GitLab与SpringBoot特征的自动化资产发现与漏洞验证实践
1. 项目概述:一次针对特定资产的高效自动化探索
最近在和一些做安全研究的朋友交流时,大家普遍提到一个痛点:面对海量的互联网资产,尤其是像高校这类目标范围明确、资产类型集中的场景,如何从“大海捞针”转变为“精准撒网”,快速定位可能存在脆弱性的服务端点。传统的全端口扫描加手动验证,效率低下且噪音巨大。而基于已知漏洞(Nday)的自动化验证脚本,如果能与精准的资产发现相结合,就能形成一套高效的“侦查-打击”工作流。我这次分享的,就是基于 GitLab 和 SpringBoot 这两个在高校信息化建设中极为常见的技术栈,构建的一套自动化探测与验证思路,并附上实践中积累的工具清单。
简单来说,这不是一个“万能攻击工具”,而是一套 针对特定技术特征(GitLab、SpringBoot)的自动化资产发现与漏洞验证方法 。它的核心价值在于,当你明确知道目标群体(如高校)大量使用了某类技术(如GitLab做代码管理,SpringBoot开发Web应用)时,你可以利用这些技术的公开特征、默认配置和已知历史漏洞,编写脚本进行批量、快速的初步筛查。这能极大节省手工收集信息、测试验证的时间,将精力聚焦在真正的漏洞分析和深度利用上。整个过程需要在合法授权(如SRC漏洞众测)的范围内进行,所有操作都应遵循安全测试的基本原则。
2. 核心思路与方案设计:从特征识别到漏洞验证
这套方法的整体逻辑链条非常清晰,可以概括为: 资产收集 -> 特征过滤 -> 漏洞验证 。关键在于每一步都尽可能实现自动化,并确保前后环节的数据能无缝流转。
2.1 目标资产特征分析
为什么选择GitLab和SpringBoot作为切入点?因为在教育、科研机构及许多企业的开发环境中,它们的使用率非常高,且存在一些易于远程识别的特征。
-
GitLab特征 :
- 默认端口与路径 :虽然可以自定义,但很多部署会使用默认的80/443端口,以及
/users/sign_in(登录页)、/explore(探索页面)、/help(帮助页面)等路径。 - HTTP响应特征 :页面HTML源码中通常包含
GitLab字样、csrf-token的meta标签、特定的CSS/JS文件路径(如/assets/webpack/)。 - API接口 :未授权或低权限下可能访问的API,如
/api/v4/version(返回版本信息),这是判断版本、关联漏洞的关键。 - 错误信息 :特定的错误页面或提示信息。
- 默认端口与路径 :虽然可以自定义,但很多部署会使用默认的80/443端口,以及
-
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 自动化工作流设计
整个自动化流程可以借助一个简单的脚本来串联,其核心步骤如下:
- 输入 :准备一个目标IP或域名列表。对于高校SRC,可以通过子域名爆破、证书透明度日志、搜索引擎语法(如
site:.edu.cn)等方式获取初始列表。 - HTTP探测与特征匹配 :使用并发HTTP请求库(如Python的
aiohttp、httpx)对列表中的目标进行快速访问。针对每个目标:- 访问根路径
/以及GitLab、SpringBoot的特定特征路径(如/users/sign_in,/actuator)。 - 分析响应状态码、响应头、HTML正文内容。
- 通过预定义的正则表达式或关键字(如“GitLab”、“Whitelabel Error Page”、“X-Application-Context”)进行匹配。
- 访问根路径
- 结果分类与存储 :将匹配成功的目标按照服务类型(GitLab、SpringBoot)分类,并记录其URL、识别到的特征、版本信息(如果可能)等,保存到文件或数据库中。
- 漏洞验证(Nday脚本执行) :这是“扫荡”的关键。针对分类后的目标列表,调用相应的Nday验证脚本。
- 对于GitLab :根据从
/api/v4/version或其他途径获取的版本号,匹配已知的公开漏洞(CVE)。例如,针对特定版本的远程代码执行(RCE)、信息泄露漏洞,编写或使用现有的PoC(概念验证)脚本进行批量测试。 - 对于SpringBoot :重点测试Actuator端点的未授权访问。如果发现
/actuator/env可访问,则检查其中是否泄露了数据库密码、API密钥等。对于已知的Spring Framework或Spring Boot的RCE漏洞(如Spring4Shell、Spring Cloud Function SpEL注入),使用对应的验证脚本进行测试。
- 对于GitLab :根据从
- 报告生成 :将漏洞验证的结果(成功/失败、目标URL、漏洞类型、可能的风险等级)进行整理,生成结构化的报告(如JSON、CSV或HTML格式),便于后续提交和审计。
注意 :漏洞验证脚本(Nday PoC)的使用必须格外谨慎。务必在获得明确授权的目标上测试,并且最好在隔离的测试环境中先验证脚本的准确性和安全性,避免对目标系统造成意外破坏或触发告警。
3. 关键工具链与脚本实现细节
工欲善其事,必先利其器。下面我详细拆解每个环节可以用到的工具和自编写脚本的关键部分。
3.1 资产发现与特征识别工具
这个阶段的目标是“广撒网,快识别”。
-
子域名枚举 :获取目标高校(如
xxx.edu.cn)的域名资产。subfinder/assetfinder/amass:被动的子域名收集工具,利用各类公开数据源。ksubdomain:一款高效的子域名爆破工具,速度极快。- 实操心得 :通常我会组合使用。先用
subfinder进行被动收集,再用ksubdomain对常见字典进行爆破。将结果去重合并,得到一个初步的域名列表。
-
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.txtnuclei:虽然主打漏洞扫描,但其模板中的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脚本。
-
GitLab Nday漏洞示例 :以某个历史RCE漏洞(CVE-2021-22205)为例。该漏洞存在于GitLab的ExifTool组件中,未授权或低权限用户可能通过上传特定图片实现命令执行。网络上存在公开的PoC脚本。
- 集成思路 :将PoC脚本函数化,接受目标URL作为参数。在资产识别阶段,如果识别出GitLab且版本在受影响范围内,则自动调用该函数进行验证。
- 关键点 :PoC脚本需要处理网络请求、Payload生成、结果判断。务必使用
try-except包裹,确保单个目标失败不影响整体任务。验证成功后,不应执行真实有害命令,通常以执行id、whoami或睡眠命令来证明漏洞存在。
-
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脚本。 重要原则:仅作验证,不进行实际破坏。
- 验证脚本 :编写脚本批量访问
-
工具化管理 :对于多个不同的Nday脚本,建议建立一个简单的调度框架。可以创建一个JSON或YAML配置文件,定义漏洞名称、影响组件、检测函数路径、目标类型(GitLab/SpringBoot)等。主调度程序根据资产识别结果,自动选择并调用对应的检测函数。
3.3 效率优化与资源管理
当目标量达到数千甚至上万时,效率和管理变得至关重要。
- 并发控制 :使用
asyncio、aiohttp实现高并发HTTP请求。但必须设置信号量(asyncio.Semaphore)来限制最大并发数,例如50-100,避免本地网络资源耗尽或触发目标速率限制。 - 断点续传 :将任务列表和已完成结果持久化(如保存到SQLite数据库或JSON文件)。每次运行前读取进度,跳过已处理的目标。这在大规模扫描中非常实用。
- 结果去重与聚合 :同一个IP可能对应多个域名(虚拟主机),同一个漏洞可能在多个路径上验证成功。需要对最终结果进行去重和聚合,按“IP:端口 - 漏洞类型”进行归并,使报告更清晰。
- 日志记录 :详细的日志对于排查问题至关重要。记录每个目标的处理状态(成功、失败、超时)、识别到的特征、漏洞验证的详细请求和响应(可脱敏)。建议使用
logging模块,并输出到文件。
4. 实战流程与操作记录
假设我们现在要对某高校域( example.edu.cn )进行授权测试。以下是模拟的完整操作流程记录。
4.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条子域名记录。 -
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 第二阶段:深度特征提取与版本识别
对于上一步得到的目标,进行更深入的交互,获取精确版本信息,为漏洞验证做准备。
-
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的结果。记录下每个目标的版本。 -
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漏洞批量验证
根据前两步收集的精准信息,启动对应的漏洞验证模块。
-
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}") -
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 第四阶段:结果整理与报告输出
所有验证完成后,将日志中记录的漏洞结果进行整理。
- 数据清洗 :去重(同一漏洞在不同路径触发),合并同一目标的多个漏洞。
- 风险评级 :根据漏洞类型(RCE > 信息泄露 > 未授权访问)和影响的业务重要性进行初步评级。
- 报告生成 :生成一份简洁明了的CSV或Markdown报告。
这份报告就是提交给SRC平台的有效凭证。# 高校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验证成功 |
5. 常见问题、排查技巧与避坑指南
在实际操作中,你会遇到各种各样的问题。下面是我踩过的一些坑和总结的经验。
5.1 资产识别阶段
-
问题1:扫描速度慢,超时多。
- 排查 :网络延迟、目标防火墙策略、本地并发过高导致资源竞争。
- 解决 :
- 调整超时时间:将默认的3-5秒调高至8-10秒,特别是对教育网目标。
- 限制并发数:将
httpx或自编脚本的并发数(-threads,-c)从几百降低到50-100。 - 使用优质代理池(如有):分散请求源IP。
- 心得 :速度和质量需要权衡。在教育网环境下,盲目追求高并发会导致大量误报(超时被误判为不存在)。先用小规模测试找到合适的并发和超时参数。
-
问题2:误报率高,把非目标识别成了GitLab/SpringBoot。
- 排查 :匹配规则过于宽泛。例如,网页中恰好有“GitLab”这个词,但其实是技术博客文章。
- 解决 :
- 多重特征校验 :不要只依赖一个关键词。例如,识别GitLab,可以组合判断:页面包含
GitLab字样 + 存在/assets/application-*.css路径 + 页面标题为Sign in · GitLab。使用正则表达式匹配更精确的模式。 - 检查特定文件 :尝试访问
/favicon.ico,计算其MD5,与已知的GitLab或SpringBoot默认图标MD5进行比对。这是一个非常准确的指纹。 - 使用专业工具复核 :将初步结果用
nuclei的官方技术检测模板再跑一遍,进行二次过滤。
- 多重特征校验 :不要只依赖一个关键词。例如,识别GitLab,可以组合判断:页面包含
5.2 漏洞验证阶段
-
问题3:PoC脚本在测试环境成功,但对真实目标失败。
- 排查 :
- 网络环境 :目标是否存在WAF、IPS等防护设备?使用
curl -v或脚本打印详细请求响应,查看是否被拦截(返回403、429等状态码,或包含cloudflare、waf等字样)。 - 版本差异 :PoC可能针对特定小版本,目标系统可能已打补丁或版本略有不同。
- 路径差异 :SpringBoot的Actuator根路径可能被修改(如
/management),GitLab的API路径可能被前置代理改写。
- 网络环境 :目标是否存在WAF、IPS等防护设备?使用
- 解决 :
- WAF绕过 :尝试对Payload进行简单的编码(如URL编码、Unicode编码)、更改请求方法(GET变POST)、添加冗余Header、分块传输等常见绕过技巧。有些PoC工具内置了这些功能。
- 路径探测 :如果标准的
/actuator访问不到,可以尝试用字典爆破常见的Actuator端点路径。 - 手工验证 :选取一个失败的目标,完全手工复现PoC的每一步,用Burp Suite拦截查看,定位差异点。
- 排查 :
-
问题4:验证脚本触发目标告警,IP被封锁。
- 预防 :
- 降低频率 :在脚本的每个请求之间加入随机延迟(如
time.sleep(random.uniform(1, 3)))。 - 分散扫描 :不要短时间内对一个目标或一个C段发起大量请求。
- 使用合法代理 :在获得授权的前提下,询问是否可以使用指定的测试IP,或了解其速率限制策略。
- 降低频率 :在脚本的每个请求之间加入随机延迟(如
- 应对 :一旦发现被封锁,立即停止对该目标的所有扫描。如果是临时封锁,等待一段时间(如30分钟)后再用极低的频率尝试。
- 预防 :
5.3 工具与工程化
-
问题5:工具链复杂,管理混乱。
- 解决 :尝试使用成熟的自动化扫描框架,如
nuclei配合自定义模板。你可以为GitLab和SpringBoot的各种漏洞编写详细的nuclei模板,它内置了并发控制、重试、结果输出等功能。这样只需要维护模板库,而不需要管理一堆独立的Python脚本。 - 心得 :初期可以用脚本快速验证想法,但当流程固定后,将其转化为
nuclei模板或xray插件等标准化形式,更利于协作和复用。
- 解决 :尝试使用成熟的自动化扫描框架,如
-
问题6:结果数据量大,难以分析。
- 解决 :一定要在项目开始就设计好结构化的数据存储。强烈推荐使用SQLite或轻量级数据库。设计简单的表结构,例如:
这样,你可以轻松地用SQL查询“所有存在GitLab RCE漏洞的目标”,或者“某个IP上发现的所有漏洞”。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);
- 解决 :一定要在项目开始就设计好结构化的数据存储。强烈推荐使用SQLite或轻量级数据库。设计简单的表结构,例如:
最后再分享一个关键心态 :自动化脚本是为了提高效率,但它不能完全替代人工判断。特别是漏洞验证环节,一个成功的PoC返回,必须由人工进行最终确认,理解漏洞的上下文和实际影响。自动化发现的是“可能性”,而安全研究员的价值在于确认“真实性”并评估“危害性”。永远保持对工具的审慎态度,和对技术的深入理解,才是安全研究的立身之本。
更多推荐
所有评论(0)