리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.

리눅스 시스템 기초 49 / 50 · Part 5. 네트워크·보안·SOC
실습 환경: 21편 샘플 로그(secure, RHEL 형식) · 모든 집계 결과는 샘플 로그에 명령을 실행한 실제 출력
이전 글: 48. Linux 로그와 journalctl

1. 들어가며

지금까지 배운 것을 하나의 사건에 적용할 차례다. 이번 글은 SSH 무차별 대입(Brute Force) 을 로그만으로 분석한다.

분석 대상은 21편에서 만든 샘플 /var/log/secure다(RHEL 형식, 176줄). 이 로그에는 정상 관리자의 접속, 인터넷에서 들어오는 산발적인 시도, 그리고 하나의 실제 공격 흐름 이 섞여 있다. 목표는 다음 질문에 근거를 가지고 답하는 것이다.

  1. 무차별 대입이 있었는가? 누가, 언제, 얼마나?
  2. 공격이 성공했는가?
  3. 성공했다면 그 이후 무엇을 했는가?
  4. 무엇을 탐지 기준으로 삼고, 어떻게 대응해야 하는가?

사용하는 도구는 21~28편의 grep, awk, sort, uniq, 쉘 스크립트가 전부다. Ubuntu라면 파일만 /var/log/auth.log로 바꾸면 같은 방법이 통한다(메시지 형식이 같다).


2. 핵심 개념

2-1. 무차별 대입의 유형

유형특징로그 모양
무차별 대입 (Brute Force)한 계정에 비밀번호를 대량으로 시도같은 계정 Failed password 반복
사전 대입 (Dictionary)흔한 계정명 × 흔한 비밀번호Invalid user admin/test/oracle…
패스워드 스프레이 (Spraying)많은 계정에 비밀번호 1~2개씩, 느리게계정은 많고 계정당 실패는 적음
크리덴셜 스터핑유출된 계정·비밀번호 쌍 재사용실패 적고 첫 시도에 성공 하기도

MITRE ATT&CK에서는 T1110(Brute Force)과 그 하위 기법(.001 Password Guessing, .003 Password Spraying, .004 Credential Stuffing)으로 분류된다. 성공한 뒤 그 계정을 쓰는 것은 T1078(Valid Accounts)이다.

2-2. 판단에 쓰는 지표

지표계산 방법의미
출발지별 실패 수Failed password의 IP 집계누가 가장 많이 시도했나
시도 기간·속도첫 시각 ~ 마지막 시각, 분당 횟수자동화 도구 여부
시도 계정 수·종류계정명 집계, Invalid user 비율사전 대입인가, 표적 계정인가
실패 후 성공같은 IP의 Accepted침해 여부의 핵심
성공 이후 행위sudo, su, 세션 시간공격 목적·진행 단계

3. 동작 원리

SSH 무차별 대입 — 시도에서 성공, 그리고 그 이후까지

분석은 넓게 → 좁게 → 시간순 으로 진행한다.

[1. 전체 규모]   실패 총계, 출발지별 집계
      ↓
[2. 대상 선정]   가장 많은 출발지 · 짧은 기간 · 많은 계정
      ↓
[3. 성공 여부]   그 출발지의 Accepted
      ↓
[4. 사후 행위]   성공 세션의 PID·계정으로 이후 로그 추적
      ↓
[5. 판단·대응]   침해 여부 결론, 차단·계정 조치, 탐지 규칙

3-1. 전체 규모

$ grep -c 'Failed password' secure
113

$ grep 'Failed password' secure | grep -oE 'from [0-9.]+' | awk '{print $2}' | sort | uniq -c | sort -rn
     95 10.0.0.128
      9 203.0.113.7
      4 203.0.113.88
      4 203.0.113.150
      1 203.0.113.45

113회 중 95회(84%)가 10.0.0.128 한 곳 이다.

3-2. 출발지별 기간·계정 수

한 번에 보기 위해 분석 스크립트(bf_analyze.sh, 7-1절)의 두 번째 항목을 실행한 실제 결과다.

== 2. 출발지별 시작·끝·계정 수 ==
10.0.0.128        95회  02:10:09 ~ 02:19:15  계정 8개
203.0.113.7        9회  03:50:16 ~ 23:07:10  계정 4개
203.0.113.88       4회  03:06:43 ~ 21:03:48  계정 3개
203.0.113.150      4회  01:47:45 ~ 21:59:16  계정 3개
203.0.113.45       1회  21:14:49 ~ 21:14:49  계정 1개
출발지판단
10.0.0.1289분 동안 95회 (분당 약 10회), 계정 8개 → 자동화된 집중 공격
203.0.113.x하루 종일 몇 번씩 → 인터넷에 노출된 SSH가 늘 받는 배경 소음

