리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 10 / 50 · Part 1. Linux 기본 구조 (마무리)
실습 환경: Rocky Linux 9 (10.0.0.200)
이전 글: 9. Linux 환경변수 이해
Part 1에서는 Linux의 구조를 커널(2편), 쉘(3편), 메모리 공간(4편), 시스템 콜(5편), 파일 시스템(6·7편), 부팅(8편), 환경변수(9편) 순서로 살펴봤다. 이번 글은 Part 1의 마무리로, 지금까지 나온 확인 명령어를 "서버에 처음 접속했을 때 무엇을 어떤 순서로 확인하는가" 라는 관점으로 묶는다.
보안관제에서 경보를 받고 서버를 확인할 때, 매번 기억에 의존해 명령어를 치면 빠뜨리는 항목이 생기고 결과도 남지 않는다. 그래서 이번 글에서는 확인 항목을 6개 질문 으로 정리하고, 이를 자동으로 수집해 파일로 저장하는 Triage 스크립트 를 만든다.
Triage(분류)는 원래 응급실에서 환자의 긴급도를 빠르게 분류하는 절차다. 침해사고 대응에서는 짧은 시간 안에 시스템 상태를 수집해 "추가 분석이 필요한가, 얼마나 급한가"를 판단하는 첫 단계 를 말한다.
| 질문 | 확인 내용 | 관련 글 |
|---|---|---|
| Q1. 어떤 시스템인가 | 호스트명, OS, 커널, 가동 시간, 재부팅 이력 | 1·2·8편 |
| Q2. 시각은 정확한가 | 시간대, NTP 동기화 | (로그 해석의 기준) |
| Q3. 누가 있는가 | 접속자, 로그인 이력, UID 0 계정, 로그인 가능 계정 | 3·9편 |
| Q4. 무엇이 실행 중인가 | 프로세스 트리, 삭제된 실행 파일, 서비스, 모듈 | 2·4편 |
| Q5. 누구와 통신하는가 | IP, 라우팅, 대기 포트, 연결 세션 | 1편 |
| Q6. 자원·저장소는 정상인가 | 메모리, 디스크·inode, 마운트 옵션, 로그 파일 | 6·7편 |
로그 분석은 결국 여러 로그를 시간순으로 맞춰 보는 작업 이다. 서버 시각이 틀리거나 시간대가 서로 다르면(KST vs UTC) 같은 사건이 9시간 차이로 보인다. SIEM에 수집된 이벤트와 서버 로그를 비교하기 전에 반드시 기준 시각을 확인해야 한다.

