LabHub
배우기 러닝패스 코스

cron が午前三時に curl を叩いた

残る場所と残らない場所を切り分ける

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

이 실습은 VM 에서 돕니다

우분투 24.04 VM 한 대입니다. root 이고 커널 권한이 있어서 감사 규칙을 실제로 올릴 수 있습니다. 실습 파드에는 capability 가 하나도 없어 auditctl 이 아예 동작하지 않기 때문에 이 실습만 VM 으로 돕니다. 처음 뜨는 데 1~2분 걸립니다.

목표

커널 감사 규칙을 직접 걸고, 사건을 일부러 일으키고, 그 기록을 꺼내 읽습니다. 같은 사건을 저널 쪽에서 다시 찾아 두 기록이 무엇을 각각 갖고 있는지 보고, 마지막으로 감시하지 않는 자리를 찾아 그것을 덮습니다.

왜 중요한가

탐지 규칙을 쓰기 전에 답해야 하는 질문이 있습니다 — 그 사건이 애초에 어딘가에 기록되는가. 리눅스에서 커널 감사는 기본으로 거의 아무것도 남기지 않습니다. 규칙을 걸어 둔 경로와 시스콜만 봅니다. 그래서 "로그를 다 모으고 있는데 답이 없다" 는 상황이 생깁니다.

여기서 배우는 것은 명령어가 아니라 범위 감각입니다. 내 규칙이 무엇을 보고 무엇을 안 보는지 말할 수 있으면, 사건이 났을 때 "기록이 없다" 와 "그 자리를 안 보고 있었다" 를 구별할 수 있습니다. 이 둘은 전혀 다른 처방으로 이어집니다.

단계

  1. auditctl -s 와 서비스 상태를 /root/det/01-status.txt 에 저장하세요.
  2. /etc/cron.d 를 쓰기·속성 변경에 대해 감시하는 규칙을 키 cron_change/etc/audit/rules.d/labhub.rules 에 적고 커널에 올리세요.
  3. 64비트 execve 중 실행 파일이 /usr/bin/curl 인 것만 잡는 규칙을 키 outbound_exec 로 같은 파일에 더하고 올리세요.
  4. /etc/cron.d/nightly-report 를 만들어 curl 을 부르게 하고, curl 을 손으로 한 번 실행한 뒤 무엇을 했는지 /root/det/04-trigger.txt 에 적으세요.
  5. ausearch -k outbound_exec -i 의 마지막 사건을 /root/det/05-evidence.txt 에 저장하고 그 아래에 pid=·exe=·key= 세 값을 적으세요.
  6. 저널에서 cron 의 실행 기록을 JSON 으로 뽑아 /root/det/06-cron.json 에 저장하세요. 이 VM 에서 매분 도는 작업(labhub-metrics)의 실행 줄이 들어 있어야 합니다.
  7. /usr/local/bin/labhub-helper.sh 를 만들어 실행 비트를 주고, cron_change 키로는 그 기록이 남지 않는다는 것을 확인해 /root/det/07-blindspot.txt 에 명령·결과·이유를 적으세요.
  8. /usr/local/bin 을 키 persistence 로 감시하는 규칙을 더해 올리고, 파일을 한 번 더 고쳐 기록이 남는 것을 /root/det/08-closed.txt 에 확인하세요.
  9. 이 호스트의 탐지 범위를 /root/det/09-report.md## 무엇이 남는가·## 무엇이 남지 않는가·## 규칙 목록·## 다음에 할 일 네 절로 쓰세요.

참고

무엇이 켜져 있는지

auditctl -s 의 출력과 systemctl is-active auditd 결과를 /root/det/01-status.txt 에 저장하세요. enabledbacklog_limit 값이 그대로 들어 있어야 합니다.

auditctl -s 는 커널 감사 서브시스템의 현재 상태를 한 줄씩 냅니다. 두 명령의 출력을 한 파일에 이어 붙이면 됩니다.

예약 작업 디렉터리를 감시한다

/etc/cron.d 를 쓰기(w)와 속성 변경(a)에 대해 감시하고 키를 cron_change 로 붙이는 규칙을 /etc/audit/rules.d/labhub.rules 에 적고 커널에 올리세요.

