
스마트 Log4Shell/Log4j/CVE-2021-44228 스캐너
조직이 중요한 Log4J / CVE-2021-44228 취약점에 노출된 애플리케이션을 검색하도록 도와주는 훌륭한 도구들이 이미 많이 나와 있지만, 제가 접한 도구 중에는 분석가가 수정(remediation) 과정 전반에 걸쳐 특정 발견 항목을 추적할 수 있게 해주는 도구는 없었습니다. Log4Shell Sentinel은 발견 결과를 CSV 형식으로 출력하여 분석가가 Excel에서 결과를 여러 각도로 분석하고, 일부 발견 항목을 무시 목록에 추가한 후 스캔을 다시 실행하거나, 스캔 결과를 비교하고, 스캔 결과에 데이터를 추가하는 등의 작업을 할 수 있도록 설계되었습니다.
그래서 Log4Shell Sentinel이 탄생했습니다. Log4Shell Sentinel은 몇 가지 고유한 기능을 갖춘 파일 기반 스캐너입니다. 다른 도구들을 대체하기 위한 것이 아니라 보완할 수 있습니다.
Log4Shell Sentinel은 파일 기반 스캐너입니다. 대상 시스템에서 다음 파일 형식의 아티팩트를 검색하여 Java 기반 애플리케이션을 찾습니다.
| 파일 유형 | 설명 |
|---|---|
| 단순 jar | log4j-core 파일이 포함되지 않은 경우입니다. |
| Fat / Uber jar | uber jar는 클래스/패키지와 애플리케이션의 모든 종속성(라이브러리, 리소스, 메타데이터 파일)을 단일 jar 파일에 포함하고 있는 jar입니다. 가장 일반적으로 사용되는 배포 방식입니다. |
| WAR | war(Web Application Archive) 파일은 라이브러리와 기타 리소스 외에도 JSP, HTML, JavaScript 코드를 포함하는 파일입니다. 이 형식은 덜 일반적으로 사용됩니다. |
| EAR | ear(Enterprise Application Archive)는 Jakarta EE 배포에서 더 일반적으로 사용되었던 또 다른 형식입니다. |
취약한 log4j-core jar 인스턴스를 검색합니다. 그런 다음 다음 작업을 수행합니다.
아티팩트의 MD5 해시를 계산합니다. 이를 통해 분석가는 다른 머신/컨테이너에서 실행 중인 동일한 애플리케이션을 식별하고 이를 단일 발견 항목으로 취급할 수 있습니다.
컨테이너에 속하는 것으로 확인된 파일(예: /var/lib/docker/overlay2/192768f471818601094bf4edd96d14bfc0e2b178a04a2efd00b2231ad4e46b33/merged/app/spring-boot-application.jar)의 경우, 해당 파일을 해당 이미지로 매핑하는 작업을 처리합니다. 예를 들어 위 경로를 ghcr.io/christophetd/log4shell-vulnerable-app:latest 이미지로 변환합니다. 다양한 컨테이너 런타임이 이 매핑을 서로 다른 방식으로 저장하므로 분석가의 수많은 불필요한 작업을 줄일 수 있습니다. 또한 분석가는 단일 애플리케이션을 실행하는 여러 컨테이너를 단일 발견 항목으로 취급할 수 있습니다.
현재 실행 중이 아닌 컨테이너(캐시된 이미지 포함)에 해당하는 매칭과 같은 의미 없는 매칭을 제거합니다. 이를 통해 분석가는 중요한 항목에 집중할 수 있으며 불필요한 작업 시간을 절약할 수 있습니다.
분석가가 다음 항목을 기준으로 매칭을 무시할 수 있습니다:
이를 통해 분석가는 취약하지 않은 것으로 알려진 애플리케이션이나 위협이 되지 않는 CLI 기반 애플리케이션을 제거할 수 있습니다. 예를 들어 Logstash 인스턴스가 감지된 경우, 분석가는 해당 도구를 실행하지 않거나 가끔 CLI에서만 실행한다면 이를 무시하도록 선택할 수 있습니다.
구성 관리 도구(예: Ansible)와 함께 사용하도록 최적화되어 있어 몇 분 안에 환경을 빠르게 스캔할 수 있습니다.
Log4Shell Sentinel이 추가하는 메타데이터 보강에 대한 자세한 내용은 제 블로그 게시물을 참조하세요.
가장 쉬운 방법은 미리 컴파일된 바이너리를 다운로드하는 것입니다.
이 역시 간단합니다. 다만 환경의 GLIBC 관련 차이를 피하기 위해 정적으로 컴파일된 버전을 빌드하고 싶을 것입니다. 이 도구는 기본적으로 CGO를 사용하는 다음 모듈을 사용합니다:
os/usernet정적으로 컴파일된 버전을 빌드하려면 이 Go 버전의 라이브러리를 사용하도록 저장소를 클론한 후 다음을 실행하세요:
$ CGO_ENABLED=0 go build -ldflags="-s -w"
$ ldd log4shell_sentinel
not a dynamic executable
$ ./log4shell_sentinel -h
NAME:
Log4Shell Sentinel - by Osama Elnaggar
USAGE:
log4shell_sentinel [global options] command [command options] [arguments...]
VERSION:
v1.0.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--path value, -p value Path to search (default: ".")
--no-banner, --nb Suppress banner (default: false)
--no-messages, --nm Suppress messages except for CSV output (default: false)
--no-header, --nh Suppress header in CSV output (default: false)
--imd5 value, --im value Ignore MD5 hashes. Refer to the GitHub page for expected format
--ipath value, --ip value Ignore file path matches. Refer to the GitHub page for expected format
--icimage value, --ic value Ignore container image matches. Refer to the GitHub page for expected format
--print-headers, --ph Print CSV Headers only (default: false)
--help, -h show help (default: false)
--version, -v print the version (default: false)
기본적으로 스캔은 현재 디렉터리만 스캔합니다. 예를 들어:
$ ./log4shell_sentinel
░█░░░█▀█░█▀▀░█░█░█▀▀░█░█░█▀▀░█░░░█░░░░░█▀▀░█▀▀░█▀█░▀█▀░▀█▀░█▀█░█▀▀░█░░
░█░░░█░█░█░█░░▀█░▀▀█░█▀█░█▀▀░█░░░█░░░░░▀▀█░█▀▀░█░█░░█░░░█░░█░█░█▀▀░█░░
░▀▀▀░▀▀▀░▀▀▀░░░▀░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░░░▀▀▀░▀▀▀░▀░▀░░▀░░▀▀▀░▀░▀░▀▀▀░▀▀▀
- A CVE-2021-44228/CVE-2021-45046/CVE-2021-45105 Scanner
- v1.0.0
- by Osama Elnaggar
[*] WARNING: Running as non-root user.
Non-readable files / dirs will be skipped
Container mapping will fail
[*] Starting shallow scan ............ [DONE]
[*] Starting deep scan ............... [DONE]
[*] Calculating MD5 hashes ........... [DONE]
[*] Performing container image lookups [DONE]
[*] Processing ignore list(s) ........ [DONE]
[*] Generating output ................ [DONE]
IP,Hostname,AppName,Team,Ignore (Y/N),Comments,MD5Hash,Timestamp,Container,ContainerImage,FullPath,Version
192.168.121.121,server3,,,,,4e615cd580758b70c49ade1f79103328,2021-12-21T07:38:09Z,true,ghcr.io/christophetd/log4shell-vulnerable-app:latest,/run/containerd/io.containerd.runtime.v2.task/k8s.io/dc2c9c214809f506283c917244cd126a9b056ac7274322d12b59c9196d95dd9b/rootfs/app/spring-boot-application.jar,log4j-core-2.14.1.jar
루트 사용자가 아닌 상태로 실행하면 WARNING 메시지가 즉시 표시됩니다. 다음 작업을 수행하려면 root 권한이 필요하기 때문입니다.
root 권한이 없어도 작동하지만 최상의 결과를 얻지 못할 수 있습니다.
샘플 발견 항목은 다음과 같습니다:
IP,Hostname,AppName,Team,Ignore (Y/N),Comments,MD5Hash,Timestamp,Container,ContainerImage,FullPath,Version
192.168.121.121,server3,,,,,4e615cd580758b70c49ade1f79103328,2021-12-21T07:38:09Z,true,ghcr.io/christophetd/log4shell-vulnerable-app:latest,/run/containerd/io.containerd.runtime.v2.task/k8s.io/dc2c9c214809f506283c917244cd126a9b056ac7274322d12b59c9196d95dd9b/rootfs/app/spring-boot-application.jar,log4j-core-2.14.1.jar
일부 필드는 의도적으로 비워 두며 Excel 등에서 분석가가 채우도록 남겨 둡니다. 각 필드에 대한 간단한 설명은 다음과 같습니다:
이 도구는 다음 옵션을 제공합니다:
Log4Shell Sentinel의 다른 도구에는 없는 고유한 기능 중 하나는 파일 시스템 스캔에서 발견된 jar 파일(예: /var/lib/docker/overlay2/192768f471818601094bf4edd96d14bfc0e2b178a04a2efd00b2231ad4e46b33/merged/app/spring-boot-application.jar)을 해당 컨테이너 이미지(위 예의 경우 ghcr.io/christophetd/log4shell-vulnerable-app:latest로 변환)로 변환할 수 있다는 것입니다. 다양한 컨테이너 런타임이 이 매핑을 서로 다른 방식으로 저장하므로 분석가의 수많은 불필요한 작업을 줄일 수 있습니다. 또한 분석가는 단일 애플리케이션을 실행하는 여러 컨테이너를 단일 발견 항목으로 취급할 수 있습니다. 현재 이 솔루션은 다음 조회를 지원합니다:
처음에는 식별된 애플리케이션 수가 압도적일 수 있으며, 외부 사용자에게 노출된 애플리케이션과 같은 특정 하위 집합에 집중하고 싶을 수도 있습니다. 또는 특정 규정 준수 범위에 집중하고 싶을 수도 있습니다. 이를 지원하기 위해 애플리케이션은 목록 지정을 지원합니다. Log4Shell은 현재 3가지 무시 목록을 지원합니다:
단순한 목록 대신 CSV 파일을 입력으로 사용합니다. 이는 분석가가 특정 발견 항목을 무시하는 이유를 문서화하도록 권장합니다. 분석가가 발견 항목을 처리하고 수동 또는 자동화된 소스 코드 분석이나 DAST 도구 등을 사용하여 취약하지 않은 애플리케이션을 식별함에 따라...
목록은 다음 형식의 간단한 CSV 파일입니다:
MD5Sum,AppName,Reason
AppName 및 Reason 열은 비어 있을 수 있지만 특정 애플리케이션을 포함하거나 제외하는 이유를 기록하기 위한 것입니다.
참고 이 기능은 애플리케이션에 대해 계산된 MD5 해시를 사용하므로 fat / uber jar, EAR 또는 WAR 애플리케이션에서만 작동합니다.
예를 들어, 다음과 같은 발견 항목이 있다고 가정합니다:
IP,Hostname,AppName,Team,Ignore (Y/N),Comments,MD5Hash,Timestamp,Container,ContainerImage,FullPath,Version
192.168.121.121,server3,,,,,4e615cd580758b70c49ade1f79103328,2021-12-21T07:38:09Z,true,ghcr.io/christophetd/log4shell-vulnerable-app:latest,/run/containerd/io.containerd.runtime.v2.task/k8s.io/dc2c9c214809f506283c917244cd126a9b056ac7274322d12b59c9196d95dd9b/rootfs/app/spring-boot-application.jar,log4j-core-2.14.1.jar
그리고 애플리케이션이 _취약하지 않다_고 판단되면 hashes.csv에 다음 내용을 저장하기만 하면 됩니다:
4e615cd580758b70c49ade1f79103328,Demo App,Not vulnerable
그런 다음 다시 실행합니다:
# ./log4shell_sentinel --im hashes.csv --path /
위 발견 항목은 표시되지 않습니다.
목록은 다음 형식의 간단한 CSV 파일입니다:
Container Image,AppName,Reason
이전 예를 이어서, 컨테이너 이미지가 ghcr.io/christophetd/log4shell-vulnerable-app:latest인 모든 발견 항목을 무시하도록 지정할 수 있습니다. images.csv 파일을 다음과 같이 생성하면 됩니다:
ghcr.io/christophetd/log4shell-vulnerable-app:latest,,
그런 다음 다음 명령으로 실행합니다:
# ./log4shell_sentinel --ic images.csv -p /
이전 발견 항목도 표시되지 않습니다.
이 역시 다음 형식의 간단한 CSV 파일입니다:
Path,AppName,Reason
위 예를 사용하여 다음 paths.csv 파일을 생성하면:
/run/containerd/io.containerd.runtime.v2.task/k8s.io/dc2c9c214809f506283c917244cd126a9b056ac7274322d12b59c9196d95dd9b/rootfs/app/spring-boot-application.jar,Demo App,Not vulnerable
이전 발견 항목이 표시되지 않습니다.
경고 가능하다면 경로가 고유하지 않을 수 있으므로 무시 목적으로 MD5 해시나 컨테이너 이미지 이름을 사용하는 것이 좋습니다.
Log4Shell Sentinel을 단일 서버에서 실행해도 전혀 문제없지만, 선호하는 구성 관리 도구(Ansible, Chef, Puppet, Salt, AWS SSM 등)나 parellel-ssh와 같은 간단한 병렬 SSH 도구와 결합할 때 진가를 발휘합니다.
이를 통해 다음과 같은 많은 이점을 얻을 수 있습니다:
이에 대한 전체 예제는 제 블로그 게시물을 참조하세요.
환경 대량 스캔 섹션을 참조하세요.
root 사용자로 실행하지 않았거나(애플리케이션이 설치된 위치에 대한 접근 권한이 필요합니다) 올바른 path를 지정하지 않았기 때문일 것입니다. root 사용자로 실행하고 --path /로 설정하는 것이 좋습니다.v1.0.0 기준으로 이 도구는 다음에 대한 매핑을 지원합니다:
다른 것을 사용하는 경우 매핑이 작동하지 않습니다. 위 항목 중 하나를 사용하는 경우 root 사용자로 도구를 실행하지 않았거나 사용되는 소켓/파일이 기본 위치가 아닌 곳에 있을 수 있습니다.
개발자: Osama Elnaggar
| 필드 | 예시 | 설명 |
|---|
| IP | 192.168.121.121 | 인스턴스에 여러 IP가 있는 경우 기본 IP가 추가됩니다. |
| Hostname | server3 | 호스트 이름 |
| AppName | 스캔 완료 후 사용자가 작성해야 합니다. | |
| Team | 스캔 완료 후 사용자가 작성해야 합니다. | |
| Ignore (Y/N) | 스캔 완료 후 사용자가 작성해야 합니다. | |
| Comment | 스캔 완료 후 사용자가 작성해야 합니다. | |
| MD5Hash | 4e615cd580758b70c49ade1f79103328 | 애플리케이션의 고유 지문(fingerprint) |
| Timestamp | 2021-12-21T07:38:09Z | 스캔이 수행된 시각. 동일 호스트에 대한 여러 스캔을 집계하려는 경우 유용합니다. |
| Container | true | True = 컨테이너임을 의미합니다. |
| ContainerImage | ghcr.io/christophetd/log4shell-vulnerable-app:latest | 컨테이너가 감지된 경우 해당 이미지입니다. |
| FullPath | /run/containerd/io.containerd. runtime.v2.task/k8s.io/.../spring-boot-application.jar | 발견 항목의 전체 경로. 이 예에서 취약한 JAR 파일은 fat / uber jar의 일부입니다. |
| Version | log4j-core-2.14.1.jar | 감지된 lo4j-core 버전 |
| 옵션 | 설명 |
|---|
--path value, -p value | 검색할 경로를 지정합니다. 기본값은 현재 디렉터리입니다. 중요 전체 파일 시스템을 포함하려면 /로 설정하는 것이 좋습니다. |
--no-banner, --nb | 배너를 숨깁니다. |
--no-messages, --nm | 진행 관련 메시지와 같은 메시지를 숨깁니다. |
--no-header, --nh | CSV 출력의 헤더를 숨깁니다. 여러 서버에서 실행할 때 권장됩니다. |
--imd5 value, --im value | MD5 해시를 무시합니다. 자세한 내용은 무시 목록 섹션을 참조하세요. |
--ipath value, --ip value | 파일 경로 매칭을 무시합니다. 자세한 내용은 무시 목록 섹션을 참조하세요. |
--icimage value, --ic value | 컨테이너 이미지 매칭을 무시합니다. 자세한 내용은 무시 목록 섹션을 참조하세요. |
--print-headers, --ph | CSV 헤더만 출력합니다. 여러 서버에서 Log4Shell Sentinel을 실행하고 결과를 집계할 때 일반적으로 헤더 출력을 비활성화합니다. 이 명령은 헤더만 출력하므로 Excel 시트에 추가할 수 있습니다. |