리눅스 시스템 아티팩트 분석은(는) 무엇을 하나요?
사용 시기
- 손상된 리눅스 서버 또는 워크스테이션을 조사할 때
- 지속성 메커니즘(cron, systemd, SSH 키) 식별 시
- 셸 히스토리와 인증 로그를 통해 사용자 활동 추적 시
- 리눅스 기반 침해의 범위를 파악하기 위한 침해 대응 중
- 루트킷, 백도어, 무단 변경 사항 탐지 시
전제 조건
- 포렌식 이미지 또는 리눅스 시스템에 대한 라이브 접근(읽기 전용)
- 리눅스 파일 시스템 계층 구조(FHS) 이해
- 일반적인 리눅스 로깅 위치(/var/log/)에 대한 지식
- 도구: chkrootkit, rkhunter, AIDE, auditd 로그
- systemd, cron, PAM 구성에 대한 숙련도
- 완전한 아티팩트 수집을 위한 루트 접근 권한
워크플로우
1단계: 시스템 아티팩트 마운트 및 수집
# 포렌식 이미지를 읽기 전용으로 마운트
mount -o ro,loop,offset=$((2048*512)) /cases/case-2024-001/images/linux_evidence.dd /mnt/evidence
# 수집 디렉토리 생성
mkdir -p /cases/case-2024-001/linux/{logs,config,users,persistence,network}
# 인증 로그 수집
cp /mnt/evidence/var/log/auth.log* /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/secure* /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/syslog* /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/kern.log* /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/audit/audit.log* /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/wtmp /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/btmp /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/lastlog /cases/case-2024-001/linux/logs/
cp /mnt/evidence/var/log/faillog /cases/case-2024-001/linux/logs/
# 사용자 아티팩트 수집
for user_dir in /mnt/evidence/home/*/; do
username=$(basename "$user_dir")
mkdir -p /cases/case-2024-001/linux/users/$username
cp "$user_dir"/.bash_history /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp "$user_dir"/.zsh_history /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp -r "$user_dir"/.ssh/ /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp "$user_dir"/.bashrc /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp "$user_dir"/.profile /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp "$user_dir"/.viminfo /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp "$user_dir"/.wget-hsts /cases/case-2024-001/linux/users/$username/ 2>/dev/null
cp "$user_dir"/.python_history /cases/case-2024-001/linux/users/$username/ 2>/dev/null
done
# 루트 사용자 아티팩트 수집
cp /mnt/evidence/root/.bash_history /cases/case-2024-001/linux/users/root/ 2>/dev/null
cp -r /mnt/evidence/root/.ssh/ /cases/case-2024-001/linux/users/root/ 2>/dev/null
# 시스템 구성 수집
cp /mnt/evidence/etc/passwd /cases/case-2024-001/linux/config/
cp /mnt/evidence/etc/shadow /cases/case-2024-001/linux/config/
cp /mnt/evidence/etc/group /cases/case-2024-001/linux/config/
cp /mnt/evidence/etc/sudoers /cases/case-2024-001/linux/config/
cp -r /mnt/evidence/etc/sudoers.d/ /cases/case-2024-001/linux/config/
cp /mnt/evidence/etc/hosts /cases/case-2024-001/linux/config/
cp /mnt/evidence/etc/resolv.conf /cases/case-2024-001/linux/config/
cp -r /mnt/evidence/etc/ssh/ /cases/case-2024-001/linux/config/
2단계: 사용자 계정 및 인증 분석
# 사용자 계정 이상 징후 분석
python3 << 'PYEOF'
print("=== 사용자 계정 분석 ===\n")
# /etc/passwd 파싱
with open('/cases/case-2024-001/linux/config/passwd') as f:
for line in f:
parts = line.strip().split(':')
if len(parts) >= 7:
username, _, uid, gid, comment, home, shell = parts[0], parts[1], int(parts[2]), int(parts[3]), parts[4], parts[5], parts[6]
# UID 0 계정 플래그 (루트 권한)
if uid == 0 and username != 'root':
print(f" 경고: UID 0 계정: {username} (셸: {shell})")
# 로그인 셸을 가져서는 안 될 계정 플래그
if shell not in ('/bin/false', '/usr/sbin/nologin', '/bin/sync') and uid >= 1000:
print(f" 사용자: {username} (UID:{uid}, 셸:{shell}, 홈:{home})")
# 로그인 셸을 가진 시스템 계정 플래그
if uid < 1000 and uid > 0 and shell in ('/bin/bash', '/bin/sh', '/bin/zsh'):
print(f" 경고: 셸을 가진 시스템 계정: {username} (UID:{uid}, 셸:{shell})")
# /etc/shadow 파싱하여 계정 상태 확인
print("\n=== 비밀번호 상태 ===")
with open('/cases/case-2024-001/linux/config/shadow') as f:
for line in f:
parts = line.strip().split(':')
if len(parts) >= 3:
username = parts[0]
pwd_hash = parts[1]
last_change = parts[2]
if pwd_hash and pwd_hash not in ('*', '!', '!!', ''):
hash_type = '알 수 없음'
if pwd_hash.startswith('$6$'): hash_type = 'SHA-512'
elif pwd_hash.startswith('$5$'): hash_type = 'SHA-256'
elif pwd_hash.startswith('$y$'): hash_type = 'yescrypt'
elif pwd_hash.startswith('$1$'): hash_type = 'MD5 (취약)'
print(f" {username}: {hash_type} 해시, 마지막 변경: 일 {last_change}")
PYEOF
# 로그인 기록 분석
last -f /cases/case-2024-001/linux/logs/wtmp > /cases/case-2024-001/linux/analysis/login_history.txt
lastb -f /cases/case-2024-001/linux/logs/btmp > /cases/case-2024-001/linux/analysis/failed_logins.txt 2>/dev/null
3단계: 지속성 메커니즘 검사
# 모든 사용자의 cron 작업 확인
echo "=== CRON 작업 ===" > /cases/case-2024-001/linux/persistence/cron_analysis.txt
# 시스템 cron
for cronfile in /mnt/evidence/etc/crontab /mnt/evidence/etc/cron.d/*; do
echo "--- $cronfile ---" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt
cat "$cronfile" 2>/dev/null >> /cases/case-2024-001/linux/persistence/cron_analysis.txt
echo "" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt
done
# 사용자 crontab
for cronfile in /mnt/evidence/var/spool/cron/crontabs/*; do
echo "--- 사용자 crontab: $(basename $cronfile) ---" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt
cat "$cronfile" 2>/dev/null >> /cases/case-2024-001/linux/persistence/cron_analysis.txt
echo "" >> /cases/case-2024-001/linux/persistence/cron_analysis.txt
done
# 지속성 확인을 위한 systemd 서비스 검사
echo "=== SYSTEMD 서비스 ===" > /cases/case-2024-001/linux/persistence/systemd_analysis.txt
find /mnt/evidence/etc/systemd/system/ -name "*.service" -newer /mnt/evidence/etc/os-release \
>> /cases/case-2024-001/linux/persistence/systemd_analysis.txt
for svc in /mnt/evidence/etc/systemd/system/*.service; do
echo "--- $(basename $svc) ---" >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt
cat "$svc" >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt
echo "" >> /cases/case-2024-001/linux/persistence/systemd_analysis.txt
done
# 인증된 SSH 키 확인 (백도어 탐지)
echo "=== SSH 인증 키 ===" > /cases/case-2024-001/linux/persistence/ssh_keys.txt
find /mnt/evidence/home/ /mnt/evidence/root/ -name "authorized_keys" -exec sh -c \
'echo "--- {} ---"; cat {}; echo ""' \; >> /cases/case-2024-001/linux/persistence/ssh_keys.txt
# rc.local 및 init 스크립트 확인
cat /mnt/evidence/etc/rc.local 2>/dev/null > /cases/case-2024-001/linux/persistence/rc_local.txt
# /etc/profile.d/에서 로그인 트리거 스크립트 확인
ls -la /mnt/evidence/etc/profile.d/ > /cases/case-2024-001/linux/persistence/profile_scripts.txt
# LD_PRELOAD 하이재킹 확인
grep -r "LD_PRELOAD" /mnt/evidence/etc/ 2>/dev/null > /cases/case-2024-001/linux/persistence/ld_preload.txt
cat /mnt/evidence/etc/ld.so.preload 2>/dev/null >> /cases/case-2024-001/linux/persistence/ld_preload.txt
4단계: 셸 히스토리 및 명령 실행 분석
# 각 사용자의 bash 히스토리 분석
python3 << 'PYEOF'
import os, glob
print("=== 셸 히스토리 분석 ===\n")
suspicious_commands = [
'wget', 'curl', 'nc ', 'ncat', 'netcat', 'python -c', 'python3 -c',
'perl -e', 'base64', 'chmod 777', 'chmod +s', '/dev/tcp', '/dev/udp',
'nmap', 'masscan', 'hydra', 'john', 'hashcat', 'passwd', 'useradd',
'iptables -F', 'ufw disable', 'history -c', 'rm -rf /', 'dd if=',
'crontab', 'at ', 'systemctl enable', 'ssh-keygen', 'scp ', 'rsync',
'tar czf', 'zip -r', 'openssl enc', 'gpg --encrypt', 'shred',
'chattr', 'setfacl', 'awk', '/tmp/', '/dev/shm/'
]
for hist_file in glob.glob('/cases/case-2024-001/linux/users/*/.bash_history'):
username = hist_file.split('/')[-2]
print(f"사용자: {username}")
with open(hist_file, 'r', errors='ignore') as f:
lines = f.readlines()
print(f" 총 명령어 수: {len(lines)}")
flagged = []
for i, line in enumerate(lines):
line = line.strip()
for cmd in suspicious_commands:
if cmd in line.lower():
flagged.append((i+1, line))
break
if flagged:
print(f" 의심스러운 명령어: {len(flagged)}개")
for lineno, cmd in flagged:
print(f" 라인 {lineno}: {cmd[:120]}")
print()
PYEOF
5단계: 루트킷 및 변조된 바이너리 확인
# 알려진 루트킷 지표 확인
# 시스템 바이너리 해시를 알려진 정상 해시와 비교
find /mnt/evidence/usr/bin/ /mnt/evidence/usr/sbin/ /mnt/evidence/bin/ /mnt/evidence/sbin/ \
-type f -executable -exec sha256sum {} \; > /cases/case-2024-001/linux/analysis/binary_hashes.txt
# SUID/SGID 바이너리 확인 (권한 상승 가능성)
find /mnt/evidence/ -perm -4000 -type f 2>/dev/null > /cases/case-2024-001/linux/analysis/suid_files.txt
find /mnt/evidence/ -perm -2000 -type f 2>/dev/null > /cases/case-2024-001/linux/analysis/sgid_files.txt
# /tmp 및 /dev/shm 내 의심스러운 파일 확인
find /mnt/evidence/tmp/ /mnt/evidence/dev/shm/ -type f 2>/dev/null \
-exec file {} \; > /cases/case-2024-001/linux/analysis/tmp_files.txt
# 숨겨진 파일 및 디렉토리 확인
find /mnt/evidence/ -name ".*" -not -path "*/\.." -type f 2>/dev/null | \
head -100 > /cases/case-2024-001/linux/analysis/hidden_files.txt
# 커널 모듈 확인
ls -la /mnt/evidence/lib/modules/$(ls /mnt/evidence/lib/modules/ | head -1)/extra/ 2>/dev/null \
> /cases/case-2024-001/linux/analysis/extra_modules.txt
# 변조된 PAM 구성 확인 (인증 백도어)
diff /mnt/evidence/etc/pam.d/ /cases/baseline/pam.d/ 2>/dev/null \
> /cases/case-2024-001/linux/analysis/pam_changes.txt
주요 개념
| 개념 | 설명 |
|---|---|
| /var/log/auth.log | Debian/Ubuntu 시스템의 주요 인증 로그 |
| /var/log/secure | RHEL/CentOS 시스템의 주요 인증 로그 |
| wtmp/btmp | 성공 및 실패한 로그인 세션을 기록하는 바이너리 로그 |
| .bash_history | 사용자 명령 히스토리 파일 (공격자에 의해 지워질 수 있음) |
| crontab | 지속성을 위해 흔히 사용되는 예약 작업 시스템 |
| authorized_keys | 계정에 대한 암호 없는 접근을 허용하는 SSH 공개 키 |
| SUID 비트 | 파일 소유자로 실행을 허용하는 파일 권한 (권한 상승 벡터) |
| LD_PRELOAD | 다른 모든 공유 라이브러리보다 먼저 로드되는 환경 변수 (후킹 기술) |
도구 및 시스템
| 도구 | 목적 |
|---|---|
| chkrootkit | 리눅스 시스템용 루트킷 탐지 스캐너 |
| rkhunter | Rootkit Hunter - 루트킷, 백도어, 로컬 익스플로잇 확인 |
| AIDE | 고급 침입 탐지 환경 - 파일 무결성 모니터 |
| auditd | 시스템 호출 및 파일 접근 모니터링을 위한 리눅스 감사 프레임워크 |
| last/lastb | wtmp/btmp를 파싱하여 로그인 및 실패 로그인 기록 확인 |
| Plaso/log2timeline | 리눅스 아티팩트를 포함한 슈퍼 타임라인 생성 |
| osquery | 라이브 포렌식 조사를 위한 SQL 기반 시스템 조회 |
| Velociraptor | 리눅스 아티팩트 수집 기능이 있는 엔드포인트 에이전트 |
일반적인 시나리오
시나리오 1: SSH 무차별 대입 공격 후 침해 auth.log에서 SSH 실패 시도 후 성공을 분석하고, 공격 IP를 식별하며, .bash_history에서 침해 후 명령어를 확인하고, authorized_keys에서 추가된 백도어 키를 검사하며, crontab에서 지속성을 확인하고, 네트워크 연결을 검토합니다.
시나리오 2: 애플리케이션 취약점을 통한 웹 서버 침해 웹 서버 접근 및 오류 로그에서 익스플로잇 시도를 검사하고, /tmp 및 /dev/shm에서 웹쉘을 확인하며, 웹 서버 사용자(www-data)의 활동을 분석하고, SUID 바이너리 또는 커널 익스플로잇을 통한 권한 상승을 확인하며, 외부 연결을 검토합니다.
시나리오 3: 데이터베이스 서버 내부 위협 의심 사용자의 bash_history에서 데이터베이스 덤프 명령어를 분석하고, 홈 디렉토리 또는 /tmp에 있는 대용량 tar/zip 파일을 확인하며, scp/rsync 명령어에서 데이터 전송을 검사하고, cron 작업에서 자동화된 유출을 검토하며, USB 장치 로그를 확인합니다.
시나리오 4: 클라우드 인스턴스의 암호화폐 채굴기 /proc(라이브) 또는 systemd 서비스 파일에서 높은 CPU 사용 프로세스를 확인하고, crontab 항목에서 채굴기 재시작 스크립트를 검사하며, /tmp에서 채굴 바이너리를 확인하고, 네트워크 연결에서 채굴 풀 통신을 분석하며, authorized_keys에서 공격자 접근을 검토합니다.
출력 형식
리눅스 포렌식 요약:
시스템: webserver01 (Ubuntu 22.04 LTS)
호스트명: webserver01.corp.local
커널: 5.15.0-91-generic
사용자 계정:
총계: 25 (UID 0 계정 3개 - 1개 이상 징후)
대화형 셸: 8명의 사용자
최근 생성됨: admin2 (2024-01-15 생성)
인증 이벤트:
성공한 SSH 로그인: 456회
실패한 SSH 시도: 12,345회 (23개의 고유 IP에서)
sudo 실행: 89회
발견된 지속성 메커니즘:
Cron 작업: 3개 의심스러움 (리버스 셸, 채굴기 재시작)
Systemd 서비스: 1개 알 수 없음 (update-checker.service)
SSH 키: root authorized_keys에 2개의 무단 키
rc.local: 다운로드 크래들로 변조됨
의심스러운 활동:
- bash_history에 pastebin URL로의 wget 포함
- SUID 바이너리 /t