Step 100. Bandit 21~25 — cron 악용 사고방식

Step 100. Bandit 21~25 — cron 악용 사고방식

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 99의 setuid와 "누구 권한으로 도는가"의 사고방식을 마쳤다. Bandit 20→21 비밀번호를 가지고 있다.

  • 준비물: SSH 접속 환경, 비밀번호 기록, 로컬 실험용 리눅스/WSL.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 합법 연습장 안내: OverTheWire Bandit은 공식 공격 연습 플랫폼입니다. cron에 끼어드는 기술은 특히 지속성(persistence) — 남의 시스템에서는 백도어 설치에 해당하므로, 연습장과 내 랩에서만 실험합니다.

서버에는 사람이 치지 않아도 도는 일들이 있습니다. 매분 로그를 정리하고, 매시간 백업을 하고, 매일 밤 보고서를 만드는 — cron이라는 스케줄러가 돌리는 자동 작업들입니다. 여기에 해커의 질문을 얹어 봅시다. 자동으로 도는 것은 보통 높은 권한으로 돕니다. 그런데 그 작업이 내가 바꿀 수 있는 파일을 읽거나 실행한다면? 그 권한을 빌릴 수 있습니다. setuid가 "프로그램의 탈"이었다면, cron 악용은 "일정표에 끼어들기"입니다. 오늘 배우는 것은 기술이기 전에 사고방식 — 자동으로 도는 것부터 본다는 눈입니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • /etc/cron.d/를 읽고 "누구 권한으로 무엇이 언제"를 해석한다
  • cron이 실행하는 셸 스크립트를 읽고 입출력 경로를 추적한다
  • md5sum으로 만들어지는 파일명을 손으로 미리 계산한다
  • "높은 권한이 내가 놓을 수 있는 곳의 것을 실행한다"는 끼어들기 조건을 설명하고 재현한다
  • 권한이 점이 아니라 선(실행부터 결과 배달까지)임을 설명한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 리눅스 셸(Bandit 서버 + 로컬 WSL), 셸 스크립트 읽기
오늘의 명령 ls /etc/cron.d/, cat, md5sum, cut, 히어독(<<'EOF'), chmod +x
필요한 개념 cron 일정표 구조, 셸 스크립트 기초, 해시, 권한 체인, 지속성
오늘의 산출물 Bandit 21→26 비밀번호 체인 + cron 악용 체크리스트 한 장

2-1. cron의 해부 — 시계가 치는 명령

리눅스의 cron은 "정해진 시각에 명령을 실행하는" 시스템의 알람 시계입니다. 설정은 /etc/cron.d/ 폴더 등에 파일로 들어 있고, 한 줄이 하나의 예약입니다.

* * * * * bandit22 /usr/bin/some_script.sh
└ 분 시 일 월 요일   └ 누구 권한으로   └ 무엇을

다섯 칸이 일정이고(* * * * *은 "매분"), 그다음이 실행 권한의 주인, 마지막이 실행될 것입니다. 읽기의 핵심은 중간 칸입니다 — "이 작업은 누구의 권한으로 도는가?" Step 99에서 얻은 바로 그 질문입니다.

2-2. 셸 스크립트 읽기 — 필요한 문법은 셋뿐

cron이 실행하는 것은 대부분 셸 스크립트 — 명령어를 순서대로 적어 둔 텍스트 파일입니다. 오늘 필요한 문법은 세 가지입니다.

#!/bin/bash                      # 이 파일은 bash가 실행한다는 선언
myname=$(whoami)                 # 명령의 결과를 변수에 저장
echo "텍스트 $myname" > /tmp/x    # 화면 대신 파일로 출력 (덮어쓰기)

$(...)는 "괄호 안의 명령을 실행하고 그 결과로 치환"입니다. 프로그래밍이 아니라, 터미널에서 치던 명령을 파일에 옮겨 적은 것뿐이니 겁먹지 마세요.

2-3. 해시로 파일명 짓기 — md5sum의 등장

md5sum은 데이터의 해시(고정 길이의 지문 같은 값)를 계산합니다. 같은 입력은 항상 같은 해시를 냅니다. 스크립트가 "사용자 이름의 MD5 값으로 파일명을 짓는다"면, 우리는 손으로 같은 계산을 해서 그 파일명을 미리 알아낼 수 있습니다. 암호가 아니라 주소록인 셈입니다.

2-4. 끼어들기의 세 조건