배경 소음을 모두 사건으로 다루면 관제가 마비된다. 양·속도·결과 로 우선순위를 나눈다.

3-3. 속도와 계정

$ grep 'Failed password' secure | grep 10.0.0.128 | awk '{print substr($3,1,5)}' | uniq -c
     10 02:10
      8 02:11
     10 02:12
      8 02:13
     10 02:14
     10 02:15
     11 02:16
     14 02:17
     10 02:18
      4 02:19

$ grep 'Failed password' secure | grep 10.0.0.128 \
    | sed -E 's/.*for (invalid user )?([^ ]+) from.*/\2/' | sort | uniq -c | sort -rn
     40 root
     25 devuser
      5 ubuntu
      5 test
      5 postgres
      5 oracle
      5 guest
      5 admin
  • 분당 8~14회가 일정하게 이어진다 → 사람이 아니라 도구다.
  • root 40회: PermitRootLogin no라면 성공할 수 없지만 공격자는 일단 시도한다.
  • admin, test, oracle 등 6개 계정은 각 5회 → 이 서버에 없는 계정(Invalid user 30회). 흔한 계정명 목록으로 사전 대입한 것이다.
  • devuser 25회 → 실제로 존재하는 계정이다. 공격자가 계정명을 알고 있었거나, 사전 목록에 있었다.

3-4. 성공 여부

== 4. 성공 로그인과 직전 실패 수 ==
Sep 23 02:19:21 password devuser 10.0.0.128 | 이전 실패 95회
Sep 23 09:03:20 publickey roror 10.0.0.10 | 이전 실패 0회
Sep 23 13:03:56 publickey roror 10.0.0.10 | 이전 실패 0회
Sep 23 18:17:26 publickey roror 10.0.0.10 | 이전 실패 0회

02:19:15 마지막 실패, 6초 뒤 02:19:21 Accepted password for devuser. 무차별 대입이 성공 했다. 정상 관리자 roror는 업무 시간에 공개키로, 관리 PC(10.0.0.10)에서만 접속하므로 뚜렷이 구분된다.

3-5. 성공 이후

$ grep -E 'sshd\[5224\]|devuser' secure | grep -v 'Failed password'
Sep 23 02:19:21 rocky sshd[5224]: Accepted password for devuser from 10.0.0.128 port 51514 ssh2
Sep 23 02:19:21 rocky sshd[5224]: pam_unix(sshd:session): session opened for user devuser(uid=1001) by (uid=0)
Sep 23 02:21:21 rocky sudo[5233]:  devuser : user NOT in sudoers ; TTY=pts/1 ; PWD=/home/devuser ; USER=root ; COMMAND=/bin/bash
Sep 23 02:22:21 rocky su[5270]: pam_unix(su-l:auth): authentication failure; logname=devuser uid=1001 euid=0 tty=pts/1 ruser=devuser rhost=  user=root
Sep 23 02:22:21 rocky su[5270]: FAILED SU (to root) devuser on pts/1
Sep 23 02:31:21 rocky sshd[5224]: pam_unix(sshd:session): session closed for user devuser
시각행위의미
02:19:21로그인 성공초기 접근 (T1078)
02:21:21sudo /bin/bash → NOT in sudoersroot 쉘 획득 시도 실패 (19·20편)
02:22:21su → FAILED SU (to root)root 비밀번호 추측 실패
02:31:21세션 종료 (12분)

권한 상승은 로그상 실패했다. 하지만 "실패했으니 끝"이 아니다. 12분 동안 devuser 권한으로 읽을 수 있는 모든 것(홈 디렉터리, 설정 파일, /tmp)에 접근했을 수 있고, 같은 IP가 02:30부터 웹 서버를 스캔한 기록도 있다(50편).


4. 실습

샘플 로그로 직접 따라 해 본다(21편 make_sample_logs.py로 생성).

LOG=secure                          # 실제 서버: /var/log/secure · Ubuntu: /var/log/auth.log

