웹 서버 한 대가 뚫리는 것과 시스템 전체가 장악되는 것 사이에는 보통 "셸을 잡았는가"라는 분기점이 있다.
공격자가 코드 실행에 성공해도, 거기서 대화형 셸을 띄울 수 없다면 공격 체인은 그 자리에서 멈춘다.
시스템 계정의 로그인 셸 하나를 /sbin/nologin으로 바꾸는 단순한 작업이 방어선으로서 의미를 갖는 이유다.
대화형 로그인의 정의부터 시스템 계정에 이것이 허용될 때 발생하는 구체적 위험, 그리고 실무에서의 차단 방법을 알아보자.
1. 대화형 로그인 개념
1️⃣ 대화형 로그인
대화형 로그인(Interactive Login)은 사람이 터미널, 콘솔, SSH 등을 통해 키보드로 직접 인증하고 셸을 획득하는 방식이다. 인증에 성공하면 /bin/bash 같은 로그인 셸이 할당되고, 사용자는 그 셸에서 임의의 명령어를 입력할 수 있는 환경을 얻는다.
2️⃣ 비대화형 로그인
대화형 로그인의 반대 개념은 비대화형(Non-interactive) 로그인이다.
서비스나 스크립트가 특정 작업만 수행하고 셸은 열지 않는 방식으로, 인증 주체가 사람이 아니라 프로세스다.
두 방식의 차이는 "명령 실행 환경(셸)이 주어지는가"에 있다.
대화형 로그인에서 인증 → 셸 할당 → 자유로운 명령 실행으로 이어지는 흐름과, 비대화형에서 지정된 작업만 수행하고 종료되는 흐름을 비교하면 다음과 같다.

2. 시스템 계정에서 대화형 로그인이 위험한 이유
1️⃣ 시스템 계정의 본래 성격
시스템 계정은 www-data, nginx, mysql, nobody처럼 서비스 실행 전용으로 만들어진 계정이다.
웹 서버 프로세스, DB 데몬 등이 최소 권한으로 동작하도록 분리해 둔 계정으로 사람이 로그인해서 작업하는 계정이 아니다.
따라서 시스템 계정(서비스 계정)에 대화형 로그인이 열려 있다는 것은 본래 의도되지 않은 통로가 존재한다는 뜻이다.
2️⃣ 공격자의 측면 이동 경로가 생긴다
웹 애플리케이션이 Command Injection 같은 취약점으로 뚫리면, 주입된 명령은 웹 서버 프로세스의 권한 즉 www-data로 실행된다.
이때 www-data에 대화형 로그인이 허용돼 있으면 공격자는 /bin/bash를 호출해 곧바로 셸을 획득한다. CMDi에서 리버스 셸로 이어지는 전형적인 시나리오가 바로 이 경로다.
CMDi 취약점에서 출발해 리버스 셸 획득까지 이어지는 단계를 시퀀스로 정리하면 다음과 같다.