오늘의 핵심 공식입니다. cron 악용은 이 셋이 모일 때 성립합니다.

  1. 높은 권한으로 도는 자동 작업이 있다
  2. 그 작업이 내가 쓸 수 있는 위치의 파일을 실행하거나 읽는다
  3. 결과물이 내가 읽을 수 있는 곳으로 배달된다

셋 중 하나라도 깨지면 공격은 성립하지 않습니다. 방어도 결국 이 셋 중 하나를 깨는 일입니다 — 공격 지도와 방어 점검표가 같은 그림입니다.


3. 따라 하기

3-1. Level 21 → 22 — 남의 일정표 엿보기

입력 (서버 안에서, 출력 예시)

ls /etc/cron.d/
cat /etc/cron.d/cronjob_bandit22

매분 bandit22 권한으로 어떤 스크립트를 실행한다는 한 줄이 보입니다. 그 스크립트를 읽습니다.

입력 (출력 예시)

cat /usr/bin/cronjob_bandit22.sh

읽는 법: 스크립트를 따라가면 비밀번호를 어떤 파일에 복사하고 있습니다. 그 파일의 경로가 이 레벨의 답입니다. "일정표(cron 설정) → 실행물(스크립트) → 결과물(출력 파일)"의 3단 추적 — 공격자의 정찰은 이 순서로 흐릅니다.

3-2. 내 컴퓨터의 cron.d 읽기

같은 읽기를 내 컴퓨터에서도 해 봅시다 (2026-09-09 WSL 실측, Ubuntu 24.04):

ls /etc/cron.d/
cat /etc/cron.d/e2scrub_all

출력 (2026-09-09 실측):

e2scrub_all
30 3 * * 0 root test -e /run/systemd/system || SERVICE_MODE=1 /usr/lib/x86_64-linux-gnu/e2fsprogs/e2scrub_all_cron
10 3 * * * root test -e /run/systemd/system || SERVICE_MODE=1 /sbin/e2scrub_all -A -r

읽는 법: 첫 줄은 "매주 일요일 새벽 3시 30분", 둘째 줄은 "매일 새벽 3시 10분"에 root 권한으로 디스크 점검 도구를 돌리는 예약입니다. 중간의 root가 권한 칸입니다 — 내 컴퓨터에도 이런 자동 작업이 조용히 살고 있습니다.

3-3. Level 22 → 23 — 결과물을 찾아 읽기

3-1에서 파악한 경로를 읽습니다 (서버 안, 출력 예시).

cat /tmp/t7O6lds9S0RqQj9aGh5PYeFZqZl2V6xW   # 실제 경로는 스크립트를 읽고 확인

읽는 법: cron이 매분 그 파일을 새로 고치므로, 내용이 없거나 오래됐으면 1분 기다렸다 다시 봅니다. 자동 작업의 결과물은 "조용히 갱신되는 게시판"입니다. cron의 시계 속도를 배우는 구간입니다 — 답이 1분 단위로 배달됩니다.

3-4. Level 23 → 24 — 파일명을 손으로 계산하기

이번 스크립트의 핵심 부분입니다 (서버 안, 출력 예시).

myname=$(whoami)
mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)
# ... 비밀번호를 /tmp/$mytarget 에 복사

cron을 기다리지 않고 미리 계산합니다. 이 계산은 서버가 아니라 내 컴퓨터에서도 완전히 동일합니다 (2026-09-09 WSL 실측):

echo "I am user bandit23" | md5sum | cut -d ' ' -f 1

출력 (2026-09-09 실측):

8ca319486bfbbc3663ea0fbe81326349

읽는 법: 이 32자리 16진수가 스크립트가 bandit23으로 돌 때 만들 파일명입니다. /tmp/8ca319486bfbbc3663ea0fbe81326349를 읽으면 됩니다. cut -d ' ' -f 1은 "공백으로 잘라 첫 번째 조각" — md5sum 출력 뒤에 붙는 -를 떼는 역할입니다.

: "프로그램의 규칙을 읽고, 그 규칙을 나에게 유리하게 돌리는 것" — 해커 사고방식의 정수입니다. 공격은 대부분 파괴가 아니라 규칙의 재활용입니다. 참고로 한 글자라도 바꾸면 해시는 완전히 달라집니다 — "I am user bandit24"ee4ee1703b083edac9f8183e4ae70293입니다 (2026-09-09 실측). 공백·대소문자까지 전부가 해시를 바꿉니다.

3-5. Level 24 → 25 — 내가 일정에 끼어들기