# 1) 규모
grep -c 'Failed password' $LOG
grep 'Failed password' $LOG | grep -oE 'from [0-9.]+' | awk '{print $2}' | sort | uniq -c | sort -rn

# 2) 특정 IP의 분당 속도
grep 'Failed password' $LOG | grep '10.0.0.128' | awk '{print substr($3,1,5)}' | uniq -c

# 3) 시도 계정
grep 'Failed password' $LOG | grep '10.0.0.128' | sed -E 's/.*for (invalid user )?([^ ]+) from.*/\2/' | sort | uniq -c | sort -rn

# 4) 없는 계정 시도
grep -oE 'Invalid user [^ ]+ from [0-9.]+' $LOG | awk '{print $5, $3}' | sort | uniq -c | sort -rn

# 5) 성공
grep 'Accepted' $LOG

# 6) 성공 세션의 이후 행위 (PID·계정 기준)
grep -E 'sshd\[5224\]|devuser' $LOG | grep -v 'Failed password'

# 7) 로그인 기록과 교차 확인 (실제 서버)
last -F devuser | head
sudo lastb -F | awk '{print $3}' | sort | uniq -c | sort -rn | head     # btmp 실패 기록

5. 결과 분석

분석 결과를 한 장으로 정리한다.

항목결과근거
공격 유형사전 + 무차별 대입 (T1110.001)9분 95회, 분당 약 10회, 계정 8개(없는 계정 6개)
출발지10.0.0.128 (내부 사설 IP)외부가 아니라 내부 호스트 → 그 호스트도 침해 의심
성공 여부성공 — 02:19:21 devuser마지막 실패 6초 후 Accepted password
사후 행위sudo 거부, su 실패user NOT in sudoers, FAILED SU
권한 상승로그상 실패추가 확인 필요 (SUID·취약점 경유 가능성, 16·30편)
영향 범위devuser 권한 12분홈·/tmp·읽기 가능한 설정 파일, 이후 웹 스캔
배경 소음203.0.113.x 4곳, 18회산발적, 성공 없음

오탐 가능성 점검. "관리자가 비밀번호를 잊어서 여러 번 틀린 것"과 구분해야 한다. 사람은 몇 번 틀리고 멈추거나 같은 계정만 시도한다. 여기서는 분당 10회가 9분간 일정하게, 없는 계정 6개를 각 5회씩 시도했으므로 자동화 도구가 확실하다.


6. 보안 관점

6-1. 탐지 기준 (예시)

규칙조건등급
R1 무차별 대입 시도같은 IP, 5분 내 Failed password ≥ 10중
R2 사전 대입같은 IP, 10분 내 서로 다른 계정 ≥ 5중
R3 대입 성공R1/R2 발생 IP에서 1시간 내 Accepted긴급
R4 성공 후 권한 상승 시도R3 계정의 NOT in sudoers / FAILED SU높음
R5 비정상 로그인평소와 다른 시간대·출발지·인증 방식중

임계값(10회, 5분)은 환경에 맞게 조정한다. 너무 낮으면 오탐이, 너무 높으면 느린 스프레이를 놓친다. 스프레이는 계정 수 기준(R2) 과 긴 시간 창(24시간) 으로 따로 본다.

6-2. 대응 계층

계층조치효과
즉시출발 IP 차단 (46편 rich rule), 세션 종료추가 시도·세션 차단
계정devuser 잠금(usermod -L)·비밀번호 변경, authorized_keys 점검탈취 계정 무력화, 지속성 제거
설정PasswordAuthentication no, PermitRootLogin no, AllowUsers, MaxAuthTries 3 (45편)대입 자체를 불가능하게
자동 차단fail2ban (실패 N회 시 방화벽 자동 등록)반복 공격 자동 대응
네트워크SSH는 관리망·VPN·배스천 호스트로만노출 면적 축소
조사10.0.0.128 호스트 침해 조사근본 원인 — 내부 거점

7. SOC / 보안관제 활용

7-1. 분석 스크립트

위 과정을 하나로 묶은 스크립트다(shellcheck 경고 없음).

#!/usr/bin/env bash
# bf_analyze.sh — SSH 무차별 대입 분석
# 사용법: ./bf_analyze.sh /var/log/secure   (Ubuntu: /var/log/auth.log)
LOG=${1:-/var/log/secure}
F=$(mktemp); grep -E 'Failed password' "$LOG" > "$F"

