카테고리 없음

[토스뱅크 부트캠프] 팀프로젝트 Interrupt 3주차

yejiamoe 2026. 1. 7. 00:00

안녕하세요 오랜만이죠..?

왜냐면 너무 바빠서 프로젝트가 다 끝난 후에야 제대로 정라하기 시작했습니다ㅋㅋㅋㅋㅋㅋㅋㅋ

(사실 뭐 핑계인데..실제로 좀 바쁘기도 했어요..)

 

정~~~말 예전일이라 기억이 가물가물한데 잘 살려서 정리해보겠습니다^^

 

 

먼저 저번주까지 기본적인 인프라 설정에 많이 힘을 쏟았습니다. 

Zeek와 Arkime을 설치하고 추가로 Elastic APM 까지 설치하였습니다. 음..이 부분은 제가 프로젝트가 다 끝났기 때문에 할 수 있는 말인것 같은데.. 팀을 구성할 때부터 프로젝트 진행 방식에 대해서도 팀원들과 이야기를 많이 해보거나 팀 구성 변동이 불가피한 상황에서는 팀 빌딩 이후에 프로젝트 진행 방식을 이야기 하는게 중요한 것 같습니다. 저희 팀이 조금 아쉬웠던 부분 중 하나인데, 주제는 제대로 정해졌지만 그 세부사항이 정확히 픽스되고 공유되지 않았던 것 같습니다. (저 혼자만의 생각일수도..)

조금 TMI가 될 수 있지만 기록용이니까..몇자 적어보자면.. 저희조는 인원도 적을 뿐더러 팀원들마다 진행방식 자체에 차이가 있었던것 같습니다. 저는 주제가 정해졌으면 조사를 통해 정말 기초 단계에서부터 자세히 틀을 잡고 가고 싶었는데 그렇게 안하는..? 팀원도 있어서 처음에는 많이 당황했던 것 같습니다. 결국 뭐 그 중간 지점을 찾는게 제일 좋은 것 같긴 합니다. 조사에 너무 치우치진 않게 직접 해가면서 나아가는 느낌이 딱 좋은 것 같긴 합니다. 팀프로젝트다 보니 각 파트별로 역할이 나뉘어질 수 있고, 그에 따라 제가 진행하는 부분은 다른 팀원이 모르고, 다른 팀원이 진행하는 부분은 제가 모를 수도 있습니다.  '팀'프로젝트이다 보니 당연한 결과일 것이고 이에 대해 '공유' 하는 시간이 전 중요하다고 생각했습니다. 뭐 회사에 가면 다를 수도 있고 더 중요할 수도 있어서 회사는 어떨지 모르겠지만 특히나 저는 교육에서 팀프로젝트를 진행하는 것이기 때문에 저는 많이 배우고 싶어서 더 원했던 것 같습니다. 뭐...끝까지 이뤄지진 않았습니다..이에 대해서는 흠 저는 충분히 노력해서 의사전달을 했는데 소통이 안된 경우라서 (생략하겠습니다..) 어쨋든 다음에 만약 이런 프로젝트를 하게 된다면 팀빌딩이후 초반 단계에서 다른 팀원들과 충분히 의사소통을 하고 진행을 하고 싶다는 생각을 하게 되었습니다.

 

상관분석 설계

제가 이런 글을 적게 된 이유도...처음 주제를 1주차에 설명한 것 과 같이 '상관분석'으로 잡았습니다. 저는 이 단계에서 정확히 어느 레벨에서 어떤 데이터들을 출력해서 각 레벨별로 어느 부분을 비교할 것인가를 확실하게 정리하고 진행하고 싶었는데 실현되지 못했던 것 같습니다. 그래서 저 혼자 생각하고 정리하는 스타일이여서 더 불안해서 좀 불만이 생겼던 것 같습니다.. 저희가 React2Shell을 선정한 이유에는 'Zero-day Attack'도 고려한 상황이였습니다. 앞에서 설명한 것과 같이 역직렬화 공격은 네트워크 레벨에서 잡는게 불가능하기 때문에 심층분석을 해야하고 그 방법으로 저희는 상관분석을 선택한 것입니다. (네트워크에서 잡을 수 있는거는 misp등에서 이미 공격IP로 판별되어 차단하는 룰에 걸리는 것이기 때문에 새로운 공격일 경우 소용이 없기 때문) 그래서  상과분석을 하기 위해 Arkime, Zeek, Elastic APM을 이용하는게 어떨까라는 생각을 하였습니다. 여기서 각각의 역할은 다음과 같습니다.

 