Triage는 수집 → 무결성 기록 → 기준값 비교 → 보고 순서로 진행한다.
sha256sum을 남겨, 이후 결과가 수정되지 않았음을 증명한다.diff 한다. 차이가 분석 대상이다.침해가 의심되는 서버에서는
ps,ss같은 명령어 자체가 변조되었을 수 있다(7·9편). 그래서 Triage 결과는 SIEM에 이미 수집된 로그와 교차 검증 한다.
# Q1
hostnamectl; uname -r; cat /etc/os-release | head -3
uptime -s; last -x reboot shutdown | head -5
# Q2
timedatectl
chronyc tracking | grep -E 'Reference|System time|Leap'
# Q3
w; last -n 10
awk -F: '$3==0 {print $1}' /etc/passwd
# Q4
ps auxf | head -30
sudo ls -l /proc/*/exe 2>/dev/null | grep -E '\(deleted\)|memfd:'
systemctl list-units --type=service --state=running --no-pager
# Q5
ip -br addr; ip route
sudo ss -tulnp; sudo ss -tanp state established
# Q6
free -h; df -hT; df -i
findmnt -T /tmp; ls -lt /var/log | head
위 명령을 하나로 묶어 결과를 파일로 저장하고 해시를 기록하는 스크립트다. 모든 명령이 읽기 전용 이다.
#!/bin/bash
# linux_triage.sh — 서버 기본 정보 수집 (읽기 전용 명령만 사용)
# 사용법: sudo bash linux_triage.sh
set -u
HOST=$(hostname -s)
TS=$(date +%Y%m%d_%H%M%S)
OUT="/root/triage/${HOST}_${TS}"
mkdir -p "$OUT"
run() { # run <파일명> <명령...>
local name=$1; shift
{ echo "### CMD: $*"; echo "### TIME: $(date -Is)"; "$@" 2>&1; } > "$OUT/$name.txt"
}
# Q1 어떤 시스템인가
run 01_hostnamectl hostnamectl
run 01_uname uname -a
run 01_os_release cat /etc/os-release
run 01_uptime uptime
run 01_reboots last -x reboot shutdown -n 20
# Q2 시각은 정확한가
run 02_timedatectl timedatectl
run 02_chrony chronyc tracking
# Q3 누가 있는가
run 03_who w
run 03_last last -n 50
run 03_uid0 awk -F: '$3==0 {print $1}' /etc/passwd
run 03_login_shell awk -F: '$7 !~ /(nologin|false)$/ {print $1, $7}' /etc/passwd
# Q4 무엇이 실행 중인가
run 04_ps ps auxf
run 04_deleted_exe bash -c 'ls -l /proc/*/exe 2>/dev/null | grep -E "\(deleted\)|memfd:"'
run 04_services systemctl list-units --type=service --state=running --no-pager
run 04_enabled systemctl list-unit-files --state=enabled --no-pager
run 04_modules lsmod
# Q5 누구와 통신하는가
run 05_ip ip -br addr
run 05_route ip route
run 05_listen ss -tulnp
run 05_estab ss -tanp state established
# Q6 자원·저장소
run 06_mem free -h
run 06_df df -hT
run 06_inode df -i
run 06_tmp_mount findmnt -T /tmp
run 06_varlog ls -lt --time-style=full-iso /var/log
# 무결성 기록
( cd "$OUT" && sha256sum *.txt > SHA256SUMS )
echo "[완료] $OUT ($(ls "$OUT" | wc -l) files)"
sudo bash linux_triage.sh
sudo ls /root/triage/
sudo cat /root/triage/<호스트>_<시각>/SHA256SUMS | head -3
# 평소(정상) 상태에서 한 번 실행해 기준값 확보 → 사고 시 다시 실행 후 비교
B=/root/triage/rocky_20260901_090000
N=/root/triage/rocky_20260923_101500
for f in 03_uid0 03_login_shell 04_enabled 04_modules 05_listen; do
echo "== $f =="; diff <(grep -v '^###' $B/$f.txt) <(grep -v '^###' $N/$f.txt)
done
프로세스 목록(ps)처럼 PID·시각이 매번 바뀌는 항목은 diff가 의미 없으므로, 계정·자동 시작 서비스·모듈·대기 포트처럼 평소에 잘 바뀌지 않는 항목 을 비교 대상으로 삼는다.
아래 출력은 형식 설명용 예시다.
① timedatectl
Local time: Wed 2026-09-23 10:15:02 KST
Universal time: Wed 2026-09-23 01:15:02 UTC
Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
NTP service: active
System clock synchronized: no라면 로그 시각을 그대로 믿으면 안 된다. 보고서에는 어느 시간대 기준인지 반드시 적는다.
② 기준값 비교 결과
== 04_enabled ==
> sys-update.service enabled disabled
== 05_listen ==
> tcp LISTEN 0 5 0.0.0.0:4444 0.0.0.0:* users:(("sys-update",pid=8812,fd=3))
기준값에 없던 자동 시작 서비스와, 그 서비스가 여는 포트가 동시에 나타났다. 이름은 업데이트 관련처럼 보이지만 이름이 아니라 차이 자체가 분석 대상 이다.
| 항목 | 정상 | 추가 분석 필요 |
|---|---|---|
| UID 0 계정 | root 하나 | root 외 계정 존재 |
| 로그인 가능 계정 | 관리자·사용자 계정만 | 서비스 계정에 /bin/bash |
| 삭제된 exe | 없음 (패키지 업데이트 직후 제외) | /tmp 등에서 실행 후 삭제된 프로세스 |
| 자동 시작 서비스 | 기준값과 동일 | 새 유닛 |
| 대기 포트 | 업무 서비스 포트만 | 알 수 없는 포트 |
| 디스크 | 사용률 90% 미만 | 로그 파티션 포화 |
| 재부팅 | 작업 일정과 일치 | 예정에 없는 재부팅 |
| 원칙 | 이유 |
|---|---|
| 읽기 전용 명령만 사용 | 분석이 증거를 바꾸지 않도록 |
| 결과를 파일로 저장하고 해시 기록 | 나중에 결과를 증거로 제시할 수 있도록 |
| 결과를 서버 밖으로 복사 | 공격자가 결과 파일을 지우거나 바꾸지 못하도록 |
| 휘발성 높은 정보 먼저 | 프로세스·연결은 곧 사라짐 (7편 휘발성 순서) |
| 서버 명령어를 완전히 믿지 않음 | 명령어 교체, 프리로드 루트킷 가능성 (7·9편) |
| 기준값을 평소에 확보 | 사고가 난 뒤에는 "정상"을 알 수 없음 |
[SIEM Alert] 예: 새벽 시간 SSH 로그인 성공 + 새로운 대기 포트 탐지
↓
[Triage] linux_triage.sh 실행 → 결과·해시 저장 → 분석 서버로 복사
↓
[비교] 기준값 diff → 새 서비스, 새 포트, 새 UID 0 계정 여부
↓
[IOC 정리] 계정, 출발지 IP, 프로세스 경로, 파일 해시, 포트
↓
[판단] 오탐 종결 / 침해 의심 → 상세 분석(Part 3~5에서 다룰 로그·프로세스·네트워크 분석)
| 편 | 핵심 | Triage에서의 쓰임 |
|---|---|---|
| 1 | Linux 계층 구조 | 확인 대상의 전체 지도 |
| 2 | 커널 | 커널 버전, 모듈, tainted |
| 3 | 쉘 | 로그인 쉘, history, 시작 파일 |
| 4 | User/Kernel Space | 커널 스레드 위장, 삭제된 exe |
| 5 | System Call | auditd 기록과 교차 검증 |
| 6 | 파일 시스템 | 디스크·inode·마운트 옵션 |
| 7 | 디렉터리 구조 | 확인 경로와 휘발성 순서 |
| 8 | 부팅 | 재부팅 이력, 자동 시작 서비스 |
| 9 | 환경변수 | PATH, 프리로드 점검 |
diff로 비교한다.Part 2 「파일·명령어·권한」이 시작된다. 다음 글 「11. Linux 파일과 디렉터리 관리」 에서는 ls, cp, mv, rm, mkdir, touch 같은 기본 명령어를 다루되, 증거를 훼손하지 않고 파일을 다루는 방법 (속성 보존 복사, 타임스탬프 확인, 삭제의 의미)에 초점을 맞춘다.