echo "== 1. 실패 총계 / 출발지별 =="
wc -l < "$F"
grep -oE 'from [0-9.]+' "$F" | awk '{print $2}' | sort | uniq -c | sort -rn

echo "== 2. 출발지별 시작·끝·계정 수 =="
awk '{for(i=1;i<=NF;i++) if($i=="from") ip=$(i+1);
      u=($9=="invalid")?$11:$9;
      if(!(ip in first)) first[ip]=$3; last[ip]=$3; n[ip]++;
      if(!((ip,u) in seen)){seen[ip,u]=1; nu[ip]++}}
     END{for(ip in n) printf "%-15s %4d회  %s ~ %s  계정 %d개\n", ip, n[ip], first[ip], last[ip], nu[ip]}' "$F" | sort -k2 -rn

echo "== 3. 없는 계정(Invalid user) 시도 =="
grep -oE 'Invalid user [^ ]+ from [0-9.]+' "$LOG" | awk '{print $5, $3}' | sort | uniq -c | sort -rn | head

echo "== 4. 성공 로그인과 직전 실패 수 =="
grep -E 'Accepted' "$LOG" | while read -r l; do
  ip=$(grep -oE 'from [0-9.]+' <<<"$l" | awk '{print $2}')
  ts=$(awk '{print $3}' <<<"$l")
  fails=$(awk -v ip="$ip" -v ts="$ts" '$0 ~ "from "ip" " && $3<=ts' "$F" | wc -l)
  printf '%s | 이전 실패 %d회\n' "$(awk '{print $1,$2,$3,$7,$9,$11}' <<<"$l")" "$fails"
done
rm -f "$F"

한계: 시각을 문자열(HH:MM:SS)로 비교하므로 하루 안의 로그 를 가정한다. 여러 날에 걸친 로그는 날짜를 포함해 비교하거나 journalctl -o short-iso 출력을 쓴다.

7-2. fail2ban (자동 차단)

# /etc/fail2ban/jail.local
[sshd]
enabled  = true
backend  = systemd          # journal 에서 읽기 (RHEL 9 / Ubuntu 22.04)
maxretry = 5
findtime = 10m
bantime  = 1h
sudo fail2ban-client status sshd        # 차단 중인 IP

fail2ban은 대응 자동화 이지 탐지를 대신하지 않는다. 차단된 IP 목록도 SIEM으로 보내 "어떤 IP가 언제 차단됐는지"를 남긴다.

7-3. 보고 요약 (템플릿)

[사건]    SSH 무차별 대입 성공 — 10.0.0.200 (rocky)
[일시]    2026-09-23 02:10:09 ~ 02:31:21 (KST)
[출발지]  10.0.0.128 (내부)
[경과]    02:10~02:19 실패 95회(계정 8개) → 02:19:21 devuser 로그인 성공
          → 02:21 sudo 거부 → 02:22 su 실패 → 02:31 세션 종료
[영향]    devuser 계정 탈취. root 권한 획득 흔적 없음(로그 기준)
[조치]    10.0.0.128 SSH 차단, devuser 잠금·비밀번호 변경, authorized_keys 점검
[권고]    PasswordAuthentication no, SSH 관리망 한정, fail2ban, 10.0.0.128 침해 조사

8. 핵심 정리

  • 분석 순서: 규모 → 대상 선정 → 성공 여부 → 사후 행위 → 판단·대응.
  • 핵심 지표: 출발지별 실패 수, 기간·속도, 계정 수와 Invalid user, 실패 후 성공.
  • 샘플 로그 결론: 10.0.0.128이 9분간 95회(분당 약 10회) 시도 → 02:19:21 devuser 성공 → sudo·su 실패.
  • 배경 소음(산발적 인터넷 시도)과 집중 공격을 양·속도·결과 로 구분한다.
  • 탐지 규칙은 "시도"보다 "시도 후 성공" 에 가장 높은 등급을 준다.
  • 대응: IP 차단 → 계정 조치 → 설정 강화(키 인증 전용) → 자동 차단 → 출발 호스트 조사.

9. 다음 글

마지막 글 「50. 보안관제 실습 — 로그에서 공격 흔적 찾기」 에서는 이번 글의 SSH 로그에 웹 로그(access_log) 를 더해, 한 공격자의 행위를 처음부터 끝까지 타임라인으로 재구성한다. 그리고 시리즈 전체에서 배운 명령어를 초동 대응 체크리스트 로 정리한다.


참고 자료

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글