1. Arkime (전체 패킷 기록)

  • 공격자가 보낸 payload 보관 역할
  • 취약점을 뚫기 위해 보낸 특수문자나 바이너리 파일을 나중에 다시 꺼내서 분석하는 과정에서 사용

2. Zeek (메타 데이터 요약)

 

  • 대량의 데이터 속에서 침해지표(IoC)를 빠르게 찾기 위한 역할
  • 수많은 패킷 중에서 공격자가 어떤 파일을 다운로드했는지/어떤 ip와 연결되어있는지 와 같은 핵심 정보만 요약

3. Elastic APM (코드 레벨 추적)

  • 취약한 코드가 실행되는 순간의 함수 호출 스택 출력 역할
  • 네트워크 패킷이 들어왔을 때, 뭐 자바의 어떤 클래스가 반응해서 쉘이 실행되는지 그 내부 동작을 증명하는 도구

여기서 중요한 것들은 이 탐지 장비들을 어떻게 이을것인가였습니다.

각 장비를 이용해서 어떤 내용을 수집하고, 그 수집하는 데이터들을 어떻게 이어서 하나의 흐름을 만들것인가에 대해서 많이 고민하고 찾아봤던 것 같습니다. 제가 생각한 방법은 다음과 같습니다.

단계 구분 (Tool) 주요 출력 로그 (Key Logs) 데이터 전송 및 연동 핵심 필요성 (Context)
1. Packet Zeek & Arkime Src IP, Dst Port, SSL 정보, JA4 지문, 전체 패킷(PCAP) Zeek 로그 → Wazuh 전송 / Arkime → 별도 저장 및 Wazuh에서 링크 참조 공격자의 유입 경로 및 외부 C2 서버와의 통신 여부 증명 (Network Level)
2. WAF Mod
Security
악성 Payload, Trace-ID, HTTP Method audit.log → Wazuh 에이전트가 실시간 수집 공격자의 기술적 의도와 어떤 취약점(CVE)을 타겟팅했는지 분류
3. Trigger Elastic
APM
호출 함수(Function), Trace-ID, Error Stack Trace APM Agent → Elasticsearch / Wazuh 통합 분석 공격 페이로드가 실제 애플리케이션의 취약한 함수를 실행시켰는지 확인 (성공 여부 판별)
4. Execution Falco 프로세스명(sh, nc), PID, 열린 포트, 시스템 콜 커널 이벤트 → Wazuh Manager 전송 취약점 실행 후, 공격자가 서버 내부에서 실제 명령어를 수행했음을 커널 수준에서 확정
5. Response Wazuh
Server
상관분석(Correlation), 시각화, MITRE Mapping 위 4단계의 모든 로그 병합 및 분석 개별 로그를 하나의 침해 시나리오로 완성하고, 분석 보고서 생성 및 자동 대응

 

[1단계 : Packet (Zeek & Arkime)] -  침투 경로 기록

출력 로그

1. Zeek

  • Src IP : 접속지의 IP
  • Dst Port : 사용 포트
  • SSL 인증서 정보
  • JA4 지문 : 공격 도구 식별

2. Arkime

  • 공격자가 보낸 전체 패킷 데이터 (PCAP)

-> 데이터 전송 : zeek의 텍스트 로그는 wazuh(SIEM)으로, Arkime은 자체 서버에 패킷 저장하고 필요할 때 wazuh에서 링크 조회 (링크 되는지 확인해야함..)

-> 필요성 : 공격자가 어떤 통로로 들어와서 어떤 외부서버와 통신하고 있는지 네트워크 관점에서 증명

 

[2단계 : WAF (ModSecurity)] - 공격 의도 파악

출력 로그

  • Payload : HTTP 요청 내부의 악성 페이로드
  • Trace-ID : 요청 식별하는 특정 ID

