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

리눅스 시스템 기초 22 / 50 · Part 3. Linux 명령어 활용
실습 환경: Rocky Linux 9 (10.0.0.200) · 실습용 파일 구조 ~/lab/fs (아래 생성 스크립트)
이전 글: 21. grep으로 로그와 문자열 검색

🔗 심화 시리즈 — 「파일 · 권한 · 사용자 관리」
이 주제를 더 깊게 다룬 실무형 보안 시리즈: 46. World-Writable 파일 점검 · 47. SUID/SGID 파일 보안 점검

1. 들어가며

21편의 grep이 파일 안의 내용 을 검색한다면, find는 파일 시스템에서 파일 자체 를 찾는다. 이름뿐 아니라 종류·크기·권한·소유자·시각을 조건으로 조합할 수 있어서, 침해사고 분석에서 "공격자가 남긴 파일"을 찾는 헌팅 도구 로 쓰인다.

이미 여러 글에서 find를 썼다.

편사용한 조건목적
7편-mtime -1최근 변경된 /etc 파일
13편-type s, -type p임시 경로의 소켓·파이프
14편-newerct사고 시간대 타임라인
16편-perm -4000SUID 파일
17편-nouser소유자 없는 파일

이번 글은 이 조건들을 체계적으로 정리하고 여러 조건을 조합하는 방법 을 다룬다.

실습 파일 구조

실제 서버를 건드리지 않도록, 홈 디렉터리 아래에 침해 흔적을 흉내 낸 파일 구조를 만든다. 웹셸처럼 보이는 파일은 문자열만 들어 있는 텍스트 이고 실행하지 않는다.

#!/bin/bash
# make_lab_fs.sh — find 실습용 파일 구조 생성 (~/lab/fs 아래에만 생성)
set -e
R=~/lab/fs; rm -rf "$R"; mkdir -p "$R"/{www/html/upload,tmp/.cache,etc/cron.d,home/devuser,var/log}
cd "$R"
# 정상 웹 파일 (과거 시각)
for f in index.php about.php view.php; do echo "<?php // page ?>" > www/html/$f; done
touch -d '2026-08-01 10:00' www/html/*.php
cp /usr/bin/true www/html/upload/logo.png.tmp 2>/dev/null; mv www/html/upload/logo.png.tmp www/html/upload/thumb.jpg
echo '<?php /* 실습용 표식: 웹셸 의심 파일 */ ?>' > www/html/upload/.cache.php   # 웹셸 흉내 (기능 없음)
# 임시 경로의 숨김 실행 파일과 큰 파일
cp /usr/bin/true tmp/.cache/kw && chmod 755 tmp/.cache/kw
head -c 60M /dev/zero > tmp/.cache/dump.bin
# SUID가 붙은 쉘 사본 흉내 (실행 권한 비트만 실습용으로 설정)
cp /usr/bin/true tmp/.cache/bash && chmod 4755 tmp/.cache/bash
# 지속성 흉내: cron 파일
echo '*/5 * * * * root /tmp/.cache/kw' > etc/cron.d/sys-update
# 누구나 쓸 수 있는 스크립트
echo 'tar czf /backup/home.tgz /home' > etc/backup.sh && chmod 777 etc/backup.sh
# 시각 위조 흉내: mtime만 과거로
echo 'x' > www/html/config.php && touch -d '2019-06-12 08:00' www/html/config.php
# 로그 파일
seq 1 1000 > var/log/app.log; : > var/log/empty.log
echo "[완료] $R"; find "$R" | wc -l

2. 핵심 개념

2-1. 기본 구조

find [시작 경로...] [범위 옵션] [조건...] [동작]
find /var/www  -xdev  -type f -name '*.php' -mmin -60  -ls
구분예의미
시작 경로/, /var/www, .어디서부터
범위-xdev, -maxdepth 2다른 파일 시스템 제외, 깊이 제한
조건-type f, -name무엇을
동작-print(기본), -ls, -exec, -print0찾으면 무엇을

2-2. 주요 조건

