영화 같은 0-day 한 방으로 회사가 무너지는 일은 거의 없습니다. 침해 사고를 따라가 보면 시작점은 대부분 어이없을 만큼 사소합니다. 잊고 방치한 파일 하나, 끄지 않은 설정 한 줄, 바꾸지 않은 기본 비밀번호. 모의침투(펜테스트) 현장에서 가장 자주 발견되는 것들은 화려하지 않습니다.
이 글은 모의침투 보고서에 반복해서 등장하는 약점들을 방어자의 점검 목록으로 정리한 것입니다. 공격 기법을 가르치려는 글이 아니라, "내 사이트에서 무엇을 가장 먼저 막아야 하는가"를 알려주는 글입니다. 여기 나오는 모든 점검은 반드시 본인 소유의 자산에 대해서만 수행해야 합니다.
법적 경고: 모의침투는 대상 시스템 소유자의 명시적·서면 허가 하에서만 수행할 수 있습니다. 허가 없는 침투 시도는 의도와 무관하게 한국의 정보통신망법 등 여러 법률을 정면으로 위반하며 형사처벌 대상입니다. 이 글의 내용은 오직 자신의 시스템을 점검·강화하기 위한 방어 지식으로만 사용하십시오.
스캐너가 못 하는 일, 사람이 하는 일
자동화된 취약점 스캔은 빠르고 넓습니다. 알려진 패턴 수천 개를 몇 분 만에 훑습니다. 하지만 스캐너는 맥락을 모릅니다. 흩어진 단서 세 개를 하나로 엮지 못하고, "이 사소한 노출과 저 사소한 설정 오류를 합치면 무슨 일이 벌어질까"를 상상하지 못합니다.
바로 그 빈틈이 사람의 영역, 즉 모의침투의 영역입니다. 모의침투는 허가받은 보안 전문가가 실제 공격자처럼 시스템에 침투를 시도하면서, 자동 스캔이 개별 항목으로만 보고 넘기는 약점들과 그 조합을 찾아내는 작업입니다.
그리고 현장에서 가장 자주 마주치는 진실이 하나 있습니다.
회사를 무너뜨리는 것은 가장 정교한 취약점이 아니라 가장 기본적인 방치다.
노출된 채 잊힌 저장소, 운영 서버에 켜진 디버그 모드, admin/admin 그대로인 관리자 계정, 아무나 읽을 수 있게 열린 키 파일. 10년 전부터 알려진 문제들이 2026년 현재까지도 침해의 1순위 원인입니다. 아래에서 이 "흔한 발견들"을 카테고리별로 짚고, 각각을 어떻게 막는지 정리하겠습니다.
공격은 단계로 전개된다 — 그리고 단계마다 차단점이 있다
실제 침투가 어떤 순서로 진행되는지 알면, 각 단계에서 방어자가 어디에 빗장을 걸 수 있는지 보입니다. 모의침투와 실제 공격은 대체로 다음 흐름을 따릅니다.
| 단계 | 공격자가 하는 일 | 방어자의 차단점 |
|---|---|---|
| 정찰 (Reconnaissance) | 도메인·서브도메인 열거, 노출 자산·기술 스택·버전 수집, OSINT | 공격면 축소: 노출 정보·그림자 자산 정리, 버전 정보 은폐 |
| 스캔·열거 (Scanning) | 열린 포트·서비스 확인, 엔드포인트 열거, 알려진 취약점 노출 점검 | 불필요한 포트 닫기, 자가 진단으로 선제 발견 |
| 침투 (Exploitation) | 취약점 익스플로잇, 약한 인증 돌파, 노출된 자격 증명·오설정 악용 | 패치, 강한 인증, 올바른 설정 |
| 권한 상승 (Privilege Escalation) | 제한된 권한에서 관리자 권한으로 승격 | 최소 권한 원칙 |
| 측면 이동 (Lateral Movement) | 장악한 시스템에서 내부의 더 가치 있는 표적으로 이동 | 네트워크 분할, Zero Trust |
| 흔적 정리·지속 (Persistence) | 로그 삭제, 백도어 설치, 장기 잠복 | 변조 곤란한 로깅, 이상 탐지 모니터링 |
이 표가 주는 교훈은 분명합니다. 방어는 한 지점이 아니라 모든 단계에 걸쳐 있어야 합니다. 한 단계를 막지 못해도 다음 단계에서 막을 기회가 남습니다. 이것이 깊이 방어(defense in depth)의 핵심입니다. 그리고 위 표의 "공격자가 하는 일"을 거꾸로 읽으면, 그대로 자가 점검 순서가 됩니다.
1순위 발견: 노출되면 안 되는 파일과 디렉터리
현장에서 가장 흔하고, 가장 먼저 확인하는 것이 바로 "공개되어서는 안 될 파일의 노출"입니다.
.git 디렉터리 통째 노출
Git은 소스 코드의 모든 변경 이력을 .git이라는 숨김 폴더에 저장합니다. 배포할 때 이 폴더가 웹 루트에 따라 올라가고 웹 서버가 접근을 막지 않으면, 공격자는 이 디렉터리를 통째로 내려받아 전체 소스 코드와 모든 커밋 이력을 복원할 수 있습니다.
문제는 두 겹입니다. 첫째, 소스를 손에 넣으면 공격자는 코드 수준에서 모든 약점을 분석합니다. 둘째, 커밋 이력에는 과거에 실수로 넣었다가 지운 비밀번호나 키가 그대로 박제되어 있는 경우가 많습니다. 코드에서 지웠다고 이력에서 사라지는 게 아니기 때문입니다.
이게 얼마나 흔하냐면, git pull 한 번으로 배포를 끝내면서 저장소를 웹 루트에 그대로 두는 패턴이 대표적입니다. 확인은 1초면 됩니다.
# 본인 사이트에 대해서만 실행하세요
curl -s https://example.com/.git/config
curl -s https://example.com/.git/HEAD
내용이 보인다면 이미 노출된 것입니다. 이 경우 단순히 접근을 차단하는 것으로는 부족합니다. 그 안에 담겼던 비밀번호·키를 전부 폐기하고 새로 발급하십시오. 이미 누군가 받아 갔을 수 있기 때문입니다.
방어:
- 웹 서버에서
.git등 버전 관리 디렉터리 접근을 명시적으로 차단 - 더 근본적으로는 배포 산출물(공개 폴더)과 소스 저장소를 분리해
.git이 웹 루트에 아예 포함되지 않게 구성 - nuclei 같은 도구로 노출 여부를 정기 점검
.env·백업·덤프 파일
다음 표의 파일들은 웹에서 직접 접근 가능하게 노출되는 사고가 끊이지 않습니다.
| 파일 유형 | 예시 | 새어 나가는 것 |
|---|---|---|
| 환경 설정 파일 | .env |
DB 비밀번호, API 키, 클라우드 자격 증명 |
| 설정 백업본 | config.php.bak, settings.py.old |
과거 시점의 비밀번호·설정 전체 |
| DB 덤프 | backup.sql |
데이터베이스 내용 전부 |
| 편집기 임시 파일 | index.php~, .swp |
소스 코드 단편 |
노출된 .env 단 하나로 데이터베이스 전체와 클라우드 계정이 통째로 넘어갈 수 있습니다. 백업 파일이 특히 위험한 이유는, 현재 코드의 비밀번호를 바꿔도 옛 비밀번호가 백업본 안에 그대로 박제되어 남기 때문입니다.
방어: 이런 민감 파일은 웹 루트 밖에 두거나 웹 서버에서 접근을 차단하십시오. 백업 파일을 웹에서 접근 가능한 경로에 두지 마십시오. 자가 점검 스크립트로 정기 확인하는 습관이 중요합니다.
디렉터리 목록(Directory Listing) 노출
웹 서버가 디렉터리 내용을 목록으로 보여주도록 설정되어 있으면, 공격자는 그 안에 어떤 파일이 있는지 훤히 들여다봅니다. 숨겨 뒀다고 생각한 백업이나 자원이 그대로 드러납니다.
방어: 디렉터리 목록 표시(indexing) 기능을 비활성화하십시오. 대부분의 경우 이 기능은 꺼져 있는 것이 정상입니다.
2순위 발견: 디버그 모드와 정보 노출 — 친절함이 부른 재앙
두 번째로 흔한 부류는, 개발 편의를 위한 기능이 운영 환경에 그대로 남아 정보를 흘리는 경우입니다.
운영 서버에 켜진 디버그 모드
대부분의 웹 프레임워크는 개발 중 문제 진단을 돕기 위해 디버그 모드를 제공합니다. 오류가 나면 코드 위치, 변수 값, 시스템 경로, 때로는 DB 연결 정보나 설정값까지 화면에 그대로 뿌려 줍니다. 개발할 땐 더없이 편리하지만, 운영 환경에서는 공격자에게 시스템 내부 지도를 친절히 그려 주는 것과 같습니다. 공격자는 일부러 오류를 유발해 이 정보를 수집합니다.
그런데 디버그 모드는 정보 노출에서 끝나지 않고, 그 자체가 침투 경로가 되기도 합니다. 대표적인 사례가 PHP 진영의 Laravel 프레임워크에서 터진 Ignition 디버그 화면 원격 코드 실행 취약점, CVE-2021-3129입니다.
RCE(Remote Code Execution, 원격 코드 실행)는 공격자가 남의 서버에서 자기 명령을 실행하는, 침해 등급 중 가장 심각한 부류입니다.
Ignition은 오류 발생 시 보기 좋은 디버그 화면을 띄워 주는 도구입니다. 그런데 APP_DEBUG=true 상태로 운영에 올라간 사이트에서는 공격자가 이 화면의 기능을 악용해 서버를 통째로 장악할 수 있었습니다. 설정 한 줄(APP_DEBUG)을 끄지 않은 것이 곧 서버 장악으로 이어진 것입니다. 패치는 진작 나왔지만, 디버그 모드를 끄지 않은 사이트들이 한동안 대량으로 털렸습니다. 상세 내용은 NVD의 CVE-2021-3129 항목에서 확인할 수 있습니다.
지금 당장 할 일은 단순합니다.
# Laravel: 운영 서버 .env에서 디버그가 꺼졌는지 확인
grep APP_DEBUG .env
# 기대값: APP_DEBUG=false
다른 프레임워크라면 그에 해당하는 디버그 설정(개발용 켜짐 값)이 운영에서 꺼져 있는지 확인하십시오. CVE-2021-3129처럼 패치가 나온 지 한참 지난 취약점이 지금도 악용되는 이유가 바로 이 "설정 한 줄 안 끈" 방치입니다. 실제로 악용이 확인된 취약점은 CISA의 KEV 목록에서 추적할 수 있습니다.
방어: 개발과 운영의 설정을 명확히 분리하고, 배포 시점에 디버그 모드가 반드시 꺼지도록 자동화하십시오.
지나치게 상세한 오류 메시지
디버그 모드만큼 극적이지 않더라도, 과한 오류 메시지는 꾸준히 정보를 흘립니다.
- DB 오류가 그대로 노출되면 → 테이블·컬럼 구조 단서가 샘
- 시스템 오류가 노출되면 → 내부 파일 경로가 드러남
- 로그인 실패 메시지가 "비밀번호가 틀렸습니다"라고 너무 친절하면 → 공격자가 유효한 계정명을 식별
방어: 운영 환경에서는 사용자에게 일반적이고 모호한 메시지만 보여 주고, 상세 내용은 외부에 노출되지 않는 로그에만 기록하십시오. 외부에 대해서는 친절함보다 보안을 택하십시오.
인증 없이 열린 상태·정보 페이지
server-status, phpinfo, 모니터링 대시보드, API 문서 같은 진단·정보 페이지가 인증 없이 외부에 노출되는 경우가 흔합니다. 이런 페이지는 시스템 구성, 설치된 모듈, 환경 변수 등 공격자에게 유용한 정보를 한가득 담고 있습니다.
방어: 운영 환경에서 필요 없는 페이지는 제거하고, 필요한 것은 인증 뒤에 두거나 접근을 제한하십시오. 안 쓰는 것을 없애는 것이 곧 공격면 축소입니다.
3순위 발견: 자격 증명과 인증의 약점 — 열쇠를 흘리다
세 번째 부류는 인증과 자격 증명에 관한 것입니다.
기본 자격 증명 (admin/admin)
수많은 소프트웨어·장치·서비스가 기본 사용자명과 비밀번호를 달고 출고됩니다. 이걸 바꾸지 않고 그대로 운영하는 경우가 — 믿기 어렵지만 — 정말 흔합니다. 공격자는 알려진 기본 자격 증명 목록을 들고 가장 먼저 그것부터 시도합니다. 관리 패널, 데이터베이스, 네트워크 장비, IoT 기기 어디서든 발견됩니다.
방어: 모든 소프트웨어와 장치의 기본 자격 증명을 설치 즉시 강한 값으로 변경하십시오. 자산 인벤토리를 만들 때 각 자산의 자격 증명이 기본값인지 함께 점검하는 것이 좋습니다.
약하거나 재사용된 자격 증명
약한 비밀번호는 무차별 대입으로 뚫리고, 재사용된 비밀번호는 한 곳의 유출이 다른 곳의 침해로 번집니다. 공격자가 즐겨 쓰는 기법이 자격 증명 스터핑(credential stuffing) 입니다. 어디선가 유출된 아이디·비밀번호 쌍을 다른 사이트에 그대로 대입해 보는 것이죠.
방어자도 같은 데이터를 거꾸로 활용할 수 있습니다. Have I Been Pwned에서 본인과 조직의 이메일이 알려진 유출에 포함됐는지 무료로 확인하고, 걸리는 계정의 비밀번호를 먼저 교체하십시오.
방어: 강한 비밀번호 정책, 재사용 금지, 그리고 무엇보다 다단계 인증(MFA) 입니다. MFA가 있으면 자격 증명이 유출돼도 두 번째 요소가 침입을 막아 줍니다.
하드코딩된 비밀 정보
코드 안에 비밀번호·API 키·토큰이 직접 적혀 있는 경우입니다. 이게 위험한 이유는 한둘이 아닙니다.
- 코드를 보는 누구나(앞서 본
.git노출을 통해서든, 내부자든) 그 비밀을 손에 넣습니다. - 비밀을 바꾸려면 코드를 수정·재배포해야 합니다.
- 한 번 커밋되면 버전 이력에 영원히 남습니다.
방어: 비밀 정보는 코드에 박지 말고 환경 변수나 전용 비밀 관리 체계로 분리하십시오. 이미 커밋된 비밀이 있다면, 코드에서 지우는 것만으로는 부족하고 그 비밀 자체를 폐기하고 교체해야 합니다. 이력에 남아 있기 때문입니다.
4순위 발견: 잘못된 권한과 설정 — 사소해 보이는 치명상
네 번째 부류는 권한·설정 오류입니다. 개별로는 사소해 보이지만 종종 침해의 결정적 발판이 됩니다.
과도한 권한
최소 권한 원칙의 위반입니다. 어떤 계정이나 프로세스가 자기 일에 필요한 것보다 훨씬 많은 권한을 쥐고 있는 경우죠. 웹 애플리케이션이 DB에 대해 필요 이상의 권한을 갖거나, 서비스 계정이 과도한 시스템 권한을 갖는 식입니다. 이런 과도한 권한은 앞서 본 권한 상승의 지름길이 됩니다. 작은 침투가 큰 장악으로 번지는 통로입니다.
방어: 각 계정·프로세스에 꼭 필요한 권한만 부여하고, 정기적으로 검토해 불필요한 권한을 회수하십시오.
잘못된 파일 권한
읽혀서는 안 될 파일이 읽히거나, 수정되면 안 될 파일이 수정 가능한 경우입니다. 특히 SSH 개인키, 설정 파일, 자격 증명을 담은 파일의 권한이 지나치게 개방적이면 그 자체가 약점입니다.
# 민감 파일 권한 점검 예시
ls -l ~/.ssh/id_rsa # 기대: -rw------- (600)
ls -l .env # 소유자만 읽고 쓰게
chmod 600 ~/.ssh/id_rsa # 너무 열려 있다면 조이기
방어: 민감한 파일은 소유자만 접근하도록 권한을 엄격히 제한하십시오.
외부에 바인딩된 내부 서비스
데이터베이스·캐시·관리 도구 같은 내부용 서비스가 로컬이 아니라 외부 인터페이스에 바인딩되어 인터넷에서 직접 접근 가능한 경우입니다. 모의침투에서 단골로 발견되는 심각한 노출입니다. 외부에 노출된 데이터베이스는 인증이 약하거나 기본값이라면 데이터 전체를 그대로 내줍니다.
# 어떤 서비스가 외부(0.0.0.0)로 열려 있는지 확인
ss -tlnp
# DB/캐시 포트가 0.0.0.0이 아니라 127.0.0.1에 묶여 있어야 안전
방어: 내부 서비스는 로컬 인터페이스(127.0.0.1)에만 바인딩하십시오. 외부 접근이 꼭 필요하면 인증 게이트나 VPN 뒤에 두십시오. 그리고 내 서버의 IP나 도메인을 Shodan에 넣어 보십시오. 공격자 눈에 어떤 서비스가 그대로 보이는지 즉시 드러납니다. 데이터베이스 포트가 검색 결과에 뜬다면, 이미 누군가 찾고 있다는 뜻입니다.
접근 제어 누락 (IDOR)
자원에 접근할 때 그 자원에 대한 권한을 제대로 확인하지 않는 경우입니다. 예를 들어 사용자가 자기 정보 페이지 주소에서 식별자(id=1001)만 1002로 바꿨더니 남의 정보가 보이는 식이죠. 이를 안전하지 않은 직접 객체 참조(IDOR, Insecure Direct Object Reference)라고 합니다. 인증은 됐지만(로그인은 했지만) 인가가 누락된(그 특정 자원에 접근할 권한 확인이 빠진) 전형적인 사례입니다.
방어: 모든 자원 접근에 대해, 그 사용자가 그 자원에 접근할 권한이 있는지를 매번 서버 측에서 확인하십시오.
가장 중요한 통찰: 작은 약점들이 엮여 큰 침해가 된다
마지막으로, 현장에서 얻은 가장 중요한 교훈을 짚겠습니다. 실제 침해는 단 하나의 치명적 취약점보다, 여러 개의 작은 약점이 사슬처럼 엮여서 일어나는 경우가 더 많습니다.
각각은 그 자체로 별것 아니어 보입니다. 디렉터리 목록 노출 하나, 상세 오류 메시지 하나, 약간 과도한 권한 하나. 스캐너는 이것들을 개별적으로 "낮음" 또는 "정보"로 표시하고 넘어갈 수 있습니다. 그러나 사람은 이것들을 조합합니다.
실제 연쇄는 이런 식으로 흘러갑니다.
- 디렉터리 목록 노출로 → 백업 파일(
backup.sql)의 존재를 발견 - 백업 파일에서 → 과거 시점의 자격 증명을 획득
- 그 자격 증명이 다른 서비스에 재사용되어 있어 → 한 계정에 로그인 성공
- 그 계정의 과도한 권한을 이용해 → 시스템 깊숙이 침투
각 단계는 사소하지만, 엮이면 완전한 침해가 됩니다. 여기서 두 가지 결론이 나옵니다.
첫째, "낮음"으로 표시된 약점도 무시하지 마십시오. 그것이 연쇄의 한 고리가 될 수 있습니다. 특히 정보 노출 부류는 그 자체로는 무해해 보여도 공격자에게 다음 단계의 단서를 줍니다.
둘째, 방어도 연쇄를 끊는 관점으로 보십시오. 모든 약점을 완벽히 없앨 수는 없어도, 연쇄의 어느 한 고리만 끊으면 전체 공격이 무너집니다. 한 겹이 뚫려도 다음 겹이 막으면 사슬은 완성되지 않습니다. 깊이 방어의 본질이 바로 여기에 있습니다.
방어자의 최우선 점검 체크리스트
위 내용을 그대로 복사해 쓸 수 있는 점검 목록으로 압축했습니다.
- 노출 파일 점검 —
/.git/config,/.git/HEAD,.env, 백업 파일, 디렉터리 목록이 외부에서 보이지 않는지 확인 - 디버그 모드 차단 — 운영 서버에서
APP_DEBUG=false(또는 프레임워크별 디버그 OFF), 상세 오류·정보 페이지 비노출 - 기본 자격 증명 변경 — 모든 자산의 기본 ID/비밀번호를 강한 값으로 교체
- 하드코딩 비밀 제거 — 코드 내 키·토큰을 환경 변수로 분리, 이미 커밋된 비밀은 폐기·교체
- 최소 권한 적용 — 과도한 계정·프로세스 권한 회수, 민감 파일은
600으로 권한 조이기 - 내부 서비스 바인딩 점검 — DB·캐시가
127.0.0.1에만 묶였는지, Shodan에 노출되지 않는지 확인 - 접근 제어 확인 — 식별자만 바꿔 남의 자원이 보이지 않는지(IDOR) 테스트
- "낮음" 약점 재검토 — 개별 정보 노출이 연쇄의 고리가 될 수 있는지 점검
자주 묻는 질문 (FAQ)
Q. 모의침투(펜테스트)와 취약점 스캔은 어떻게 다른가요? 취약점 스캔은 자동화된 도구로 알려진 패턴을 빠르고 넓게 점검하는 작업이고, 모의침투는 사람이 실제 공격자처럼 침투를 시도하며 스캐너가 놓치는 맥락·조합·비즈니스 로직 결함을 찾아내는 작업입니다. 스캔은 "개별 항목이 취약한가"를, 모의침투는 "이 약점들을 엮으면 실제로 뚫리는가"를 봅니다. 둘은 대체재가 아니라 보완재로, 스캔으로 1차 점검을 자동화하고 모의침투로 깊이를 더하는 것이 이상적입니다.
Q. .git 디렉터리가 노출됐는데 접근 차단만 하면 끝 아닌가요?
아닙니다. 접근을 차단하는 것은 출혈을 멈추는 것일 뿐입니다. 노출되어 있던 동안 이미 누군가 저장소를 통째로 받아 갔을 수 있고, 커밋 이력에는 과거에 넣었다 지운 비밀번호·키가 그대로 남아 있습니다. 따라서 차단과 동시에 그 안에 담겼던 모든 자격 증명을 폐기하고 새로 발급해야 합니다. .env 노출이나 하드코딩된 비밀도 마찬가지로 "지우기"가 아니라 "폐기·교체"가 정답입니다.
Q. CVE-2021-3129는 오래된 취약점인데 지금도 신경 써야 하나요?
네. 패치가 나온 지 한참 됐지만, 이 취약점이 지금도 악용되는 이유는 정확히 "설정 한 줄을 안 끈" 방치 때문입니다. 운영 서버에 APP_DEBUG=true가 남아 있고 Ignition이 취약 버전이면 서버 전체가 장악될 수 있습니다. 운영 환경의 디버그 모드가 꺼져 있는지 확인하고, 프레임워크와 의존성을 최신 패치로 유지하십시오. 실제 악용이 확인된 취약점 목록은 CISA의 KEV 카탈로그에서 추적할 수 있습니다.
Q. 데이터베이스가 외부에 노출됐는지 어떻게 빠르게 확인하나요?
서버에서 ss -tlnp로 각 서비스가 어떤 인터페이스에 바인딩됐는지 확인하십시오. DB·캐시 포트가 0.0.0.0이 아니라 127.0.0.1에 묶여 있어야 안전합니다. 외부 관점에서는 본인 서버의 IP나 도메인을 Shodan에 넣어 보면 공격자 눈에 보이는 서비스가 그대로 드러납니다. 검색 결과에 DB 포트가 뜬다면 즉시 로컬 바인딩으로 바꾸고 인증을 강화해야 합니다.
Q. 스캐너가 "낮음(Low)"으로 표시한 항목은 나중에 처리해도 되지 않나요? 위험합니다. 실제 침해는 단일 "치명적" 취약점보다 여러 개의 "낮음" 약점이 사슬처럼 엮여서 일어나는 경우가 더 많습니다. 디렉터리 목록 노출 → 백업 파일 발견 → 옛 자격 증명 획득 → 재사용된 계정 침투 → 권한 상승으로 이어지는 식입니다. 특히 정보 노출 부류는 그 자체로는 무해해 보여도 공격자에게 다음 단계의 단서를 줍니다. 우선순위는 두되, 연쇄의 고리가 될 수 있는지를 기준으로 재검토하십시오.
원문 출처 및 더 알아보기
이 글은 (주)뎁팀 웹 보안팀이 운영하는 보안 가이드 사이트 SGAEPS(시그앱스)의 가이드를 바탕으로 재구성했습니다. 더 깊은 원문은 SGAEPS 가이드 원문에서 확인하실 수 있습니다.