-> 데이터 전송 : Modsecurity의 audit.log파일을 wazuh(SIEM)으로

-> 필요성 : 방어는 안하지만 공격자가 어떤 취약점을 노리고 어떤 값(ID)를 던졌는지 기술적으로 분류

 

[3단계 : Trigger (Elastic APM)] - 코드 실행 여부 증명

출력 로그

  • Function : 특정 함수 호출
  • Trace-ID : 애플리케이션 내부에서의 trace id
  • Error Stack Trace

-> 데이터 전송 : APM Agent로그를 Elasticsearch/Wazuh로 전송

-> 필요성 : 네트워크로 들어온 공격 페이로드가 실제 서버의 취약점 함수를 건드렸는가를 확인해서 그 공격이 성공했는지 실패했는지 확인

 

[4단계 : Execution (Falco)] - 최종 침해 증명

출력 로그

  • Comm : sh, nc로 생성된 프로세스 이름
  • PID
  • 열린 포트

- 데이터 전송 : 커널 레벨에서 발생하는 이벤트를 Wazuh(SIEM)으로

- 필요성 : 취약점이 터진 후, 공격자가 실제로 서버에서 명령어를 실행했는지 커널 수준에서 확정 짓는 최종 증거

 

[5단계 : Response (Wazuh Server)] - 상관분석 및 시각화

  • 역할 : 위 4단계의 모든 로그를 하나로 병합
  • 분석 방법
    • Timestamp : 동일 시간대에서 발생한 이벤트 정렬
    • IP 주소 : 동일한 Src IP에서 발생한 네트워크 활동 (Zeek)와 공격 시도(WAF)매칭
    • Trace-ID : WAF에서 탐지된 페이로드와 APM에서 실행된 함수를 1:1 로 매핑
    • Agent-ID : victim에서 일어난 모든 일을 그룹화

이렇게 5단계로 정리했습니다. 좀 더 자세히 설명할 수 있지만 나중에 다 안쓰게 되어서(^^) 자세한 설명은 패스 하겠습니다. 

 

디지털 포렌식 - 침해사고 서버분석(log4j)

이때 아마 멘토님 피드백이 있고 일단 인프라는 어느정도 마무리가 되었으니까 다같이 java의 log4shell에 대해서 서버분석을 같이 해보자라는 의견이 있었습니다. 기존에는 java, php, node.js 3개를 각각 한명씩 맡았는데 서버분석은 다같이 해보았습니다. 저도 분석이 처음이라 열심히 공부하면서 해보았습니다...!

 

먼저 저도 제가 이 서버분석을 하면서 이런게 포렌식인가..?라는 생각을 했습니다.ㅎㅎ 저도 처음이다 보니...

포렌식은 다양한 영역이 있습니다. 네트워크포렌식, 라이브포렌식, 애플레키이션포렌식, 안티포렌식 등이 있습니다. 네트워크 포렌식은 네트워크 통신을 통해 이동하는 데이터를 포착하고 분석해서 침입 경로와 유출 정보를 확인하는 과정입니다. 우리가 흔히 아는 wireshark같은 프로그램으로 패킷단위로 데이터를 저장해서 분석하는 것입니다. 라이브 포렌식은 전원이 켜져 있는 시스템에서만 확인할 수 있는 휘발성 데이터(메모리, 실행중인 프로세스 등)를 추출하는 활동으로 우리가 netstat나 ps명령어로 실시간 네트워크 연결 상태나 악성 프로세스 목록을 보면서 메모리에 남아있는 암호화키 같은 거를 확보하는 활동입니다. 애플리케이션 포렌식은 서버 내 설치된 특정 sw가 남긴 로그와 데이터를 분석하여 비정상적인 행위를 찾는 것입니다. log4j의 로그에서 에러 메시지나 공격 구문을 찾거나 웹 서버의 access.log를 뒤지는 분석입니다. 그다음이 안티 포렌식 대응인데  공격자가 자신의 흔적을 지우가나 숨기려고 한 행위를 파악하고 이를 우회해서 복구하는 활동입니다. 이때 docker diff 명령어를 통해서 파일 시스템의 변경점을 찾아내는 방법을 저는 이용했습니다. 

 

