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 악용은 이 셋이 모일 때 성립합니다.
- 높은 권한으로 도는 자동 작업이 있다
- 그 작업이 내가 쓸 수 있는 위치의 파일을 실행하거나 읽는다
- 결과물이 내가 읽을 수 있는 곳으로 배달된다
셋 중 하나라도 깨지면 공격은 성립하지 않습니다. 방어도 결국 이 셋 중 하나를 깨는 일입니다 — 공격 지도와 방어 점검표가 같은 그림입니다.
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 악용 체크리스트
- bandit25까지 체인을 완성하고 각 레벨을 write-up 형식으로 기록합니다
- 내 컴퓨터의
/etc/cron.d/를 읽고 각 작업의 "누구 권한으로 무엇을"을 표로 해석합니다 - 3-4의 md5sum 계산과 3-5의 스풀 실험을 로컬에서 재현합니다
- 위키에 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 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.