파일 감시 규칙은 -w <경로> -p <권한> -k <키> 꼴입니다. rules.d 에 적은 것을 커널에 올리는 명령은 augenrules --load 이고, 올라간 규칙은 auditctl -l 로 확인합니다.

무엇이 실행됐는가

64비트 execve 중 실행 파일이 /usr/bin/curl 인 것만 잡는 규칙을 키 outbound_exec/etc/audit/rules.d/labhub.rules 에 더하고 커널에 올리세요.

시스콜 규칙은 -a always,exit -F arch=b64 -S <시스콜> -F <필터> -k <키> 꼴입니다. 실행 파일 경로로 좁히는 필터 이름은 exe 입니다. 파일 감시로는 '무엇이 실행됐는가' 가 안 보입니다.

사건을 일으킨다

/etc/cron.d/nightly-report 를 만들어 새벽 세 시에 curl 을 부르게 하고, 이어서 curl 을 손으로 한 번 실행하세요. 그리고 무엇을 했는지 두 줄 이상으로 /root/det/04-trigger.txt 에 적으세요.

규칙을 올린 뒤에 파일을 만들어야 기록이 남습니다. curl 은 실제로 어딘가에 닿을 필요가 없습니다 — 실행됐다는 사실이 execve 로 남습니다.

기록을 꺼내 읽는다

ausearch -k outbound_exec -i 의 마지막 사건을 /root/det/05-evidence.txt 에 저장하고, 그 아래에 pid=·exe=·key= 세 값을 각각 한 줄로 적으세요.

-i 를 빼면 uid 와 시스콜 번호가 숫자로 나옵니다. 마지막 사건만 보려면 ausearch 결과를 awk/sed 로 마지막 ---- 구분자 뒤만 남기거나 tail 로 자르면 됩니다.

같은 사건을 저널에서

저널에서 cron 이 남긴 실행 기록을 JSON 으로 뽑아 /root/det/06-cron.json 에 저장하세요. 이 VM 에서 매분 도는 작업(labhub-metrics)의 CMD 줄이 들어 있어야 합니다.

journalctl -t CRON -o json --since "-30 min" 이면 한 줄에 하나씩 JSON 이 나옵니다. 아직 안 돌았다면 1분 기다렸다가 다시 뽑으세요.

감시하지 않는 자리

/usr/local/bin/labhub-helper.sh 를 만들어 실행 비트를 주고, ausearch -k cron_change 에 그 경로가 나오지 않는다는 것을 확인해 /root/det/07-blindspot.txt 에 명령·결과·왜 안 남는지를 120자 이상으로 적으세요.

감사 규칙은 적어 둔 경로만 봅니다. 공격자는 예약 작업을 새로 만들 필요 없이 이미 예약된 스크립트의 내용을 고치면 됩니다. 그 자리가 지금 감시 밖이라는 것을 직접 확인하는 단계입니다.

빈 자리를 덮는다

/usr/local/bin 을 쓰기·속성 변경에 대해 감시하고 키를 persistence 로 붙이는 규칙을 /etc/audit/rules.d/labhub.rules 에 더해 올리고, labhub-helper.sh 를 한 번 더 고쳐 기록이 남는 것을 /root/det/08-closed.txt 에 확인하세요.

키를 cron_change 로 하면 7단계에서 확인한 '안 남는다' 가 뒤집혀 앞 단계가 깨집니다. 새 자리는 새 키로 덮습니다. 규칙을 올린 뒤에 파일을 고쳐야 기록이 남습니다.

탐지 범위를 문서로

이 호스트에서 지금 무엇이 남고 무엇이 안 남는지를 /root/det/09-report.md## 무엇이 남는가·## 무엇이 남지 않는가·## 규칙 목록·## 다음에 할 일 네 절로 쓰세요. 세 키 이름과 규칙 파일 경로가 본문에 나와야 합니다.

규칙 목록 절에는 auditctl -l 결과를 붙이면 됩니다. '무엇이 남지 않는가' 가 이 문서에서 가장 값어치 있는 절입니다 — 다음 사람이 '다 남는다' 고 믿지 않게 해 주는 자리입니다.