제가 log4j 를 분석한 과정은 다음과 같습니다.

 

1단계: 네트워크 유입 및 페이로드 분석 (L7)

→ 공격자가 어떤 경로로 어떤 형태의 공격 구문을 던졌는지 확인

  • 로그 데이터 추출 및 탐색
# 컨테이너의 모든 로그를 log4j_analysis.log 파일로 저장
docker logs 94d2c2f536ac > log4j_analysis.log 2>&1

분석 내용 : 페이로드 위치 로그에 Received a request for API version이라는 문구 바로 뒤에 공격 구문이 붙어 있음 공격자는 HTTP 요청 헤더 중 X-Api-Version (혹은 이와 유사한 이름의 커스텀 헤더)에 공격 구문을 실어 보냈을 것.

  • 핵심 공격 구문 탐색
# 1. 기본적인 JNDI 호출 및 변칙 패턴(${) 검색
grep -E "\$\{|\{jndi:" log4j_analysis.log

분석 내용 : 공격자 정보 추출 악성 서버는 192.168.10.56 (또는 10.55) → 공격자가 미리 준비해둔 JNDI 서버. 최종 목적지는 192.168.10.61:4444 → 페이로드 마지막 부분에 있는 이 주소가 공격자가 서버의 권한을 획득한 뒤 연결 받은 자신의 PC(C2 PC)의 IP와 포트 번호임을 알 수 있음

  • 실제 공격 성공 여부 확인
# 컨테이너 내부에서 현재 외부(공격자)와 연결된 네트워크 세션이 있는지 확인
docker exec 94d2c2f536ac netstat -antp | grep ESTABLISHED

 

공격자의 IP(192.168.10.61)로 연결된 세션이 있기 때문에 리버스 쉘이 성공했다는 것을 알 수 있음

 

2단계 : 아웃바운드 콜백 탐지 (L4)

→ 서버가 공격자의 LDAP 서버로 접속을 시도 했던 네트워크 흔적 확보

→ 컨테이너 네트워크 소켓 상태, 어플리케이션 에러 로그 확인

  • 잔류 세션 및 실시간 연결 확인

: 공격이 성공했다면 서버는 공격자의 LDAP 서버(138포트)에 접속했거나 리버스쉘 송격을 통해서 공격자의 PC에 연결되었을 것

# 컨테이너 내부의 네트워크 소켓 상태 확인
# 1389, 389, 1099(LDAP/RMI) 뿐만 아니라 로그에서 확인된 4444, 9001 포트도 함께 검색합니다.
docker exec 94d2c2f536ac ss -antp | grep -E "1389|389|1099|4444|9001"

# ss도구가 없어서 다른 방법으로 
# 컨테이너 내부에서 수동으로 확인 시
docker exec 94d2c2f536ac cat /proc/net/tcp

첫 번째 연결 (sl 0): 020011AC:D434 → 3D0AA8C0:115C (공격자) 내 서버: 172.17.0.2 (Docker 내부 IP) 공격자: 192.168.10.61 포트: 4444 (16진수 115C = 10진수 4444) 상태(st): 01 (ESTABLISHED, 연결 완료) 두 번째 연결 (sl 1): 020011AC:D2CA → 380AA8C0:115C (공격자) 공격자: 192.168.10.56 포트: 4444 상태(st): 01 (ESTABLISHED, 연결 완료)

현재 공격자(192.168.10.61, 192.168.10.56)가 리버스 쉘을 통해 victim 컨테이너에 실시간으로 접속해 있는 상태임을 알 수 있음

 

  • 로그 내 연결 실패 흔적 분석

: 공격자의 LDAP 서버가 일시적으로 꺼져 있었거나 네트워크가 차단된다면 자바 애플리케이션 로그에 통신 에러가 남음

# "LDAP" 또는 "JNDI" 관련 에러 메시지 정밀 수색
grep -Ei "LDAP|JNDI|Error looking up" log4j_analysis.log
# 에러 메시지만 따로 모아서 확인하기
grep -Ei "NamingException|Connection refused|Connection timed out" log4j_analysis.log

위 내용을 보면 두가지를 알 수 있다. 첫번째로는 NamingException&ClassCastException이다.