드디어 역공입니다. 이번 cron은 "/var/spool/bandit24/에 놓인 스크립트를 전부 실행하고 지운다"고 합니다.

입력 (서버 안에서, 출력 예시)

mkdir /tmp/mystage
cat > /var/spool/bandit24/myjob.sh <<'EOF'
#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/mystage/password
EOF
chmod +x /var/spool/bandit24/myjob.sh

1~2분 기다리면 /tmp/mystage/password가 생깁니다 — bandit24 권한으로 읽은 비밀번호가 담겨 있습니다.

읽는 법: 내가 쓴 스크립트를 bandit24 권한의 cron이 대신 실행해 준 것입니다. <<'EOF'는 여러 줄을 한 번에 파일에 쓰는 입력 기법(히어독)이고, chmod +x는 실행 권한 부여입니다 — 둘 다 필수입니다.

이 구조를 내 컴퓨터에서 안전하게 모사했습니다. 스풀 폴더에 스크립트를 심고 실행되는 모습입니다 (2026-09-09 WSL 실측):

mkdir -p /tmp/spool /tmp/mystage
cat > /tmp/spool/myjob.sh <<'EOF'
#!/bin/bash
echo "simulated_secret_password" > /tmp/mystage/password
EOF
chmod +x /tmp/spool/myjob.sh
/tmp/spool/myjob.sh        # cron 역할을 손으로 대신함
cat /tmp/mystage/password

출력 (2026-09-09 실측):

simulated_secret_password

스크립트가 놓인 곳에서 실행되어 결과물이 다른 폴더로 배달되는 전체 사슬이 재현됐습니다.

3-6. 예측해 보기 — 왜 /tmp에 두었을까

3-5에서 출력 경로를 내 홈 폴더가 아닌 /tmp/mystage로 한 이유는 무엇일까요?

  • (a) 습관
  • (b) cron이 도는 bandit24도 그곳에 쓸 수 있어야 하므로
  • (c) 숨기려고

직접 확인 (2026-09-09 WSL 실측):

ls -ld /tmp
drwxrwxrwt 10 root root 4096 Sep  9 14:32 /tmp

drwxrwxrwt — 누구나 읽고 쓸 수 있는 공용 폴더입니다. 정답은 (b). 내 홈 폴더는 bandit24가 쓸 수 없어 결과물이 만들어지지 않습니다.

왜 중요한가: "실행 권한"만이 아니라 "결과물의 배달 경로"까지 권한이 맞아야 체인이 완성됩니다. 권한은 점이 아니라 선입니다 — 시작부터 끝까지 모든 구간이 이어져야 합니다.

3-7. Level 25 → 26 — 제한된 환경의 부품이 탈출구

마지막은 이례적인 문제입니다. bandit26의 로그인 쉘이 일반 쉘이 아닙니다.

입력 (서버 안, 출력 예시)

grep bandit26 /etc/passwd

맨 끝이 /bin/bash가 아닌 다른 무언가 — 그것이 이 계정의 제한된 쉘입니다. 그 프로그램이 내부적으로 more(긴 텍스트를 쪽 나눠 보여 주는 도구)를 쓴다면, more는 "터미널 창보다 내용이 길 때만" 등장합니다. 터미널 창을 아주 작게 줄이고 접속하면 more 상태에 갇히고, 그 상태에서 v를 누르면 편집기(vim)가 열립니다. vim에서는 :e /etc/bandit_pass/bandit26으로 파일을 읽는 길이 있습니다.

읽는 법: 막힌 문을 찾지 말고, 문을 구성하는 부품을 보라는 교훈입니다. 제한 환경의 부품(more)이 오히려 탈출구였습니다. 못 풀어도 좋습니다 — 사고방식만 가져가세요. vim에 갇히면 Esc:q! + Enter가 탈출 주문입니다.


4. 미션과 연습문제

미션 — Bandit 21→26 체인과 cron 악용 체크리스트

  1. bandit25까지 체인을 완성하고 각 레벨을 write-up 형식으로 기록합니다
  2. 내 컴퓨터의 /etc/cron.d/를 읽고 각 작업의 "누구 권한으로 무엇을"을 표로 해석합니다
  3. 3-4의 md5sum 계산과 3-5의 스풀 실험을 로컬에서 재현합니다
  4. 위키에 cron 악용 체크리스트 한 장을 씁니다 — 포함할 항목: ① 일정표 확인(/etc/cron.d/와 "누구 권한으로") ② 실행물 분석(스크립트의 입출력 경로) ③ 끼어들기 조건 3종 ④ 결과 배달 경로의 권한 ⑤ cron 주기만큼의 기다림

