시스템 보안 · 취약점 › I. 지속성 · 재침투 징후 · 21/50편 (전체 421/450)
학습 단계: 스케줄·서비스·부팅 지속성 탐지
실습 표기: 이 글의 명령어·출력·로그는 로컬 VMware 테스트 VM(Rocky Linux 9 / Ubuntu 22.04) 기준의 「실습 예시」이며, IP·계정·호스트명은 가상의 값입니다.
현대 Linux에서 부팅 시 실행의 중심은 systemd입니다. 공격자는 정상처럼 보이는 이름의 서비스 유닛을 만들고 enable해 재부팅 후에도 자동 실행되게 합니다(T1543.002). Restart=always를 넣으면 프로세스를 죽여도 다시 살아납니다.
악성 유닛 예시
/etc/systemd/system/dbus-helper.service
[Service]
ExecStart=/usr/local/lib/.cache/dbus-helper
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
↓ systemctl enable → multi-user.target.wants/ 심볼릭 링크 생성
핵심: 유닛 파일 위치·ExecStart 경로·패키지 소유가 판정 기준
F영역 20편과 A영역 40편이 실행·신규 서비스 관점이었다면, 이번 편은 재부팅 후 재기동 장치로서 유닛을 보고, enable 링크와 Restart 설정까지 확인합니다.
| 확인 | 명령 | 포인트 |
|---|---|---|
| enabled 목록 | systemctl list-unit-files --state=enabled | 기준선 diff |
| 유닛 내용 | systemctl cat <unit> | ExecStart·Restart |
| 소유 | rpm -qf <unit file> | 패키지 미소유 |
| enable 링크 | ls -la /etc/systemd/system/*.wants/ | 신규 링크 |
| 실행 파일 | rpm -qf <ExecStart 경로> | 숨김·비표준 경로 |
# systemd 서비스 지속성 점검 (분석 방법)
systemctl list-unit-files --state=enabled --no-legend | awk '{print $1}' | sort > /tmp/units_now
diff <(awk '{print $1}' /root/baseline/persist-*/units.txt | sort -u) /tmp/units_now
for f in /etc/systemd/system/*.service; do
rpm -qf "$f" >/dev/null 2>&1 || dpkg -S "$f" >/dev/null 2>&1 || {
echo "== 미소유 유닛: $f"; grep -E '^(ExecStart|Restart|User)=' "$f"; }
done
sudo journalctl _PID=1 --since "-7d" | grep -E 'Reloading|Started .*helper|Created symlink' | tail
enabled: sshd.service, chronyd.service, wazuh-agent.service ... (기준선 일치)
미소유 유닛: 없음
enabled 유닛이 기준선과 같고, /etc/systemd/system의 유닛이 패키지 또는 승인된 배포로 만들어진 상태가 정상입니다.
> dbus-helper.service
== 미소유 유닛: /etc/systemd/system/dbus-helper.service
ExecStart=/usr/local/lib/.cache/dbus-helper
Restart=always
systemd[1]: Created symlink /etc/systemd/system/multi-user.target.wants/dbus-helper.service
systemd 서비스 지속성 분석 흐름입니다(가상의 예시).
[유닛 변경] /etc/systemd/system 감시 · enable 링크 생성 로그
↓
[유닛 해석] ExecStart · User · Restart · WantedBy
↓
[실행 파일] 경로·해시·패키지 소유·생성 시각
↓
[동작] journalctl -u <unit> 기동 기록 · 프로세스 네트워크 연결
| 필드 | 악성 신호 |
|---|---|
| ExecStart | 숨김·임시 경로 |
| Restart | always(재기동) |
| User | root |
| 소유 | 패키지 미소유 |
프로세스만 죽이면 Restart=always로 다시 살아나므로 반드시 systemctl disable --now로 유닛을 멈추고 비활성화한 뒤 파일을 격리해야 합니다.
<!-- 실습 예시 룰: 적용 전 wazuh-logtest 및 테스트 환경 검증 필요 -->
<group name="local,syssec_i,persistence,">
<rule id="108200" level="12">
<if_sid>108000</if_sid>
<field name="audit.key">persist_svc</field>
<field name="audit.file.name" type="pcre2">^/(etc|usr/lib|lib)/systemd/system/.*\.service$</field>
<field name="audit.exe" negate="yes" type="pcre2">/(rpm|dnf|yum|dpkg|apt)</field>
<description>패키지 관리자 외 systemd 서비스 유닛 생성·수정(서비스 지속성 의심)</description>
<mitre><id>T1543.002</id></mitre>
</rule>
</group>
패키지 설치로 생기는 유닛을 제외하고, 그 밖의 프로그램이 유닛을 만들면 경보합니다. systemctl enable이 만드는 .wants/ 링크도 같은 키로 감시하면 활성화 시점까지 확인할 수 있습니다.
systemctl disable --now로 중지·비활성화하고 유닛과 바이너리를 격리합니다.| 확인 | 기준 |
|---|---|
| 목록 | enabled 기준선 diff |
| 내용 | ExecStart·Restart |
| 소유 | 패키지 미소유 |
| 제거 | disable --now 후 격리 |
| 면접 포인트 | "유닛 이름이 아니라 ExecStart를 본다 — Restart=always는 죽여도 살아난다" |
다음 편 422. 지속성·재침투 — systemd timer 지속성 탐지 에서는 cron 대신 systemd timer로 만든 주기 실행 지속성을 list-timers·유닛 쌍·실행 기록으로 탐지하는 방법을 다룹니다.
이전 편: 420. 지속성·재침투 — at·batch 일회성 예약 실행 탐지
📚 시리즈 전체 보기: 시스템 보안 · 취약점