NamingException & ClassCastException (공격 성공의 신호)

로그 내용을 보면 서버가 공격자의 LDAP 서버에서 악성 자바 클래스(Exploit)를 다운로드해서 메모리에 올리는데 성공했다는 것을 확인할 수 있습니다. 마지막에 java.lang.ClassCastExeption~~하면서 행변환 오류가 있지만 공격은 이 에러가 나기 전에 이미 악성 코드를 실행하기 때문에 이 에러가 보여도 공격 코드는 이미 작동한 것입니다.

Connection timed out (연결 실패 흔적)

중간에 보면 Connection time out 이 있는데 이 로그는 이 시간대에 공격자가 던진 12.168.10.55:1389로 서버가 접속을 시도하였지만 실패했다는 뜻입니다. 아마 이때 LDAP 가 잠깐 꺼져 있었을 가능성이 높다고 생각하였고 연결 실패 흔적은 있지만 공격자의 IP가 잘 찍혀있기 때문에 페이로드 시행을 시도했다는 확증은 이미 있는 상태입니다.

 

  • 악성 클래스 다운로드 단계 확인

: Log4Shell 공격은 LDAP 접속→ 악성 Java 클래서 경로 수신 → HTTP로 클래스 다운로드 순서로 진행되기 때문에 보통 공격자들은 HTTP 서버를 8888포트 등으로 열어둠

# 80, 443, 8888 등 일반적인 웹 포트로 나간 이력이 있는지 확인
docker exec 94d2c2f536ac ss -antp | grep -E ":80 |:443 |:8888"

이에 대한 로그가 출력되지는 않았지만, 아마 파일 다운로드는 1초도 안걸리는 짧은 동작이라서 다운로드가 완료되는 순간 연결이 끊겨 목록에서 사라진 것이라고 생각했습니다. 즉, 사라진 것이지 시도가 없어진 것은 아니라고 판단하였습니다. 하지만 이 부분 역시 이전에 검증하였기 때문에 흔적은 없어도 됩니다. 

 

3단계 : 커널 레벨 실행 및 프로세스 트리 분석(OS)

→ 자바 프로세스가 비정상적인 자식프로세스를 실행했는지 증명

  • 프로세스 계층 구조 분석
# 컨테이너 내부의 프로세스 트리 확인
docker exec 94d2c2f536ac ps -ef --forest

# 컨테이너가 아주 가벼운 환경이라 나무모양으로 보여주는 기능이 없어서 
# 그냥 전체 목록 뽑아서 부모-자식 관계 찾아내기
docker exec 94d2c2f536ac ps -ef

침투 시작 (PID 1): 보안 취약점이 있는 Java 기반 애플리케이션이 실행 중입니다. 권한 획득 (PID 63): 공격자가 취약점을 이용해 시스템 명령을 내릴 수 있는 첫 번째 쉘(/bin/sh)을 실행했습니다. 외부 연결 (PID 130): 서버의 제어권을 공격자 PC(192.168.10.61:4444)로 넘겨주는 리버스 쉘 명령이 수행되었습니다. 완전 장악 (PID 131): 공격자가 자유롭게 명령어를 입력할 수 있는 대화형 쉘이 열려, 현재 실시간으로 조작 중인 상태입니다.

의심 프로세스는 총 3개를 탐지하였습니다. 리버스쉘 실행주체인 PID 130, 공격자가 사용한 인터렉티브 쉘인 PID 131, 중간 매개체인 PID 63을 확인하였습니다.

프로세스 실행 환경 같은 경우에는 권한 부분에서는 모든 의심 프로세스가 root사용자로 실행중임을 알 수 있고  부모 프로세스가 PID1이고 자식프로세스가 PID63에서 PID130 으로 이어진 것을 확인하였습니다. 

이와 관련하여서 추가적으로 정밀 분석을 하였습니다. 환경 변수, 실행 경로 등을 확인하기 위해 다음과 같은 추가 명령어를 실행시켰습니다. 

# 의심 프로세스의 실제 바이너리 경로 확인
docker exec 94d2c2f536ac ls -l /proc/130/exe

# 공격자가 설정한 환경 변수 확인 (비밀번호나 API 키 탈취 여부 확인)
docker exec 94d2c2f536ac cat /proc/130/environ