3️⃣ 셸 히스토리와 환경 변수 노출
대화형 셸이 열리면 그 안에서 접근 가능한 정보가 그대로 공격자에게 넘어간다.
.bash_history에는 과거 입력한 명령어가, env 명령어 결과에는 프로세스에 주입된 환경 변수가 노출된다.
특히 환경 변수에 DB 접속 패스워드나 API 키 같은 자격증명이 저장되어 있는 경우, 셸 하나로 추가 인증 없이 내부 시스템 접근 정보까지 수집할 수 있다.
4️⃣ 권한 상승의 발판
대부분의 권한 상승(PrivEsc) 기법은 셸이라는 실행 환경을 전제로 한다.
SUID 바이너리 탐색(find / -perm -4000), sudo 설정 오용, cron job 탈취 같은 기법은 모두 셸에서 명령을 실행하며 권한 상승 경로를 찾아 나가는 과정이다.
대화형 로그인을 차단하면 이런 탐색 자체가 시작되지 못하므로, 초기 침투에 성공한 공격자라도 그 권한에 묶인 채 더 이상 나아가지 못한다.
5️⃣ 감사 추적의 어려움
시스템 계정의 대화형 로그인이 정상 동작으로 허용돼 있으면, 공격자가 그 계정으로 로그인해도 정상 트래픽과 구별하기 어렵다.
반대로 시스템 계정의 로그인 셸이 원천 차단돼 있다면, 해당 계정으로 셸을 띄우려는 시도 자체가 비정상 신호가 되어 탐지 지점으로 활용할 수 있다.
3. 실무 대응 방법
1️⃣ 로그인 셸 차단
기본 대응은 /etc/passwd에 정의된 시스템 계정의 로그인 셸을 막는 것이다.
로그인 셸을 /sbin/nologin 또는 /bin/false로 지정하면, 인증이 통과되더라도 할당할 셸이 없으므로 대화형 세션이 성립하지 않는다.
# 대화형 로그인 차단: 셸을 /sbin/nologin 또는 /bin/false로 설정
usermod -s /sbin/nologin www-data
usermod -s /sbin/nologin mysql
🟢 /sbin/nologin과 /bin/false의 차이
/sbin/nologin은 로그인을 거부하면서 안내 메시지를 출력하고 종료한다(필요 시 /etc/nologin.txt로 메시지 지정 가능). /bin/false는 아무 메시지 없이 즉시 실패 상태로 종료한다.
두 경우 모두 대화형 셸은 열리지 않으며, 서비스 데몬은 셸을 거치지 않고 직접 실행되므로 정상 동작에 영향받지 않는다.
2️⃣ SSH forced command - 계정은 유지하되 명령은 제한
계정 자체를 삭제하지 않고 "존재하되 자유로운 셸은 막는" 접근도 있다.
지난 SOAR 구축 실습에서 적용한 SSH forced command가 그 예다.
authorized_keys의 command= 옵션으로 soaradmin 계정이 특정 스크립트(soar_iptables.sh)만 실행하도록 강제하면, 해당 키로 접속이 성공해도 지정된 명령 외에는 아무것도 실행할 수 없다.
# ~/.ssh/authorized_keys
command="/opt/soar/soar_iptables.sh",no-port-forwarding,no-pty ssh-ed25519 AAAA...
🟢 로그인 셸 차단과 forced command의 관계
두 방식은 "자유로운 대화형 셸을 허용하지 않는다"는 동일한 원리를 공유한다.
로그인 셸 차단은 셸 할당 자체를 없애 사람의 접속을 막는 쪽이고, forced command는 자동화된 원격 실행은 허용하되 그 실행을 단일 명령으로 묶어 두는 쪽이다.
mini-SOAR처럼 원격 시스템이 방화벽 대응 명령을 실행해야 하지만 그 외 권한은 주고 싶지 않을 때 forced command가 적합하다.
세 가지 차단 방식이 공격 표면을 어떻게 줄이는지 정리하면 다음과 같다.

4. 정리
대화형 로그인 차단은 공격 체인의 한 마디를 끊는 구조적 조치다.
초기 침투(CMDi, 취약점 익스플로잇)는 막지 못하더라도, 그 다음 단계인 셸 획득 → 정보 수집 → 권한 상승으로 이어지는 흐름을 시작부터 차단한다.
핵심 원칙은 "서비스 계정에는 사람이 쓰는 셸을 주지 않는다"는 것이다.
서비스 전용 계정은 usermod -s /sbin/nologin으로 셸을 제거하고, 자동화 목적의 원격 실행이 필요한 계정은 SSH forced command로 실행 가능한 명령을 단일 스크립트로 고정한다.
두 방식 모두 계정의 정상 기능은 유지하면서 공격자가 활용할 수 있는 자유로운 명령 실행 환경만 선택적으로 제거한다는 동일한 원칙에 기반한다.
'Journey to Security > 취약점진단|모의해킹' 카테고리의 다른 글
| Spring Boot 사내 포털 모의침투 - 공격 체인을 끊는 4단계 패치 (0) | 2026.06.30 |
|---|---|
| Spring Boot 사내 포털 모의침투 - MITRE ATT&CK 기반 공격 시나리오 (0) | 2026.06.29 |
| DVWA 문제풀이: Command Injection (0) | 2026.05.25 |
| DVWA 문제풀이: Netcat을 이용한 Reverse Shell 공격 (0) | 2026.05.25 |
| DVWA 애플리케이션으로 웹 취약점 실습하기 (0) | 2026.05.25 |