분류조건의미
이름-name '*.php', -iname이름 (대소문자 무시)
경로-path '*upload*'전체 경로 패턴
종류-type f/d/l/s/p파일 종류 (13편)
크기-size +50M, -empty크기, 빈 파일
권한-perm 644(정확히), -perm -4000(해당 비트 모두 포함), -perm /6000(하나라도)
소유-user apache, -group, -nouser, -nogroup
시각 (일 단위)-mtime -1, -ctime +30, -atime24시간 단위 (-1 = 1일 이내)
시각 (분 단위)-mmin -60, -cmin60분 이내
시각 (기준)-newer 파일, -newermt '날짜', -newerct기준보다 최근

2-3. 논리 연산

표기의미
나란히 쓰기AND
-oOR
! 또는 -notNOT
\( ... \)묶기 (쉘이 해석하지 않도록 \)

-o는 AND보다 우선순위가 낮다. 그래서 find . -type f -perm -4000 -o -perm -2000은 의도와 다르게 "(파일이면서 SUID) 또는 (SGID인 모든 것)"이 된다. OR를 쓸 때는 괄호로 묶는다.

find / -xdev -type f \( -perm -4000 -o -perm -2000 \)

3. 동작 원리

find 표현식 구조와 침해 흔적 헌팅 쿼리

find는 시작 경로부터 디렉터리를 재귀적으로 내려가며, 각 항목에 대해 조건을 왼쪽부터 평가 한다. AND로 연결된 조건은 앞 조건이 거짓이면 뒤 조건을 평가하지 않는다. 그래서 가벼운 조건(-type, -name)을 앞에, 무거운 조건(-exec)을 뒤에 두는 것이 빠르다.

각 파일마다 inode 정보(stat)를 읽어 조건을 비교하므로, / 전체를 검색하면 시간이 오래 걸리고 디스크 I/O가 발생한다. 운영 중인 서버에서는 시작 경로를 좁히고 -xdev로 범위를 제한 하는 것이 기본이다.


4. 실습

cd ~/lab/fs

# 1) 오늘 생성·변경된 파일
find . -type f -newermt "$(date +%F)" | sort

# 2) SUID/SGID 파일 (괄호 주의)
find . -type f \( -perm -4000 -o -perm -2000 \) -ls

# 3) 숨김 파일·디렉터리
find . -name '.*' ! -name '.'

# 4) 임시·웹 경로의 실행 파일 (확장자 무관, 13편)
find tmp www -type f -exec file {} + | grep -i elf

# 5) 50MB 넘는 파일
find . -type f -size +50M -exec ls -lh {} +

# 6) 누구나 쓸 수 있는 파일
find . -type f -perm -0002

# 7) 업로드 폴더의 스크립트
find www -path '*upload*' -type f \( -name '*.php' -o -name '*.jsp' \)

# 8) 시각 위조 의심 — mtime은 오래됐는데 ctime은 오늘
find www -type f ! -newermt '2026-01-01' -newerct "$(date +%F)"
stat -c '%n  mtime=%y  ctime=%z' www/html/config.php

# 9) 결과를 안전하게 다음 명령으로 (공백 포함 이름 대비)
find tmp -type f -print0 | xargs -0 sha256sum

5. 결과 분석

아래는 ~/lab/fs에 실행한 실제 결과다.

① 오늘 생성·변경된 파일

./etc/backup.sh
./etc/cron.d/sys-update
./tmp/.cache/bash
./tmp/.cache/dump.bin
./tmp/.cache/kw
./var/log/app.log
./var/log/empty.log
./www/html/upload/.cache.php
./www/html/upload/thumb.jpg

웹 루트의 정상 페이지(index.php 등)는 8월 시각이라 목록에 없다. 변경 목록이 곧 조사 목록 이 된다.

② ~ ⑧ 헌팅 결과 요약

쿼리결과해석
SUID./tmp/.cache/bash (-rwsr-xr-x root)임시 경로의 root SUID 쉘 사본 → 지속성 백도어 패턴 (16편)
숨김./tmp/.cache, ./www/html/upload/.cache.php숨김 디렉터리 + 업로드 폴더의 숨김 PHP
ELFtmp/.cache/kw, tmp/.cache/bash, upload/thumb.jpg이미지 이름의 실행 파일 포함 (13편)
큰 파일60M ./tmp/.cache/dump.bin데이터 수집·유출 준비 가능성
world-writable./etc/backup.sh누구나 수정 가능한 스크립트 (15편)
업로드 스크립트www/html/upload/.cache.php웹셸 의심
시각 위조www/html/config.phpmtime 2019-06-12 08:00:00.000000000, ctime 오늘 (14편)

