Created
October 23, 2026 11:54
-
-
Save Garphy/0532f362edd44ec3e9704591fda24db1 to your computer and use it in GitHub Desktop.
针对 eMudhra CA 证书弱私钥泄露事件的容器批量排查与热升级工具
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================================== | |
| # 脚本名称: container_batch_audit_and_upgrade.sh | |
| # 功能描述: 针对 eMudhra CA 证书弱私钥泄露事件的容器批量排查与热升级工具 | |
| # 探测端点: | |
| # - 原生链: https://dv-g3b-emudhra.we-have-a-problem.com/ | |
| # - 交叉签名链: https://dv-g3b-aaa-emudhra.we-have-a-problem.com/ | |
| # 判定标准: | |
| # - 握手失败/抛出证书错误 -> 安全 (SECURE) | |
| # - 握手成功/HTTP 200 -> 存在高危风险 (VULNERABLE) | |
| # ============================================================================== | |
| set -uo pipefail | |
| # 颜色输出 | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| TARGET_URL_DIRECT="https://dv-g3b-emudhra.we-have-a-problem.com/" | |
| TARGET_URL_CROSS="https://dv-g3b-aaa-emudhra.we-have-a-problem.com/" | |
| # 受影响的 eMudhra 关键中间证书 SHA256 指纹列表(大写) | |
| COMPROMISED_FINGERPRINTS=( | |
| "42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493" # EM DV TLS CA - G3B (Native) | |
| "45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE" # EM DV TLS CA - G3B (Sectigo AAA Cross) | |
| "0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19" # EM DV TLS CA - G3C (Native) | |
| "98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B" # EM OV TLS CA - G3B (Native) | |
| "CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F" # EM OV TLS CA - G3C (Native) | |
| "E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838" # EM EV TLS CA - G3B (Native) | |
| "432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3" # EM EV TLS CA - G3C (Native) | |
| "9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4" # EM OV CS CA - G3B (Code Signing Native) | |
| "A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8" # EM OV CS CA - G3B (Sectigo Cross) | |
| ) | |
| MODE="audit" # 默认模式:audit(仅审计)或 fix(审计并升级) | |
| AUTO_YES=false | |
| print_banner() { | |
| echo -e "${BLUE}========================================================================${NC}" | |
| echo -e "${BLUE} eMudhra CA 证书泄露容器内部批量排查与修复工具 (v1.0) ${NC}" | |
| echo -e "${BLUE} 针对 CVE-2026-111943 弱私钥中间证书信任排查 ${NC}" | |
| echo -e "${BLUE}========================================================================${NC}" | |
| echo "" | |
| } | |
| show_help() { | |
| echo "用法: $0 [选项]" | |
| echo "" | |
| echo "选项:" | |
| echo " -a, --audit 仅执行检测与排查,不修改容器(默认模式)" | |
| echo " -f, --fix 执行排查,并对发现易受攻击的容器尝试在线更新 ca-certificates" | |
| echo " -y, --yes 在 fix 模式下自动确认操作,无需交互提示" | |
| echo " -c, --container 指定单个容器名称或 ID 进行排查处理" | |
| echo " -h, --help 显示本帮助信息" | |
| echo "" | |
| echo "示例:" | |
| echo " $0 --audit # 批量检查当前主机上所有运行中的容器" | |
| echo " $0 --fix -y # 批量检查并自动尝试热升级所有容器的 CA 证书包" | |
| echo " $0 -c my-web-app --fix # 针对指定容器进行排查与修复" | |
| echo "" | |
| } | |
| # 检查 Docker 运行时 | |
| check_runtime() { | |
| if ! command -v docker &>/dev/null; then | |
| echo -e "${RED}[ERROR] 未检测到 docker 命令,请确认主机已安装 Docker 且已启动服务。${NC}" | |
| exit 1 | |
| fi | |
| if ! docker info &>/dev/null; then | |
| echo -e "${RED}[ERROR] 无法连接 Docker Daemon,请检查当前用户权限或 sudo 权限。${NC}" | |
| exit 1 | |
| fi | |
| } | |
| # 探测单个容器内部的连通性与证书信任状态 | |
| probe_container() { | |
| local cid="$1" | |
| local cname="$2" | |
| local vulnerable=false | |
| local probe_tool="none" | |
| echo -e "${CYAN}------------------------------------------------------------------------${NC}" | |
| echo -e "${CYAN}[*] 正在扫描容器: ${cname} (${cid:0:12})${NC}" | |
| # 1. 检测容器内可用的 HTTP 工具 (curl, wget, python, busybox) | |
| if docker exec "${cid}" which curl &>/dev/null; then | |
| probe_tool="curl" | |
| elif docker exec "${cid}" which wget &>/dev/null; then | |
| probe_tool="wget" | |
| elif docker exec "${cid}" which python3 &>/dev/null; then | |
| probe_tool="python3" | |
| elif docker exec "${cid}" which python &>/dev/null; then | |
| probe_tool="python" | |
| fi | |
| # 2. 对靶场发起探测 | |
| if [ "${probe_tool}" = "curl" ]; then | |
| # 测试原生链 | |
| if docker exec "${cid}" curl -s -m 5 -o /dev/null -w "%{http_code}" "${TARGET_URL_DIRECT}" 2>/dev/null | grep -q "200"; then | |
| echo -e " ${RED}[!] 靶场探测 (原生链): 握手成功 (HTTP 200) -> 存在高危信任漏洞!${NC}" | |
| vulnerable=true | |
| else | |
| echo -e " ${GREEN}[✓] 靶场探测 (原生链): 握手被成功阻断 (安全)${NC}" | |
| fi | |
| # 测试交叉签名链 | |
| if docker exec "${cid}" curl -s -m 5 -o /dev/null -w "%{http_code}" "${TARGET_URL_CROSS}" 2>/dev/null | grep -q "200"; then | |
| echo -e " ${RED}[!] 靶场探测 (交叉签名链): 握手成功 (HTTP 200) -> 存在高危信任漏洞!${NC}" | |
| vulnerable=true | |
| else | |
| echo -e " ${GREEN}[✓] 靶场探测 (交叉签名链): 握手被成功阻断 (安全)${NC}" | |
| fi | |
| elif [ "${probe_tool}" = "wget" ]; then | |
| if docker exec "${cid}" wget -q -T 5 -O /dev/null "${TARGET_URL_DIRECT}" 2>/dev/null; then | |
| echo -e " ${RED}[!] 靶场探测 (原生链): 握手成功 -> 存在高危信任漏洞!${NC}" | |
| vulnerable=true | |
| else | |
| echo -e " ${GREEN}[✓] 靶场探测 (原生链): 握手被成功阻断 (安全)${NC}" | |
| fi | |
| elif [ "${probe_tool}" = "python3" ] || [ "${probe_tool}" = "python" ]; then | |
| local py_cmd="${probe_tool}" | |
| local py_script=" | |
| import urllib.request, sys | |
| try: | |
| urllib.request.urlopen('${TARGET_URL_DIRECT}', timeout=5) | |
| sys.exit(0) # 成功连接,说明存在漏洞 | |
| except Exception as e: | |
| sys.exit(1) # 握手失败,说明安全 | |
| " | |
| if docker exec "${cid}" "${py_cmd}" -c "${py_script}" &>/dev/null; then | |
| echo -e " ${RED}[!] 靶场探测 (Python urllib): 握手成功 -> 存在高危信任漏洞!${NC}" | |
| vulnerable=true | |
| else | |
| echo -e " ${GREEN}[✓] 靶场探测 (Python urllib): 握手被阻断 (安全)${NC}" | |
| fi | |
| else | |
| echo -e " ${YELLOW}[?] 容器内未找到 curl/wget/python,转入静态证书文件检索...${NC}" | |
| fi | |
| # 3. 静态检索容器内部证书存储文件中是否存在涉事指纹/关键字 | |
| local cert_found=false | |
| local cert_check_cmd=" | |
| for f in /etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/cert.pem /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem; do | |
| if [ -f \"\$f\" ]; then | |
| if grep -qi \"emudhra\" \"\$f\" 2>/dev/null || grep -qi \"EM DV TLS CA\" \"\$f\" 2>/dev/null; then | |
| echo \"FOUND_NAME:\$f\" | |
| fi | |
| fi | |
| done | |
| " | |
| local cert_output | |
| cert_output=$(docker exec "${cid}" sh -c "${cert_check_cmd}" 2>/dev/null || true) | |
| if [ -n "${cert_output}" ]; then | |
| echo -e " ${RED}[!] 静态检测: 容器信任库中发现 eMudhra 关键字: ${cert_output}${NC}" | |
| vulnerable=true | |
| cert_found=true | |
| fi | |
| # 4. 根据模式执行修复 | |
| if [ "${vulnerable}" = true ]; then | |
| echo -e " ${RED}>>> 综合评估: 容器 ${cname} 判定为 [高危/易受攻击] <<<${NC}" | |
| if [ "${MODE}" = "fix" ]; then | |
| attempt_fix_container "${cid}" "${cname}" | |
| else | |
| echo -e " ${YELLOW}>>> 建议: 请使用 '--fix' 选项或重新基于最新基础镜像构建该容器。<<<${NC}" | |
| fi | |
| else | |
| echo -e " ${GREEN}>>> 综合评估: 容器 ${cname} 判定为 [安全/已阻断] <<<${NC}" | |
| fi | |
| } | |
| # 尝试在容器内部热升级 ca-certificates | |
| attempt_fix_container() { | |
| local cid="$1" | |
| local cname="$2" | |
| echo -e " ${BLUE}[>] 正在尝试在线热升级容器内部 ca-certificates...${NC}" | |
| # 判断包管理器类型 | |
| local pkg_manager="" | |
| if docker exec "${cid}" which apk &>/dev/null; then | |
| pkg_manager="apk" | |
| elif docker exec "${cid}" which apt-get &>/dev/null; then | |
| pkg_manager="apt" | |
| elif docker exec "${cid}" which dnf &>/dev/null; then | |
| pkg_manager="dnf" | |
| elif docker exec "${cid}" which yum &>/dev/null; then | |
| pkg_manager="yum" | |
| elif docker exec "${cid}" which microdnf &>/dev/null; then | |
| pkg_manager="microdnf" | |
| fi | |
| case "${pkg_manager}" in | |
| apk) | |
| echo -e " [*] 检测到 Alpine 环境,执行 apk 升级..." | |
| if docker exec "${cid}" sh -c "apk update && apk add --no-cache --upgrade ca-certificates" &>/dev/null; then | |
| echo -e " ${GREEN}[✓] Alpine 容器 ca-certificates 热升级成功!${NC}" | |
| else | |
| echo -e " ${RED}[✗] Alpine 容器升级失败,请检查容器出网网络策略。${NC}" | |
| fi | |
| ;; | |
| apt) | |
| echo -e " [*] 检测到 Debian/Ubuntu 环境,执行 apt 升级..." | |
| if docker exec "${cid}" sh -c "DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y --only-upgrade ca-certificates || apt-get install --no-install-recommends -y ca-certificates" &>/dev/null; then | |
| docker exec "${cid}" update-ca-certificates &>/dev/null || true | |
| echo -e " ${GREEN}[✓] Debian/Ubuntu 容器 ca-certificates 热升级成功!${NC}" | |
| else | |
| echo -e " ${RED}[✗] Debian/Ubuntu 容器升级失败,请检查网络或软件源。${NC}" | |
| fi | |
| ;; | |
| dnf|yum) | |
| echo -e " [*] 检测到 RHEL/CentOS 环境,执行 ${pkg_manager} 升级..." | |
| if docker exec "${cid}" "${pkg_manager}" update -y ca-certificates &>/dev/null; then | |
| docker exec "${cid}" update-ca-trust &>/dev/null || true | |
| echo -e " ${GREEN}[✓] RHEL/CentOS 容器 ca-certificates 热升级成功!${NC}" | |
| else | |
| echo -e " ${RED}[✗] RHEL/CentOS 容器升级失败。${NC}" | |
| fi | |
| ;; | |
| microdnf) | |
| echo -e " [*] 检测到 UBI/Minimal 环境,执行 microdnf 升级..." | |
| if docker exec "${cid}" microdnf update -y ca-certificates &>/dev/null; then | |
| docker exec "${cid}" update-ca-trust &>/dev/null || true | |
| echo -e " ${GREEN}[✓] UBI 容器 ca-certificates 热升级成功!${NC}" | |
| else | |
| echo -e " ${RED}[✗] microdnf 升级失败。${NC}" | |
| fi | |
| ;; | |
| *) | |
| echo -e " ${YELLOW}[!] 未能识别容器内支持的包管理器(可能是 scratch/distroless 或只读文件系统)。${NC}" | |
| echo -e " ${YELLOW}[!] 注意: 只读或极简镜像无法执行热升级,强烈建议在 Dockerfile 中更新并重新部署。${NC}" | |
| ;; | |
| esac | |
| } | |
| # 解析命令行参数 | |
| SPECIFIC_CONTAINER="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -a|--audit) | |
| MODE="audit" | |
| shift | |
| ;; | |
| -f|--fix) | |
| MODE="fix" | |
| shift | |
| ;; | |
| -y|--yes) | |
| AUTO_YES=true | |
| shift | |
| ;; | |
| -c|--container) | |
| SPECIFIC_CONTAINER="$2" | |
| shift 2 | |
| ;; | |
| -h|--help) | |
| show_help | |
| exit 0 | |
| ;; | |
| *) | |
| echo "未知参数: $1" | |
| show_help | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| print_banner | |
| check_runtime | |
| if [ "${MODE}" = "fix" ] && [ "${AUTO_YES}" = false ]; then | |
| echo -e "${YELLOW}[WARNING] 您已启用 '--fix' 模式,该模式将直接在运行中的容器内执行软件包更新。${NC}" | |
| echo -e "${YELLOW}热升级仅为应急临时措施,容器重新拉取或重建后仍需 Dockerfile 根治。${NC}" | |
| read -r -p "是否继续执行? [y/N]: " confirm | |
| if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then | |
| echo "操作已取消。" | |
| exit 0 | |
| fi | |
| fi | |
| if [ -n "${SPECIFIC_CONTAINER}" ]; then | |
| # 单容器扫描 | |
| cid=$(docker inspect --format '{{.Id}}' "${SPECIFIC_CONTAINER}" 2>/dev/null || true) | |
| cname=$(docker inspect --format '{{.Name}}' "${SPECIFIC_CONTAINER}" 2>/dev/null || true) | |
| if [ -z "${cid}" ]; then | |
| echo -e "${RED}[ERROR] 未找到容器: ${SPECIFIC_CONTAINER}${NC}" | |
| exit 1 | |
| fi | |
| probe_container "${cid}" "${cname#/}" | |
| else | |
| # 批量扫描所有运行中的容器 | |
| CONTAINERS=$(docker ps -q) | |
| if [ -z "${CONTAINERS}" ]; then | |
| echo -e "${YELLOW}[INFO] 当前宿主机未发现任何正在运行的 Docker 容器。${NC}" | |
| exit 0 | |
| fi | |
| TOTAL_COUNT=$(echo "${CONTAINERS}" | wc -w | tr -d ' ') | |
| echo -e "${BLUE}[*] 发现 ${TOTAL_COUNT} 个正在运行的容器,开始批量执行排查 (模式: ${MODE})...${NC}\n" | |
| CURRENT_INDEX=1 | |
| for cid in ${CONTAINERS}; do | |
| cname=$(docker inspect --format '{{.Name}}' "${cid}" 2>/dev/null || echo "unknown") | |
| cname="${cname#/}" | |
| echo -e "${BLUE}[${CURRENT_INDEX}/${TOTAL_COUNT}] 准备检测容器: ${cname}${NC}" | |
| probe_container "${cid}" "${cname}" | |
| echo "" | |
| CURRENT_INDEX=$((CURRENT_INDEX + 1)) | |
| done | |
| fi | |
| echo -e "${BLUE}========================================================================${NC}" | |
| echo -e "${GREEN}[✓] 容器批量排查与处置任务执行完毕。${NC}" | |
| echo -e "${BLUE}========================================================================${NC}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment