리눅스 ELF 악성코드 분석은(는) 무엇을 하나요?
사용 시기
- 리눅스 서버나 컨테이너가 침해되었고 의심스러운 ELF 바이너리가 발견된 경우
- 리눅스 봇넷(Mirai, Gafgyt, XorDDoS), 암호화폐 채굴기, 랜섬웨어 분석 시
- 클라우드 인프라, Docker 컨테이너, Kubernetes 파드를 대상으로 하는 악성코드 조사 시
- 리눅스 루트킷 및 커널 모듈 역공학 시
- Linux x86_64, ARM, MIPS 아키텍처용으로 컴파일된 크로스 플랫폼 악성코드 분석 시
Windows PE 바이너리 분석에는 사용하지 마십시오. Windows 악성코드는 PEStudio, Ghidra, IDA를 사용하세요.
사전 조건
- 디스어셈블리 및 디컴파일을 위한 Ghidra 또는 IDA(리눅스 ELF 지원)
- 개발 도구가 설치된 리눅스 분석 VM(Ubuntu 22.04 권장)
- 동적 분석 및 디버깅을 위한 strace, ltrace, GDB
- 정적 검사를 위한 GNU binutils의 readelf, objdump, nm
- 빠른 바이너리 분류 및 스크립트 기반 분석을 위한 Radare2
- 격리된 컨테이너 기반 악성코드 실행을 위한 Docker
워크플로우
1단계: ELF 바이너리 속성 식별
ELF 헤더와 기본 속성을 검사합니다:
# 파일 유형 식별
file 의심_바이너리
# 상세 ELF 헤더 분석
readelf -h 의심_바이너리
# 섹션 헤더
readelf -S 의심_바이너리
# 프로그램 헤더(세그먼트)
readelf -l 의심_바이너리
# 심볼 테이블(스트립되지 않은 경우)
readelf -s 의심_바이너리
nm 의심_바이너리 2>/dev/null
# 동적 링킹 정보
readelf -d 의심_바이너리
ldd 의심_바이너리 2>/dev/null # 아키텍처가 일치하는 경우에만!
# 해시 계산
md5sum 의심_바이너리
sha256sum 의심_바이너리
# 패킹/UPX 확인
upx -t 의심_바이너리
# Python 기반 ELF 분석
from elftools.elf.elffile import ELFFile
import hashlib
with open("의심_바이너리", "rb") as f:
data = f.read()
sha256 = hashlib.sha256(data).hexdigest()
with open("의심_바이너리", "rb") as f:
elf = ELFFile(f)
print(f"SHA-256: {sha256}")
print(f"클래스: {elf.elfclass}-비트")
print(f"엔디언: {'리틀' if elf.little_endian else '빅'}")
print(f"머신: {elf.header.e_machine}")
print(f"유형: {elf.header.e_type}")
print(f"엔트리 포인트: 0x{elf.header.e_entry:X}")
# 스트립 여부 확인
symtab = elf.get_section_by_name('.symtab')
print(f"스트립됨: {'예' if symtab is None else '아니오'}")
# 섹션 엔트로피 분석
import math
from collections import Counter
for section in elf.iter_sections():
data = section.data()
if len(data) > 0:
entropy = -sum((c/len(data)) * math.log2(c/len(data))
for c in Counter(data).values() if c > 0)
if entropy > 7.0:
print(f" [!] 고엔트로피 섹션: {section.name} ({entropy:.2f})")
2단계: 문자열 및 침해 지표 추출
내장된 IOC 및 기능 관련 단서를 검색합니다:
# ASCII 문자열
strings 의심_바이너리 > strings_output.txt
# 네트워크 지표 검색
grep -iE "(http|https|ftp)://" strings_output.txt
grep -iE "([0-9]{1,3}\.){3}[0-9]{1,3}" strings_output.txt
grep -iE "[a-zA-Z0-9.-]+\.(com|net|org|io|ru|cn)" strings_output.txt
# 셸 명령어 검색
grep -iE "(bash|sh|wget|curl|chmod|/tmp/|/dev/)" strings_output.txt
# 암호화폐 채굴 지표 검색
grep -iE "(stratum|xmr|monero|pool\.|mining)" strings_output.txt
# SSH/자격 증명 도용 검색
grep -iE "(ssh|authorized_keys|id_rsa|shadow|passwd)" strings_output.txt
# 지속성 메커니즘 검색
grep -iE "(crontab|systemd|init\.d|rc\.local|ld\.so\.preload)" strings_output.txt
# FLOSS를 사용한 난독화 문자열 추출(사용 가능한 경우)
floss 의심_바이너리
3단계: 시스템 콜 및 라이브러리 사용 분석
악성코드가 사용하는 시스템 콜과 라이브러리를 식별합니다:
# 임포트된 함수 목록(동적 링크)
readelf -r 의심_바이너리 | grep -E "socket|connect|exec|fork|open|write|bind|listen"
# 실행 중 시스템 콜 추적(격리된 VM에서만)
strace -f -e trace=network,process,file -o strace_output.txt ./의심_바이너리
# 라이브러리 콜 추적
ltrace -f -o ltrace_output.txt ./의심_바이너리
# 주의 깊게 볼 주요 시스템 콜:
# 네트워크: socket, connect, bind, listen, accept, sendto, recvfrom
# 프로세스: fork, execve, clone, kill, ptrace
# 파일: open, read, write, unlink, rename, chmod
# 지속성: inotify_add_watch(파일 모니터링)
4단계: GDB를 이용한 동적 분석
악성코드를 디버깅하여 런타임 동작을 관찰합니다:
# GDB로 바이너리 시작
gdb ./의심_바이너리
# 주요 함수에 중단점 설정
(gdb) break main
(gdb) break socket
(gdb) break connect
(gdb) break execve
(gdb) break fork
# 실행 및 분석
(gdb) run
(gdb) info registers # 레지스터 상태 보기
(gdb) x/20s $rdi # 문자열 인자 검사
(gdb) bt # 백트레이스
(gdb) continue
# 스트립된 바이너리의 경우 엔트리 포인트에 중단점 설정
(gdb) break *0x400580 # readelf로 확인한 엔트리 포인트
(gdb) run
# 실행 중 네트워크 연결 모니터링
# 다른 터미널에서:
ss -tlnp # 리스닝 소켓 목록
ss -tnp # 설정된 연결 목록
5단계: Ghidra로 역공학
ELF 바이너리에 대한 심층 코드 분석을 수행합니다:
Ghidra를 이용한 리눅스 ELF 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 임포트: 파일 -> 임포트 -> ELF 바이너리 선택
- Ghidra가 ELF 형식과 아키텍처를 자동 감지
- 기본 분석 옵션 수락
2. 주요 분석 대상:
- main() 함수(또는 스트립된 경우 엔트리 포인트)
- 소켓 생성 및 연결 함수
- 명령 디스패치 로직(수신 데이터의 switch/case)
- 암호화/인코딩 루틴
- 지속성 설치 코드
- 자가 전파/스캔 함수
3. Mirai 계열 봇넷의 경우 다음을 찾으세요:
- 무차별 대입용 자격 증명 목록(telnet/SSH)
- 공격 모듈 선택(UDP 플러드, SYN 플러드, ACK 플러드)
- 스캐너 모듈(취약한 장치 포트 스캔)
- 킬러 모듈(경쟁 봇넷 제거)
4. 암호화폐 채굴기의 경우 다음을 찾으세요:
- 마이닝 풀 연결(stratum 프로토콜)
- 지갑 주소 문자열
- CPU/GPU 활용 함수
- 프로세스 은닉 기술
6단계: 리눅스 특화 지속성 분석
지속성 메커니즘을 확인합니다:
# LD_PRELOAD 루트킷 확인
strings 의심_바이너리 | grep "ld.so.preload"
# 악성코드가 /etc/ld.so.preload에 쓰면 모든 동적 라이브러리 콜을 후킹할 수 있음
# crontab 지속성 확인
strings 의심_바이너리 | grep -i "cron"
# systemd 서비스 생성 확인
strings 의심_바이너리 | grep -iE "systemd|\.service|systemctl"
# init 스크립트 생성 확인
strings 의심_바이너리 | grep -iE "init\.d|rc\.local|update-rc"
# SSH 키 주입 확인
strings 의심_바이너리 | grep -i "authorized_keys"
# 커널 모듈(루트킷) 로딩 확인
strings 의심_바이너리 | grep -iE "insmod|modprobe|init_module"
# 프로세스 은닉 확인
strings 의심_바이너리 | grep -iE "proc|readdir|getdents"
주요 개념
| 용어 | 정의 |
|---|---|
| ELF(Executable and Linkable Format) | 리눅스 실행 파일, 공유 라이브러리, 코어 덤프의 표준 바이너리 형식으로 헤더, 섹션, 세그먼트를 포함 |
| 스트립드 바이너리 | 디버그 심볼이 제거된 ELF 바이너리로, 함수 이름이 사라져 역공학이 더 어려워짐 |
| LD_PRELOAD | 다른 모든 라이브러리보다 먼저 로드할 공유 라이브러리를 지정하는 리눅스 환경 변수. 루트킷이 시스템 라이브러리 콜을 가로채는 데 악용됨 |
| strace | 프로세스가 수행한 모든 시스템 콜과 시그널을 기록하는 리눅스 시스템 콜 추적기로, 파일, 네트워크, 프로세스 작업을 파악 |
| GOT/PLT | Global Offset Table과 Procedure Linkage Table. 동적 링킹을 위한 ELF 구조로, 함수 후킹에 하이재킹될 수 있음 |
| 정적 링크 | 모든 라이브러리 코드가 포함된 바이너리. IoT 악성코드에서 일치하는 공유 라이브러리가 없는 시스템에서 실행하기 위해 흔히 사용 |
| Mirai | telnet 무차별 대입으로 IoT 장치를 표적으로 삼는 대표적인 리눅스 봇넷. 소스 코드가 유출되어 많은 변종이 생겨남 |
도구 및 시스템
- Ghidra: NSA 역공학 도구로, x86, x86_64, ARM, MIPS 및 기타 리눅스 아키텍처에 대한 완전한 ELF 지원
- Radare2: 명령줄 인터페이스를 갖춘 오픈소스 역공학 프레임워크로 빠른 바이너리 분석 및 스크립팅에 적합
- strace: 파일, 네트워크, 프로세스 작업을 포함한 바이너리 동작 관찰을 위한 리눅스 시스템 콜 추적 도구
- GDB: GNU 디버거로 중단점 설정, 메모리 검사, 리눅스 바이너리 실행 단계별 분석 지원
- pyelftools: 자동화된 분석 파이프라인을 위해 ELF 파일을 프로그래밍 방식으로 파싱하는 Python 라이브러리
일반 시나리오
시나리오: 침해된 리눅스 서버에서 발견된 암호화폐 채굴기 분석
상황: 클라우드 서버에서 CPU 사용률이 100%를 기록했습니다. 조사 결과 /tmp에서 의심스러운 이름의 알 수 없는 바이너리가 실행 중이었습니다. 이 바이너리가 암호화폐 채굴기인지 확인하고 공격자의 지갑과 풀을 식별해야 합니다.
접근법:
- 바이너리를 분석 VM으로 복사하고 SHA-256 해시 계산
file과readelf를 실행하여 아키텍처와 링크 유형 식별- 문자열 추출 및 마이닝 풀 주소(stratum+tcp://)와 지갑 주소 검색
- 샌드박스에서 strace로 실행하여 네트워크 연결(마이닝 풀 연결) 관찰
- Ghidra로 임포트하여 마이닝 알고리즘과 설정 추출 식별
- 지속성 메커니즘(crontab, systemd 서비스, SSH 키) 확인
- 풀 주소, 지갑, 업데이트용 C2, 지속성 아티팩트 등 모든 IOC 문서화
주의사항:
- 샌드박스 외부에서 악성코드에 대해
ldd실행(ldd가 바이너리의 코드를 실행할 수 있음) - x86_64 실행 전 ARM/MIPS 아키텍처 확인 누락
- 지속성과 정리를 처리할 수 있는 동반 스크립트(.sh 파일) 간과
- 초기 침입 경로(채굴기가 어떻게 배포되었는지: SSH 무차별 대입, 웹 익스플로잇, 컨테이너 탈출) 무시
출력 형식
리눅스 ELF 악성코드 분석 보고서
====================================
파일: /tmp/.X11-unix/.rsync
SHA-256: e3b0c44298fc1c149afbf4c8996fb924...
유형: ELF 64비트 LSB 실행 파일, x86-64
링크: 정적 링크(모든 라이브러리 포함)
스트립됨: 예
크기: 2,847,232 바이트
패커: UPX 3.96(분석을 위해 언패킹됨)
분류
계열: XMRig 암호화폐 채굴기(변형)
변종: C2 업데이트 메커니즘이 포함된 커스텀 빌드
기능
[*] RandomX 알고리즘을 통한 XMR(모네로) 채굴
[*] 작업 제출을 위한 Stratum 풀 연결
[*] 설정 업데이트를 위한 C2 체크인
[*] 프로세스 이름 위장(argv[0] = "[kworker/0:0]")
[*] 경쟁 프로세스 제거(다른 채굴기 종료)
[*] 재접근을 위한 SSH 키 주입
네트워크 지표
마이닝 풀: stratum+tcp://pool.minexmr[.]com:4444
C2 서버: hxxp://update.malicious[.]com/config
지갑: 49jZ5Q3b...Monero_Wallet_Address...
지속성
[1] Crontab 항목: */5 * * * * /tmp/.X11-unix/.rsync
[2] /root/.ssh/authorized_keys에 SSH 키 추가
[3] Systemd 서비스: /etc/systemd/system/rsync-daemon.service
[4] 프로세스 은닉을 위해 /etc/ld.so.preload 수정
프로세스 은닉
LD_PRELOAD: /usr/lib/.libsystem.so
Hook: readdir()를 후킹하여 ls에서 /tmp/.X11-unix/.rsync 숨김
Hook: fopen()을 후킹하여 /proc/*/maps 읽기에서 숨김