# 공격자가 현재 열어두고 있는 파일 확인
docker exec 94d2c2f536ac ls -l /proc/130/fd

/proc/130/exe 는 해당 프로세스가 실행중인 바이너리 파일을 가르킴

확인결과, /bin/bash를 가르킴. 즉, 다른 이름으로 위장한 프로그램이 아니라 시스템 표준 Bash 쉘을 그대로 사용중이라는 의미입니다. 

프로세스가 시작될 때 가지고 있던 환경 변수들로 보통 자식 프로세스는 부모 프로세스의 환경 변수를 상속 받음 JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk, JAVA_VERSION=8u181 등의 변수가 보임

이 Bash 쉘은 일반적인 로그인 과정을 거친게 아니라 취약한 java 애플리케이션 환경(PID1)의 환경을 그대로 물려받아 실행되었음을 입증할 수 있습니다. 즉, java 앱 내부에서 쉘이 터져나왔다는 증거 입니다. 

프로세스가 현재 열어두고 있는 파일이나 통신 경로를 보여줌 0 : 입력 / 1: 출력 / 2: 에러 0, 1, 2 번 모두가 pipe:[677787]와 같은 파이프로 연결되어 있음

정상적인 터미널 쉘이라면 /dev/pts/0과 같은 터미널 장치와 연결되어있어야 합니다. 즉, 모두 파이프로 연결되어 있다는 점은 이 쉘의 입출력이 네트워크 소켓(공격자의 4444포트)로 리다이렉션 되어있음을 물리적으로 증명합니다.

 

4단계 : 포렌식 및 흔적

  • 파일 시스템 변경점 추출 

: 컨테이너가 처음 실행된 시점 대비 현재 변경된 모든 파일 목록을 보여주는 포렌식

# 호스트 OS(VMware) 터미널에서 실행
docker diff 94d2c2f536ac

docker diff 결과에서 A (Added)는 새로 생성된 파일, C(Changed)는 수정된 파일

결정적인 증거는 크게 3가지로 나눌 수 있습니다.

1. A  /malware_test

- 루트 디렉토리에서 생성된 수상판 파일로 공격자가 시스템의 쓰기 권한을 확인하기 위해 테스트용으로 만들었거나 실제 악성 스크립트일 가능성이 매우 높음

2. A  /root/.bash_history

- 전 단계에서 확인한 리버스 쉘(bash -i)의 실행 기록이 담긴 파일로 이 파일이  생성되었다는 것은 이미 공격자가 여러 명령어를 입력했다는 증거

3. C /root/.ash_history

- 기본 쉘인 ash 에서도 명령어 기록이 수정되었음으로 공격자가 침투 초기 단계에서 기본 쉘을 만졌음을 알 수 있음

 

애플리케이션 및 임시파일들에 대해서는 다음과 같이 분석할 수 있습니다.

1. C /tmp, C /tmp/hsperfdata_root

- 자바 프로세스(PID1)가 실행되면서 생성되는 임시 데이터들로 이전에 확인한 ClassCastException 오류 당시 악성 클래스가 로드되었던 흔적이 이 디렉토리 어딘가에 남아있을 수도 있음

2. A /tmp/tomcat.8080... 계열

- Spring Boot 애플리케이션이 내장 톰캣을 사용하면서 생성한 작업 파일들로 공격 과정에서 생성된 임시 서블릿 파일이나 세션 정보가 포함되어 있을 가능성 존재

 

위 분석 과정에서 의심스러운것들을 직접 열어보자면,

# 공격자가 친 명령어 전부 확인하기
docker exec 94d2c2f536ac cat /root/.bash_history

# 생성된 의심 파일의 정체 확인하기
docker exec 94d2c2f536ac cat /malware_test

 

1. /root/.bash_history

- 공격자가 리버스쉘로 접속해서 실제로 입력한 명령어 기록

2. /malware_test

- 공격자가 wget을 통해 파일을 성공적으로 가져왔음을 입증

 

추가로 도커파일 안의 malware_test 파일을 내용도 확인 가능합니다. (저희가 생성한 파일이라서 I am Malware라고 출력되는 상태입니다.)