연습문제

문제 1. cron 설정 한 줄 * * * * * bandit22 /usr/bin/x.sh의 각 부분(다섯 칸, 사용자, 명령)을 해석해 보세요.

문제 2. echo "I am user bandit23" | md5sum | cut -d ' ' -f 1에서 cut은 왜 필요한가요?

문제 3. cron 끼어들기의 세 조건을 말하고, 각 조건을 깨는 방어책을 하나씩 짝지어 보세요.

문제 4. 3-6에서 결과 경로가 /tmp여야 했던 이유를 "권한은 선이다"라는 문장과 연결해 설명해 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

체인의 뼈대 (서버 안 명령은 출력 예시):

cat /etc/cron.d/cronjob_bandit22              # L21→22: 스크립트 경로 파악
cat /usr/bin/cronjob_bandit22.sh              # → 결과물 경로 파악 후 cat
echo "I am user bandit23" | md5sum | cut -d ' ' -f 1   # L23→24: 파일명 선계산
# L24→25: /var/spool/bandit24/에 히어독으로 스크립트를 심고 1~2분 대기

검증하는 법: ① 체크리스트 한 장에 다섯 항목이 전부 있는가. ② 내 컴퓨터 cron.d 표에 "권한 칸" 해석이 붙어 있는가 (3-2절 실측처럼). ③ 로컬 재현에서 히어독으로 만든 스크립트가 실행되어 결과 파일이 생겼는가. ④ 실험에 쓴 스크립트와 폴더를 정리했는가.

연습문제 해답

문제 1 해답. 다섯 개의 별은 "분 시 일 월 요일"이 전부 매번 — 즉 매분 실행이라는 일정이고, bandit22는 이 작업이 도는 권한의 주인이며, 끝의 경로가 매분 실행되는 것입니다. 읽기의 핵심은 가운데 권한 칸입니다.

문제 2 해답. md5sum의 출력은 해시 뒤에 파일명(파이프 입력이라 -)이 붙기 때문입니다. 8ca3...6349 -에서 해시만 떼어 내려면 "공백으로 잘라 첫 조각"이 필요합니다 (2026-09-09 실측 출력 형태 참조).

문제 3 해답. 조건: ① 높은 권한의 자동 작업 ② 내가 쓸 수 있는 위치의 실행물 ③ 내가 읽을 수 있는 배달 경로. 방어: ① 작업 권한 최소화(꼭 필요한 권한만) ② 스크립트·스풀 폴더의 쓰기 권한을 주인만으로 ③ 결과물을 공용 폴더가 아닌 권한 있는 위치에. 셋 중 하나만 깨도 체인은 끊깁니다.

문제 4 해답. 실행은 bandit24 권한으로 일어나지만, 결과 파일을 만드는 행위도 bandit24 권한으로 일어납니다. 내 홈 폴더는 bandit24가 쓸 수 없으니 그 구간에서 선이 끊깁니다. /tmp는 누구나 쓸 수 있어(drwxrwxrwt, 2026-09-09 실측) 선이 끝까지 이어집니다. 공격 체인은 모든 구간의 권한이 연결되어야 완성됩니다.

완료 기준 체크리스트

  • [ ] /etc/cron.d/를 읽고 "누구 권한으로 무엇이 언제"를 해석할 수 있다
  • [ ] 셸 스크립트의 입출력 경로를 읽어 낼 수 있다
  • [ ] 해시로 만들어지는 파일명을 손으로 미리 계산할 수 있다
  • [ ] 히어독(<<'EOF')으로 여러 줄 스크립트를 파일로 쓸 수 있다
  • [ ] 끼어들기 세 조건을 설명하고 방어책과 짝지을 수 있다
  • [ ] 권한이 점이 아니라 선임을 /tmp 권한 예시로 설명할 수 있다
  • [ ] 미션: 체인 완성과 cron 악용 체크리스트 한 장을 완성했다

6. 흔한 실수와 해결

벽 1. 내 스크립트가 사라졌는데 결과가 없다

증상: cron이 실행한 것 같은데(스풀의 파일이 지워짐) 결과 파일이 없습니다.
원인: 스크립트 안의 경로 오타, 실행 권한(chmod +x) 누락, 결과 폴더 미생성, 또는 상대 경로 사용(cron의 작업 폴더는 내 예상과 다릅니다).
해결: 경로는 전부 절대 경로로, 결과 폴더는 미리 mkdir로, 스크립트 첫 줄 #!/bin/bash를 확인하세요. 그리고 1~2분 기다리는 것을 잊지 마세요 — cron의 시계는 분 단위입니다.

