JumpServer堡垒机安全检测工具BlackJump设计与实战 1. 项目概述与核心价值最近在内部安全评估和红队演练中JumpServer堡垒机是一个高频出现的目标。作为一款广泛使用的开源堡垒机它承载着企业核心资产访问的跳板和控制功能一旦失守后果不堪设想。我手头正好有一个在实战中打磨出来的工具暂且叫它“BlackJump”。这并非一个公开的漏洞利用框架而是我个人为了提升效率基于对JumpServer多个历史安全问题的深入研究整合而成的一个综合验证与利用脚本集。它的核心价值在于能够自动化、批量化地检测JumpServer资产是否存在常见的未授权访问、信息泄露、命令执行等漏洞并将复杂的利用链简化为几条命令极大提升了安全测试的效率和深度。对于安全研究人员、企业内部的蓝队和红队成员而言这样一个工具能帮助快速定位风险验证修复方案是否生效。需要明确的是开发和使用此类工具必须严格遵守法律法规仅用于授权范围内的安全测试、漏洞验证和自身系统的防护建设。任何未经授权的测试行为都是非法的。本文的目的在于技术交流通过剖析工具背后的原理和实现思路帮助安全从业者更深入地理解JumpServer的安全机制和常见弱点从而更好地进行防御。2. JumpServer安全架构与常见攻击面分析要理解一个漏洞利用工具必须先理解其目标。JumpServer的安全架构可以粗略分为几个层面Web前端基于Django、API接口、核心组件如Koko、Guacamole用于连接资产、以及底层依赖如Redis、MySQL/H2 Database、Celery等。攻击面也相应地分布在这些层面。2.1 未授权访问漏洞的根源未授权访问Unauthorized Access是JumpServer历史上屡次出现的高危问题。其根源往往在于API接口鉴权缺失或绕过某些API端点本应校验用户会话或Token但因配置错误、代码逻辑缺陷或默认安装问题允许未认证用户直接访问。例如早期的某些版本中获取用户列表、资产列表的API可能存在此问题。默认弱口令与默认安装JumpServer的默认管理员账号密码admin/admin如果未被修改是最直接的未授权入口。此外一些快速部署脚本如docker-compose可能使用默认的、强度不高的密码或密钥。依赖组件暴露JumpServer依赖的中间件服务如果配置不当可能直接暴露在公网。最典型的例子就是H2 Database控制台未授权访问。在JumpServer的某些部署方式中H2数据库的控制台可能被开启且未设置访问密码攻击者可以直接通过Web接口连接到这个嵌入式数据库执行SQL命令从而窃取或篡改所有核心数据包括用户凭证、资产信息、会话记录等。这与Nacos、Druid等组件的未授权访问漏洞原理类似都是管理界面缺乏认证。信息泄露接口一些用于监控或调试的接口如/metrics,/actuator,/api/v1/health/等可能泄露敏感信息如内部IP、组件状态、甚至部分配置为后续攻击提供情报。2.2 从信息泄露到命令执行单纯的未授权信息泄露危害有限但JumpServer的架构特性使得信息泄露极易升级为严重的命令执行或权限提升。例如通过未授权API获取到有效的Session ID或Token即可模拟合法用户身份调用更高权限的API。通过H2 Database未授权访问可以直接读取django_session表或core_user表获取管理员会话或密码哈希虽然破解有难度但如果有盐值泄露或弱哈希风险很高。更直接的是可以通过SQL语句修改用户密码或权限。JumpServer的“命令过滤器”、“命令存储”等功能涉及对用户输入的处理如果存在注入漏洞如SQL注入、命令注入结合未授权访问点可能实现远程代码执行。2.3 与其它组件漏洞的关联搜索热词中提到了Nacos、Druid、Swagger、Spring Cloud Gateway等组件的未授权漏洞。这提醒我们在针对JumpServer的测试中视野不能局限于JumpServer本身。一个完整的企业运维体系可能集成了配置中心Nacos、监控Metrics、API文档Swagger等多个组件。这些组件的未授权漏洞同样可以作为进入JumpServer内网的跳板或者直接获取到JumpServer的配置信息如数据库连接串、Redis密码、API密钥。因此一个综合性的工具需要具备对关联组件的探测能力。3. BlackJump工具设计与核心模块解析基于以上攻击面分析BlackJump被设计为一个模块化、插件化的Python工具。它不采用重量级的框架而是通过清晰的函数和类来组织功能便于理解和二次开发。核心结构如下BlackJump/ ├── core/ │ ├── requester.py # 封装HTTP请求处理会话、代理、重试等 │ └── utils.py # 通用工具函数日志、解析、编码等 ├── modules/ │ ├── auth_bypass.py # 各类未授权访问检测模块 │ ├── info_disclosure.py # 信息泄露探测模块 │ ├── h2_console.py # H2 Database控制台利用模块 │ ├── weak_password.py # 默认口令与弱口令检测 │ └── rce_check.py # 潜在命令执行漏洞检测谨慎使用 ├── exploits/ # 完整的利用链如H2未授权-改密-登录 └── cli.py # 命令行交互界面3.1 请求引擎封装一个稳健的请求引擎是基础。requester.py需要处理SSL证书验证通常在内网测试中需要忽略、超时设置、请求头伪装模拟浏览器、Cookie和Session的自动管理。特别是对于JumpServer需要注意其CSRF Token的处理。很多需要POST请求的漏洞利用需要先GET一个页面来获取CSRF Token。# core/requester.py 简化示例 import requests from requests.adapters import HTTPAdapter from urllib3 import disable_warnings from urllib3.exceptions import InsecureRequestWarning disable_warnings(InsecureRequestWarning) class RequestEngine: def __init__(self, base_url, proxyNone, timeout15): self.base_url base_url.rstrip(/) self.session requests.Session() self.session.verify False # 忽略SSL警告仅用于测试环境 self.timeout timeout self.session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: application/json, text/html, */*, Accept-Language: zh-CN,zh;q0.9, }) if proxy: self.session.proxies {http: proxy, https: proxy} # 适配器增加重试 adapter HTTPAdapter(max_retries3) self.session.mount(http://, adapter) self.session.mount(https://, adapter) self.csrf_token None def get_csrf_from_html(self, html_text): 从HTML页面中提取CSRF Token常见于Django模板 # 简化实现实际需用正则或解析库更健壮 import re match re.search(rnamecsrfmiddlewaretoken value([^]), html_text) if match: return match.group(1) return None def get(self, path, **kwargs): url f{self.base_url}{path} resp self.session.get(url, timeoutself.timeout, **kwargs) return resp def post(self, path, dataNone, jsonNone, use_csrfFalse, **kwargs): url f{self.base_url}{path} headers kwargs.pop(headers, {}) if use_csrf and self.csrf_token: if data is not None and isinstance(data, dict): data[csrfmiddlewaretoken] self.csrf_token elif json is not None and isinstance(json, dict): # JSON API通常使用X-CSRFToken头 headers[X-CSRFToken] self.csrf_token resp self.session.post(url, datadata, jsonjson, headersheaders, timeoutself.timeout, **kwargs) return resp注意在生产环境中务必进行合法的授权测试。verifyFalse仅用于测试环境绕过自签名证书在知晓风险的情况下使用。工具应内置风险提示。3.2 未授权访问检测模块这是工具的核心。auth_bypass.py集成了对多个历史漏洞和常见错误配置的检测。# modules/auth_bypass.py from core.requester import RequestEngine import logging log logging.getLogger(__name__) def check_unauthorized_api(engine: RequestEngine): 检测未授权的API端点 checks [ (/api/v1/users/users/, GET, 用户列表), (/api/v1/assets/assets/, GET, 资产列表), (/api/v1/perms/asset-permissions/, GET, 资产授权列表), (/api/v1/terminal/sessions/, GET, 会话列表), (/api/v1/ops/command-executions/, GET, 命令执行历史), (/jumpserver/health/, GET, 健康检查可能泄露信息), ] results [] for path, method, desc in checks: try: if method GET: resp engine.get(path) # 可以扩展POST检查 if resp.status_code 200: # 关键不能仅看状态码还要判断返回内容是否包含敏感数据 # 例如检查返回的JSON是否包含results字段且非空或者是否包含id等关键字 import json try: data resp.json() # 简单启发式判断有数据且不是错误信息 if isinstance(data, dict) and data.get(results) is not None: log.warning(f[] 疑似未授权访问漏洞: {desc} ({path})) results.append((path, desc, 可能存在未授权访问, data)) elif isinstance(data, list) and len(data) 0: log.warning(f[] 疑似未授权访问漏洞: {desc} ({path})) results.append((path, desc, 可能存在未授权访问, data)) except json.JSONDecodeError: # 如果是HTML可能是跳转到了登录页不算漏洞 if login not in resp.text.lower(): log.info(f[?] {path} 返回非JSON需人工确认: {resp.status_code}) except Exception as e: log.debug(f检查 {path} 时出错: {e}) return results实操心得检测未授权API时最大的误区是只依赖HTTP状态码。很多接口即使返回200内容也可能是“请登录”或空列表。因此必须对响应内容进行解析和语义判断。同时请求频率要低避免触发WAF或封禁。3.3 H2 Database控制台利用模块这是JumpServer最具威胁的漏洞之一。h2_console.py模块实现了自动化的检测和利用。# modules/h2_console.py def check_and_exploit_h2(engine: RequestEngine): 检测并尝试利用H2 Database控制台未授权访问 h2_paths [/h2-console, /h2, /console, /admin/h2] for path in h2_paths: try: resp engine.get(path) if resp.status_code 200 and H2 Console in resp.text: log.critical(f[!!!] 发现H2 Database控制台: {engine.base_url}{path}) # 尝试自动连接并执行探测性SQL return _exploit_h2_console(engine.base_url path, engine) except Exception as e: continue return False def _exploit_h2_console(console_url, engine): 利用H2控制台执行SQL需根据JumpServer版本调整表名 # H2控制台连接需要POST特定参数 # 这里是一个高度简化的示例实际需要模拟浏览器提交连接表单 # 步骤1: 获取连接页面解析可能的隐藏字段 # 步骤2: 构造连接参数JDBC URL, 用户名密码 # JumpServer的H2通常是内嵌模式JDBC URL类似 jdbc:h2:file:./data/jumpserver # 用户名可能是 sa密码可能为空或为 password # 由于H2控制台交互复杂且不同版本表单有差异此处仅给出思路 # 1. 使用requests的Session保持会话。 # 2. 访问console_url解析出form中的action和input。 # 3. 向action提交连接参数。 # 4. 如果连接成功会跳转到查询页面然后可以POST SQL语句。 # 一个非常危险的PoC SQL是修改管理员密码 # UPDATE core_user SET password [新密码的PBKDF2哈希] WHERE username admin; # **注意此操作极具破坏性仅用于授权测试验证漏洞存在性切勿在非授权环境使用** # 更安全的验证方式是执行只读查询如 # SELECT username, email FROM core_user LIMIT 5; log.warning(H2控制台利用模块需要精细的HTTP交互模拟建议结合浏览器手动测试或使用专门工具。) # 在实际工具中这里会实现完整的自动化流程包括判断连接参数、执行查询、提取数据。 return True重要警告利用H2控制台修改数据库数据是破坏性操作。在授权测试中应优先使用SELECT语句验证漏洞存在性并立即报告。修改密码等操作必须在得到明确授权且准备充分如已备份的情况下进行。3.4 弱口令与默认凭证检测weak_password.py模块不仅检查JumpServer的Web登录也检查其依赖服务如Redis、MySQL。# modules/weak_password.py def check_jumpserver_default_login(engine: RequestEngine): 检测JumpServer默认管理员密码(admin/admin) login_url /api/v1/authentication/auth/ # 首先获取CSRF Token resp engine.get(/core/auth/login/) csrf_token engine.get_csrf_from_html(resp.text) if not csrf_token: log.info(无法获取CSRF Token可能页面结构已变化。) return False engine.csrf_token csrf_token data { username: admin, password: admin, csrfmiddlewaretoken: csrf_token } resp engine.post(/core/auth/login/, datadata) # 判断登录成功状态码可能是302重定向或者返回JSON包含token if resp.status_code in [200, 302]: if location in resp.headers and login not in resp.headers[location].lower(): log.critical(f[!!!] 默认密码登录成功 (admin/admin)) return True try: json_resp resp.json() if json_resp.get(token) or json_resp.get(msg) ok: log.critical(f[!!!] 默认密码登录成功 (admin/admin)) return True except: pass return False def check_redis_unauth(engine: RequestEngine, hostNone, port6379): 检测Redis未授权访问如果Redis暴露 # 注意Redis通常不通过HTTP暴露这是一个TCP服务检查。 # 此处展示思路实际实现可能需要socket连接。 import socket target_host host or engine.base_url.split(://)[1].split(:)[0].split(/)[0] try: s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(3) s.connect((target_host, port)) s.send(bINFO\r\n) # 发送Redis命令 result s.recv(1024) if bredis_version in result: log.warning(f[] Redis未授权访问可能: {target_host}:{port}) return True except Exception as e: log.debug(fRedis检测失败 {target_host}:{port} - {e}) finally: s.close() return False4. 实战演练从发现到验证的完整流程假设我们获得了一个授权测试目标https://jumpserver.example.com。我们将使用BlackJump进行非侵入式的安全检测。4.1 信息收集与初步探测首先使用工具进行基本的存活判断和指纹识别。虽然BlackJump主要聚焦漏洞利用但前期信息收集必不可少。# 使用命令行启动工具进行综合检测 python cli.py -u https://jumpserver.example.com --check-all工具内部会按顺序执行基础连接验证检查目标是否可达是否是一个JumpServer通过favicon、特定标题、Cookie名称jms_等判断。默认口令检测尝试使用admin/admin等常见组合登录。未授权API扫描运行check_unauthorized_api函数。信息泄露路径扫描检查/api/v1/health/,/metrics,/static/,/media/等路径是否存在敏感信息泄露。组件未授权检测检查/h2-console,/druid/index.html如果用了Druid,/actuator等。4.2 漏洞发现与深度验证假设扫描结果发现两个高危点/api/v1/users/users/返回了200状态码并且包含了非空的用户列表JSON。/h2-console路径存在且可以访问到H2登录页面。对于未授权API工具会标记为高危。我们需要手动验证其危害。我们可以用curl或工具的调试功能尝试获取更多数据# 使用工具内置的请求功能进行深度探测 python cli.py -u https://jumpserver.example.com --module info_disclosure --path /api/v1/assets/assets/如果资产列表也能未授权访问那么攻击者就获得了JumpServer管理的所有服务器、网络设备等清单这是严重的信息泄露。对于H2控制台这是最高危的发现。工具会尝试自动连接。在授权测试中我们进行验证打开浏览器访问https://jumpserver.example.com/h2-console。设置JDBC URL为jdbc:h2:file:./data/jumpserver这是常见路径也可能在配置文件中找到。用户名填sa密码留空或尝试password、jumpserver等。点击连接。如果成功则证明存在H2数据库未授权访问漏洞。4.3 利用链构造仅用于理解攻击路径在理解攻击者视角时我们可以构想一条利用链入口通过未授权API/api/v1/users/users/获取所有用户名。升级发现H2控制台未授权访问。连接后执行SQL查询SELECT * FROM core_user;直接获取所有用户的密码哈希和盐值。或者更直接地修改某个用户的密码哈希为一个已知值需要提前计算好对应密码的PBKDF2哈希。控制使用修改后的密码登录JumpServer Web界面获得该用户权限如果是管理员则获得完全控制权。横向移动通过JumpServer的“资产授权”和“会话管理”功能直接连接后台服务器实现内网横向移动。这个链条展示了从外部信息泄露到完全控制堡垒机乃至内网的过程危害极大。防御视角作为防御方看到这个链条就应该立即检查1. API权限校验是否完备2. H2控制台是否对外暴露且无认证3. 是否使用了默认或弱密码4. 网络层面是否对管理界面做了访问控制。5. 防御加固建议与排查清单基于上述攻击面为企业部署和维护JumpServer提供以下加固建议5.1 网络与访问控制最小化暴露面JumpServer管理界面绝不应直接暴露在公网。应通过VPN或零信任网络网关进行访问。网络隔离将JumpServer部署在内网安全区域严格限制源IP访问如仅允许运维网段访问管理端口。组件隔离确保H2 Database、Redis、MySQL等依赖服务仅监听在本地回环地址127.0.0.1或内部网络禁止绑定0.0.0.0。5.2 应用安全配置及时更新密切关注JumpServer官方安全公告及时更新到最新稳定版本。历史漏洞大多在新版本中已修复。禁用调试功能在生产环境中确保关闭Django的调试模式DEBUGFalse避免泄露敏感错误信息。强化认证强制修改默认管理员密码并启用强密码策略。启用多因素认证MFA。定期审计和清理无用账户。API安全审查所有API接口的权限控制确保遵循最小权限原则。可以使用API网关或WAF对管理API进行额外的访问控制。会话安全配置安全的Cookie属性HttpOnly, Secure并设置合理的会话超时时间。5.3 依赖组件安全H2 Database如果非必须在生产环境使用MySQL或PostgreSQL等外部数据库。如果必须使用H2确保其控制台绝对禁用通过配置spring.h2.console.enabledfalse或类似配置并且数据库文件权限严格限制。Redis务必设置强密码并配置requirepass。禁止使用--protected-mode no启动。绑定IP为127.0.0.1。定期扫描使用BlackJump这类工具在授权下定期对自身的JumpServer进行安全扫描模拟攻击者视角发现潜在风险。5.4 安全监控与审计日志审计开启JumpServer所有组件的详细日志并集中收集分析。特别关注登录失败、异常API访问、数据库连接请求等日志。入侵检测在JumpServer主机上部署HIDS主机入侵检测系统监控对敏感文件如数据库文件、配置文件的异常读写、异常进程启动等行为。定期评估将JumpServer纳入定期的渗透测试和红队演练范围检验整体安全防护的有效性。6. 工具开发的伦理思考与注意事项开发像BlackJump这样的工具是一把双刃剑。它在我手中是提升安全测试效率、帮助客户发现风险的利器但若被恶意使用则会造成严重危害。因此在开发和使用的全过程中必须恪守以下原则合法授权是前提任何测试行为必须在获得资产所有者明确书面授权的前提下进行。未经授权的测试等同于攻击是违法行为。最小影响原则在测试中优先使用只读操作如SELECT查询验证漏洞避免修改、删除数据或影响系统正常运行。如需进行破坏性验证如修改密码必须与客户充分沟通并在隔离环境或备份后进行。工具可控不在工具中集成高度自动化的、破坏性的攻击载荷如自动提权、自动横向移动。工具应定位为“漏洞验证器”而非“自动化攻击平台”。知识共享共同防御将工具发现的漏洞原理、利用方式和防御方案清晰地记录下来帮助开发者和运维人员理解风险共同提升产品和服务的安全性。这正是本文写作的初衷。持续学习与更新JumpServer等开源项目在快速迭代安全漏洞也在不断变化。工具需要持续维护关注新的CVE和安全研究。同时防御思路也要随之更新不能依赖一成不变的规则。最后我想强调的是安全的核心是人。工具再强大也只是辅助。真正的安全源于对架构的深刻理解、对流程的严格遵守、以及对安全的持续敬畏。希望这篇关于JumpServer安全研究和工具实践的分享能为大家带来一些有价值的参考。在实战中每一个细节都值得深究每一次测试都要心怀敬畏。