
2026년 10월 eMudhra/emSign CA 키 침해 사건을 위한 오프라인 스캐너로, OS 신뢰 저장소, CA 번들, Java cacerts, NSS 및 컨테이너 이미지 전반에서 영향을 받은 인증서를 찾아냅니다.
2026년 10월 eMudhra / emSign 중간 CA
개인 키 침해 사고를 위한 탐지 도구입니다. 여덟 개의 중간 CA가 P-384 키로 발급되었는데,
그 개인 스칼라 값이 아주 작은 정수였습니다(복구된 값 4, 56, 66, 115, 155, 156, 230, 237). 따라서 누구나 이들 CA로 이어지는 인증서를 손쉽게 위조할 수 있습니다.
브라우저와 OS 트러스트 스토어는 신속하게 신뢰를 철회했습니다. 하지만 대부분의 비브라우저
트러스트 스토어는 자동으로 업데이트되지 않으며, 이 도구가 바로 그런 곳을 찾아냅니다.
상태: 사고가 현재 진행 중이므로, 포함된 IOC는 스냅샷으로 취급하십시오(실행할 때마다 해당 IOC의
generated날짜가 출력됩니다). IOC를 갱신하려면 업스트림 트래커에서tools/sources/*.json을 새로 고친 뒤 생성기를 다시 실행하십시오(아래 참조).
trustscan(Python)과 trustscan.ps1(Windows/PowerShell)은 호스트의
트러스트 스토어와 CA 번들을 인벤토리화하고, 영향을 받은 인증서와 일치하는 항목을
표시합니다. 순수 표준 라이브러리 + cryptography만 사용하며, 다른 의존성은 없습니다.
Python 3와 cryptography가 필요합니다(유일한 의존성):
pip install -r requirements.txt
# scan this host's trust stores and CA bundles
python trustscan.py
# machine-readable for ticketing
python trustscan.py --format csv > findings.csv
# Windows-native: cert stores (both scopes) + GPO roots + env bundles
pwsh -File trustscan.ps1
종료 코드: 0 = 정상, 1 = 발견 사항 있음, 2 = 오류. INFO 전용 결과(아래
--flag-aaa 참조)는 0으로 종료됩니다.
모든 실행은 AAA 교차 서명 경로에 관한 한 줄짜리 주의 문구로 끝납니다(참조:
범위 및 제한 사항). text 모드에서는 이 문구가
보고서의 일부이며, json/csv 모드에서는 기계 판독 가능한 출력이 깨끗하게
유지되도록 stderr로 전송됩니다.
아래 각 위치에서 발견된 모든 인증서는 지문이 계산되어 네 가지 방식으로 검사됩니다:
| 매칭 | 심각도 | 의미 |
|---|---|---|
compromised_ca_cert | CRITICAL | 정확히 일치하는 손상된 중간 인증서(인증서 SHA-256) |
compromised_key_reuse | CRITICAL | 손상된 키를 가진 다른 인증서(SPKI SHA-256) - Sectigo 교차 서명 및 모든 재발급을 포착 |
issued_by_compromised_ca | HIGH | AuthorityKeyIdentifier가 손상된 중간 인증서를 가리키는 리프/하위 CA - 위조 가능 |
distrust_root_present | CRITICAL | 신뢰가 철회된 emSign 루트가 여전히 이곳에서 신뢰됨. 자체 키는 온전하지만 중간 인증서 키가 공개되어 있으므로, 이 루트가 신뢰되는 동안 모든 도메인에 대한 위조 인증서가 폐기 확인을 하지 않는 모든 클라이언트(대부분의 비브라우저 클라이언트)에서 유효하게 검증됨. 제거하거나 번들을 업데이트할 것. 단독으로는 AAA 교차 서명 경로(아래)를 닫지 못함 |
추가 검사 하나는 옵트인입니다(--flag-aaa / -FlagAaa):
| 매칭 | 심각도 | 의미 |
|---|---|---|
cross_sign_root_present | INFO | 합법적인 Sectigo 루트 AAA Certificate Services가 존재함. 이 루트는 여덟 개의 중간 인증서에 교차 서명했으므로 이들의 두 번째 신뢰 경로를 고정함. 자체 키는 온전함. Mozilla와 Chrome은 2025년 4월에 이를 제거했지만, Windows, Apple, 구형 Linux 번들 및 많은 Java 키스토어는 여전히 이를 신뢰함. 제거 여부를 결정하는 팀을 위해 요청 시에만 보고됨(이 사건에 대한 Debian의 업데이트는 제거함). 종료 코드에는 절대 영향을 주지 않음 |
기본적으로 AAA Certificate Services는 플래그되지 않습니다: 자체 키가 온전하며 이를 제거하는 것은 기본 교정 조치가 아닌 의도적인 선택입니다. 이것이 왜 중요한지는 아래 제한 사항을 참조하십시오.
trustscan이 검사하는 위치| 소스 | 설명 | 확인하는 위치 |
|---|---|---|
certifi | Python CA 번들 | certifi.where() (런타임에 확인) |
bundles → 배포판 | OS ca-certificates 집계 | Debian/Arch/Alpine /etc/ssl/certs/ca-certificates.crt · RHEL /etc/pki/tls/certs/ca-bundle.crt, /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem, 통합 Java cacerts, 관리자 anchors/ 디렉터리 · SUSE /etc/ssl/ca-bundle.pem, /var/lib/ca-certificates/ca-bundle.pem · Debian 소스 디렉터리 /usr/share/ca-certificates/ · /etc/ssl/cert.pem |
bundles → FreeBSD | ca_root_nss | /usr/local/share/certs/ca-root-nss.crt · /usr/local/etc/ssl/cert.pem |
bundles → Homebrew / MacPorts | brew / port ca-certificates, openssl | /opt/homebrew/etc/ca-certificates/cert.pem · /opt/homebrew/etc/openssl*/cert.pem · /usr/local/etc/... (Intel) · /opt/local/etc/{openssl,ssl}/cert.pem |
bundles → curl | 독립형 curl 번들 | /usr/local/share/curl/curl-ca-bundle.crt · /opt/local/share/curl/curl-ca-bundle.crt · C:\Program Files\Git\...\ca-bundle.crt |
bundles → conda | 각 환경의 ssl/cacert.pem | $CONDA_PREFIX · /opt/conda · ~/anaconda3, ~/miniconda3, ~/miniforge3 (envs/* 포함) |
bundles → AWS CLI v2 | 번들된 botocore | /usr/local/aws-cli/.../botocore/cacert.pem · C:\Program Files\Amazon\AWSCLIV2\... |
bundles → 클라우드 SDK / 레지스트리 / 생태계 | 추가 벤더 번들 | gcloud / Azure CLI third_party/certifi · 컨테이너 레지스트리 /etc/{docker,containers}/certs.d/*/ · Perl Mozilla::CA · Erlang/Elixir certifi |
bundles → pip | pip에 번들된 certifi | <pip>/_vendor/certifi/cacert.pem (import pip로 확인) |
nss | Firefox / Thunderbird / Chrome-Linux + 시스템 NSS DB | ~/.mozilla/firefox/*/cert9.db · ~/.thunderbird/*/cert9.db · ~/snap/firefox/common/.mozilla/firefox/*/cert9.db · ~/.pki/nssdb/cert9.db · /etc/pki/nssdb/cert9.db · %APPDATA%\Mozilla\Firefox\Profiles\*\cert9.db · %APPDATA%\Thunderbird\Profiles\*\cert9.db |
| 소스 | 확인하는 위치 |
|---|---|
os (Linux) | /etc/ssl/certs · /usr/local/share/ca-certificates · /etc/pki/tls/certs · /etc/pki/ca-trust/extracted/pem (재귀), 그리고 Android/AOSP /system/etc/security/cacerts, /apex/com.android.conscrypt/cacerts, cacerts-added/ (해시 이름 파일) |
os (macOS) | 기본 키체인 + SystemRootCertificates.keychain에 대해 security find-certificate -a -p |
os (Windows, Python) | ROOT, CA, MY, TrustedPublisher, AuthRoot에 대한 ssl.enum_certificates + 그룹 정책으로 푸시된 LocalMachine 루트 (레지스트리) |
java | $JAVA_HOME + JVM 루트 (/usr/lib/jvm/*, /opt/java/*, /Library/Java/JavaVirtualMachines/*, C:\Program Files\Java\*, Adoptium, Corretto, Microsoft) → .../lib/security/cacerts |
env | SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, NODE_EXTRA_CA_CERTS의 파일; SSL_CERT_DIR의 디렉터리 |
containers (옵트인) | Docker /var/lib/docker/overlay2/*/diff, podman (root + rootless, 다른 사용자 포함), containerd overlayfs 스냅샷 - 각 레이어의 집계 번들을 스캔; root 필요, 위치에 레이어 이름 표시 |
apptrust (옵트인) | 앱 서버 및 데이터 인프라 Java 트러스트스토어 (Tomcat, WildFly/JBoss, Kafka, Elasticsearch) 및 /opt/*/jre/.../cacerts, C:\Program Files\*\jre\... 아래의 앱 번들 JRE - 호스트 Java 스캔이 놓치는 벤더 번들 전용 JRE |
--path 대상 | 설정에서 참조하는 저장소 (nginx, Apache, HAProxy, Oracle sqlnet.ora, LDAP) 및 프로젝트별 번들 (Composer vendor/composer/ca-bundle/...) - --path를 해당 위치로 지정 |
paths | 전달한 모든 파일/디렉터리 |
--rootfs <dir> | 추출된 이미지 또는 펌웨어 rootfs 내부의 배포판 / curl / Java / Android 저장소 위치 |
env변수는 인증서 데이터가 아닌 경로를 담습니다 -trustscan은 해당 경로의 파일을 읽습니다.
--path 사용certifi가 자체 CA 루트를 포함하는 유일한 Python 패키지는 아닙니다. 다음 패키지들은 certifi와 OS 저장소 모두와 독립적으로 site-packages 내부에 자체 *.pem을 번들로 포함합니다:
| 패키지 | 번들 파일 |
|---|---|
| botocore (AWS CLI / boto3) | botocore/cacert.pem |
| grpcio (gRPC) | grpc/_cython/_credentials/roots.pem |
| httplib2 | httplib2/cacerts.txt (특이한 확장자 - 파일을 직접 전달하세요) |
| requests (레거시 <2.16) | requests/cacert.pem |
기본 스캔은 오직 certifi와 pip에 벤더링된 certifi만 자동으로 찾아내며, 오직 trustscan.py를 실행하는 인터프리터에 대해서만 그렇게 합니다. 위의 나머지 모든 것 - 그리고 시스템에 있는 다른 모든 virtualenv 또는 conda 환경 - 은 --path를 site-packages로 지정하여 명시적으로 스캔해야 합니다:
python trustscan.py "$(python -c 'import site;print(site.getsitepackages()[0])')"
# or cover many environments at once
python trustscan.py /opt/venvs/ /home/*/.virtualenvs/ /opt/conda/envs/
# Windows / PowerShell equivalent
python trustscan.py (python -c "import site; print(site.getsitepackages()[0])")
OS 루트 저장소(/etc/ssl/certs 등)에는 이러한 항목이 포함되지 않습니다 - 라이브러리는 이에 의존하지 않기 위해 자체 사본을 번들로 제공합니다. 디렉터리 스캔은 인증서 확장자 파일만 열기 때문에, cacerts.txt와 같은 이상한 이름은 파일로 직접 전달해야 합니다.
trustscan.ps1)는 더 나아갑니다Python os 소스는 LocalMachine 저장소와 LocalMachine GPO 루트를 다룹니다. trustscan.ps1은 Python 표준 라이브러리가 Win32 상호 운용 없이는 접근할 수 없는 한 가지, 즉 CurrentUser 범위(동일한 논리적 저장소)를 추가하며, env 번들과 -Path 파일/디렉터리도 기본적으로 처리합니다. 사용자별 신뢰가 중요할 때 사용하십시오. 그렇지 않으면 Python 도구의 Windows 지원 범위와 동등합니다.
PEM/DER X.509, PKCS#7(.p7b/.p7c), PKCS#12(.p12/.pfx, changeit/빈 값으로 시도), 그리고 JKS 키스토어 - 모두 표준 라이브러리만으로 파싱됩니다.