벽 2. md5sum으로 계산했는데 그런 파일이 없다

증상: 파일명을 계산했는데 /tmp에 없습니다.
원인: 공식을 한 글자라도 다르게 입력했거나(띄어쓰기·대소문자 포함), 아직 cron이 안 돌았거나.
해결: 스크립트 원문의 echo 부분을 글자 단위로 대조하세요. 한 글자 차이로 해시가 완전히 바뀌는 것을 3-4절 실측(bandit23 vs bandit24)으로 확인했습니다. 그리고 1분 기다렸다 다시 확인하세요.

벽 3. cron 설정 파일을 못 찾겠다

증상: /etc/cron.d/에 안내와 다른 파일명만 보입니다.
원인: 서버 구성은 시기에 따라 조금씩 달라질 수 있습니다.
해결: ls /etc/cron.d/ 전체 목록에서 다음 레벨 번호가 들어간 이름을 찾으세요. 정 안 보이면 공식 레벨 페이지의 힌트를 다시 확인합니다.

벽 4. more에서 v를 눌러도 반응이 없다

증상: Level 25의 트릭이 안 먹힙니다.
원인: more 상태가 아닙니다 — 창이 충분히 커서 텍스트가 한 화면에 다 나왔거나, 키 입력이 더보기 프로그램에 전달되지 않았습니다.
해결: 터미널 창의 높이를 2~3줄로 극단적으로 줄이고 다시 접속하세요. 텍스트가 창을 넘어야만 more가 등장합니다.

벽 5. 히어독으로 쓴 파일이 비어 있다

증상: cat > 파일 <<'EOF'를 쳤는데 내용이 안 들어갔습니다.
원인: 마지막 EOF를 줄 맨 앞에 쓰지 않았거나(앞에 공백이 있으면 안 됩니다), 따옴표 없는 <<EOF와 변수 치환 규칙이 섞였거나.
해결: 닫는 EOF는 반드시 줄 맨 앞에 단독으로. 오늘의 용도(스크립트 그대로 쓰기)에는 'EOF'처럼 따옴표를 붙인 형태가 안전합니다 (3-5절 실측 절차 그대로 따라 하세요).


7. 정리

오늘의 개념

개념 한 줄 설명
cron 정해진 시각에 명령을 실행하는 시스템의 알람 시계
/etc/cron.d/ 시스템 전역 일정표 폴더 — 한 줄이 하나의 예약
셸 스크립트 명령을 순서대로 적은 텍스트 파일 — #!/bin/bash로 시작
md5sum 고정 길이 해시 계산 — 같은 입력은 항상 같은 값
권한 체인 실행부터 결과 배달까지 이어지는 권한의 선
지속성(persistence) 자동화에 심어 두는 유지 기법 — 연습장 밖에서는 백도어

오늘의 명령어

명령 하는 일
ls /etc/cron.d/ 일정표 목록 보기
cat /etc/cron.d/파일 예약 내용 읽기
echo "..." | md5sum | cut -d ' ' -f 1 해시만 떼어 내기
cat > 파일 <<'EOF' 여러 줄을 한 번에 파일로 (히어독)
chmod +x 파일 실행 권한 부여
ls -ld /tmp 폴더 자체의 권한 보기

명령어보다 중요한 감각

오늘의 추적 순서를 몸에 새기세요 — 일정표 → 실행물 → 결과물. 그리고 스크립트를 발견하면 읽기 전에 먼저 ls -l로 "누가 쓸 수 있는가"를 확인하는 습관. 쓰기 가능한 스크립트 + 높은 권한의 cron은 이미 열린 문입니다. Level 20의 "내가 기다리고 상대가 접속"과 오늘의 "자동 작업이 내 파일을 실행"을 합치면 보입니다 — 공격의 상당수는 정상적인 흐름에 내 것을 태우는 것입니다. 이 구조로 묶어 두면, 새 기법을 만나도 "아, 또 태우는 거네"로 읽히기 시작합니다. Step 100, 백 번째 문입니다. 파일 찾기로 시작한 여정이 어느새 "시스템의 흐름을 읽고 태우는" 수준에 와 있습니다.


전부 체크되면 Step 100 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.