그리고 etc/cron.d/sys-update의 내용을 보면 /tmp/.cache/kw를 5분마다 root로 실행하도록 되어 있다. find 결과끼리 연결 하면 "임시 경로의 실행 파일 + 그것을 주기 실행하는 cron"이라는 지속성 구조가 드러난다.


6. 보안 관점

주의점이유방법
-delete, -exec rm 사용 금지증거 훼손분석 단계에서는 목록·해시만
공백·특수문자 파일명`findxargs`가 이름을 잘라 오작동
/proc, 네트워크 마운트 검색느리고 무의미한 결과, 원격 저장소 부하-xdev, -path /proc -prune
-mtime 단위 오해-mtime -1은 "24시간 이내", +1은 "48시간 이전"정확한 시각은 -newermt
mtime만 기준공격자가 touch로 위조 가능ctime(-newerct) 함께 사용
권한 오류 출력결과가 오류 메시지에 묻힘2>/dev/null (27편 리다이렉션)

7. SOC / 보안관제 활용

7-1. 실제 서버용 헌팅 쿼리 모음

T='2026-09-23 02:00'     # 사고 추정 시작 시각

# 사고 이후 변경된 시스템 파일 (ctime 기준)
sudo find /etc /usr/bin /usr/sbin /var/spool/cron -xdev -newerct "$T" -ls 2>/dev/null

# 임시 경로의 실행 파일
sudo find /tmp /var/tmp /dev/shm -type f -perm /111 -ls 2>/dev/null

# 웹 루트의 최근 스크립트
sudo find /var/www -type f \( -name '*.php' -o -name '*.jsp' \) -newerct "$T" -ls

# 새 SUID (16편 기준값과 비교)
sudo find / -xdev -type f -perm -4000 2>/dev/null | sort | comm -13 /root/baseline_suid.txt -

# 홈 디렉터리의 SSH 키 변경
sudo find /root /home -name authorized_keys -newerct "$T" -ls 2>/dev/null

-perm /111은 "소유자·그룹·기타 중 하나라도 실행 권한이 있는" 파일이다.

7-2. 분석 흐름

[Event]   21편: 02:19 devuser 로그인 성공 (무차별 대입 IP)
   ↓
[Hunting] find -newerct '02:19' → cron.d/sys-update, /tmp/.cache/{kw,bash}, upload/.cache.php
   ↓
[연결]    cron 이 /tmp/.cache/kw 를 root로 주기 실행 / bash 사본에 SUID
   ↓
[IOC]     파일 경로, sha256 해시, 생성 시각(btime·ctime)
   ↓
[Response] 파일 격리·해시 보존 → cron 제거 → SUID 사본 제거 → 같은 해시를 다른 서버에서 검색

8. 핵심 정리

  • find는 시작 경로 + 조건 + 동작 으로 구성된다. 조건은 나란히 쓰면 AND다.
  • -o(OR)는 우선순위가 낮으므로 \( \)로 묶는다.
  • 헌팅 핵심 조건: -newermt/-newerct(시각), -perm -4000(SUID), -perm -0002(world-writable), -name '.*'(숨김), -size, -nouser.
  • 시각은 위조가 어려운 ctime(-newerct) 도 함께 본다.
  • 분석 중에는 -delete를 쓰지 않고, -print0 | xargs -0, -xdev, 2>/dev/null로 안전하게 검색한다.
  • find 결과는 따로 보지 말고 서로 연결 한다 (실행 파일 ↔ cron ↔ SUID ↔ 로그인 시각).

9. 다음 글

다음 글 「23. head와 tail」 에서는 로그의 앞·뒤를 보는 가장 단순한 명령어를 다룬다. 특히 실시간 관찰에 쓰는 tail -f와 tail -F의 차이, 그리고 로그 로테이션 중에 모니터링이 끊기는 문제 를 inode 관점에서 설명한다.


참고 자료

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

0개의 댓글