Step 145. 디렉토리 탐색과 정보 노출 — “주소를 모르면 안전하다”는 착각

Step 145. 디렉토리 탐색과 정보 노출 — "주소를 모르면 안전하다"는 착각

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★☆☆☆ | 예상 소요 시간 3시간

전제: Step 121(숨겨진 경로 탐색)과 Step 144(파일 포함)를 마쳤다. 상태 코드 200/301/403/404를 읽을 수 있다.

  • 준비물: 파이썬 3 (로컬 웹서버 실습), Kali의 gobuster (랩 실습), 텍스트 에디터
  • 주의: ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.

개발자는 종종 이렇게 믿습니다. "주소를 모르면 못 찾겠지." 그러나 공격자는 사전으로 전부 두드립니다 — Step 121에서 우리가 직접 만들어 본 그 기법입니다. 오늘은 그 탐색이 실제로 무엇을 낚는지 봅니다. 웹에 통째로 올라간 .git 저장소, 비밀번호가 그대로인 백업 파일, "보지 말라"고 적어서 오히려 지도가 된 robots.txt. 오늘은 이것들을 일부러 노출한 로컬 서버를 만들어, 공격자의 눈으로 하나씩 털어 봅니다 (본 교재는 2026-09-09에 실측했습니다).


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • 강제 브라우징(forced browsing)의 원리와 gobuster의 기본 사용법을 안다
  • .git 폴더 노출이 왜 "소스 전체 유출"인지 직접 확인한다
  • 백업 파일(.bak, ~)과 메타 파일(.DS_Store)의 위험을 설명한다
  • 403 응답도 "존재한다"는 정보임을 탐색에 활용한다
  • 공격자가 가장 먼저 찾는 정보 노출 실수 TOP 5를 정리한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 파이썬 3 (http.server — 로컬 재현) / Kali gobuster (랩)
오늘의 명령 gobuster dir -u 주소 -w 워드리스트 -x php,txt,bak, curl
필요한 개념 강제 브라우징, 상태 코드 판독, .git 구조, 디렉토리 리스팅
오늘의 산출물 노출 실수 TOP 5 문서 + 발굴 기록표

2-1. 강제 브라우징 — 링크 없는 곳을 두드린다

Step 121에서 배운 디렉터리 버스팅의 실전 이름이 강제 브라우징입니다. 사전의 단어를 경로에 하나씩 붙여 요청하고 상태 코드로 판독합니다. gobuster는 이 일을 수만 개 단어와 병렬로 처리하는 도구입니다:

gobuster dir -u http://대상/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,bak

-x는 확장자까지 조합해 config.php, config.txt, config.bak를 함께 두드립니다. 백업 파일 탐색의 핵심 옵션입니다.

2-2. .git 노출 — 저장소가 통째로 웹에 올라간 사고

git clone이나 배포 스크립트가 웹루트에 .git 폴더를 남기는 사고가 실제로 잦습니다. .git에는 소스의 전체 이력이 들어 있습니다. /.git/config에 응답하는 서버를 발견하면, 공격자는 GitToolsDumper 같은 도구로 저장소를 통째로 내려받아 소스를 복원합니다. 커밋 이력에는 "일단 커밋하고 나중에 지운" 비밀번호까지 살아 있습니다. 오늘 직접 노출시켜 보고 무엇이 새는지 확인합니다.

2-3. 백업 파일과 메타 파일 — 개발의 부산물

에디터와 배포 도구가 남긴 흔적들입니다.

  • config.php.bak, index.php~: 서버가 .bak를 PHP로 실행하지 않고 텍스트로 내려 줍니다 — 소스와 DB 비밀번호가 그대로.
  • .DS_Store: macOS가 폴더마다 만드는 메타 파일. 그 안에는 해당 폴더의 파일 이름 목록이 들어 있어, 링크 없는 파일명을 알아내는 지도가 됩니다.
  • robots.txt: 검색엔진에게 "크롤링하지 마"라는 안내지만, 공격자에게는 "여기 숨은 경로 목록"입니다.

2-4. 403의 정보 가치

Step 121에서 301이 사실상 "있다"였듯, 403(금지)도 "있다"는 신호입니다. 없는 경로는 404가 나가는데 403이 왔다는 것은 — 경로는 존재하되 접근 규칙이 막고 있다는 뜻입니다. 탐색 결과를 정리할 때 403은 버리는 값이 아니라 별도 칸에 모으세요. 방어 규칙의 우회법(헤더 조작, 경로 변형)을 연구하는 출발점이 됩니다.


3. 따라 하기

3-1. 모사 대상 — 개발 흔적을 고스란히 노출한 웹사이트

겉은 평범한 회사 홈페이지, 속은 개발 산출물 투성이인 사이트를 만듭니다. 파이썬 표준 라이브러리만으로 됩니다.

입력 (노출 사이트 구성)

mkdir -p exposed_site/.git/refs/heads exposed_site/admin
cd exposed_site
echo "<h1>우리 회사 홈페이지(랩)</h1>" > index.html
# 개발자가 실수로 남긴 것들
echo "DB_PASSWORD = 'sup3r-secret!'" > app.py
echo "<?php \$db_pass = 'backup-pass-123'; ?>" > config.php.bak
printf '[remote "origin"]\n\turl = https://dev:hunter2@github.com/company/secret-repo.git\n' > .git/config
echo "ref: refs/heads/main" > .git/HEAD
echo "9f2ab1c7d4e5a0b3c8d1e2f3a4b5c6d7e8f9a0b1" > .git/refs/heads/main
echo "User-agent: *
Disallow: /admin/
Disallow: /.git/" > robots.txt
python -m http.server 8082 --bind 127.0.0.1

읽는 법: 홈페이지에는 이 파일들로 가는 링크가 하나도 없습니다. 하지만 "링크가 없다"와 "접근할 수 없다"는 완전히 다른 말임을 곧 확인합니다.

3-2. .git 노출 — 저장소의 문이 열려 있다

공격자가 처음 확인하는 것은 .git의 존재 자체입니다.

입력

curl http://127.0.0.1:8082/.git/config
curl http://127.0.0.1:8082/.git/HEAD
curl http://127.0.0.1:8082/.git/refs/heads/main

출력 (2026-09-09 실측):

GET /.git/config -> 200
[remote "origin"]
	url = https://dev:hunter2@github.com/company/secret-repo.git

GET /.git/HEAD -> 200
ref: refs/heads/main

GET /.git/refs/heads/main -> 200
9f2ab1c7d4e5a0b3c8d1e2f3a4b5c6d7e8f9a0b1

읽는 법: 세 줄의 피해를 세어 보세요. config에서 원격 저장소 주소와 자격증명 포함 URL(dev:hunter2@)이 새었습니다. HEAD에서 현재 브랜치를, refs/heads/main에서 최신 커밋 해시를 알았습니다. 실제 사고 현장에서는 여기서 GitToolsDumper가 개입해 저장소 전체(모든 소스 파일과 커밋 이력)를 복원합니다 — .git 노출은 "파일 하나 유출"이 아니라 "프로젝트 전체 유출"입니다.

3-3. 백업 파일과 소스 직접 열람

입력

curl http://127.0.0.1:8082/config.php.bak
curl http://127.0.0.1:8082/app.py
curl http://127.0.0.1:8082/robots.txt

출력 (2026-09-09 실측):

GET /config.php.bak -> 200
<?php $db_pass = 'backup-pass-123'; ?>

GET /app.py -> 200
DB_PASSWORD = 'sup3r-secret!'
flag{소스코드_유출}

GET /robots.txt -> 200
User-agent: *
Disallow: /admin/
Disallow: /.git/

읽는 법: .bak 파일은 서버가 실행하지 않고 원문을 내려 줍니다 — PHP 소스가 그대로 보이는 이유입니다. 그리고 robots.txt를 보세요. "보지 말라"는 Disallow 목록이 공격자에게는 탐색 우선순위표입니다. 실제로 우리는 이 파일 덕분에 /admin//.git/의 존재를 확인받았습니다.

3-4. 디렉토리 리스팅 — 폴더 속이 훤히 보이는 서버

index.html이 없는 폴더를 요청하면 어떻게 될까요? 설정에 따라 서버는 폴더 내용 목록을 통째로 보여 줍니다.

입력

curl http://127.0.0.1:8082/.git/refs/heads/

출력 (2026-09-09 실측):

<title>Directory listing for /.git/refs/heads/</title>
<h1>Directory listing for /.git/refs/heads/</h1>
<ul>
<li><a href="main">main</a></li>
</ul>

읽는 법: "Directory listing for …"라는 제목과 함께 폴더 속 파일 목록이 링크로 나왔습니다. 아파치의 Options Indexes, 일부 개발 서버가 기본으로 이 동작을 켭니다. 방어자라면 이 목록 출력을 끄는 것이 기본 설정입니다 — 파일명을 모르면 사전으로 두드려야 하는데, 리스팅은 그 수고를 없애 줍니다.

3-5. gobuster로 랩 스캔 (랩 실습, 출력 예시)

여러분의 Kali에서 DVWA 등 랩 대상으로 진행하세요. 출력은 출력 예시입니다.

입력

gobuster dir -u http://DVWA주소/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,bak
/.git                 (Status: 301)
/config               (Status: 301)
/docs                 (Status: 301)
/login.php            (Status: 200)
/robots.txt           (Status: 200)
/setup.php            (Status: 200)
/config.inc.php.bak   (Status: 200)   ← -x bak가 낚은 백업 파일

읽는 법: Status 열을 읽는 습관이 핵심입니다. 301은 디렉토리 존재, 200은 파일 존재, 403은 "있지만 금지"입니다. common.txt는 수천 개라 랩에서 금방 끝납니다. 큰 리스트는 랩 서버를 느리게 하니, 원리를 익힐 땐 작은 리스트로 충분합니다.


4. 미션과 연습문제

미션 — 정보 노출 사냥과 TOP 5 문서

  1. 3-1의 노출 사이트를 만들고, 홈페이지 소스(HTML)에 .git, .bak, robots.txt 링크가 없음을 먼저 확인한다
  2. 링크 없이 주소만으로 /.git/config, /config.php.bak, /app.py를 읽어 유출된 비밀(비밀번호·플래그)을 세 개 이상 기록한다
  3. robots.txt의 Disallow 목록을 "공격자의 탐색 우선순위표"로 해석해 노트에 적는다
  4. gobuster로 랩(DVWA)을 스캔하고, 200/301/403 결과를 상태 코드별로 분류해 기록한다
  5. "공격자가 제일 먼저 찾는 노출 실수 TOP 5"를 작성한다 — 각 항목에 "무엇이 새는가" 한 줄씩

연습문제

문제 1. .git 폴더 노출이 일반 파일 하나의 노출보다 훨씬 위험한 이유를 두 가지 말해 보세요.

문제 2. config.php.bak는 왜 원문이 그대로 보이나요? 같은 파일이 .php 확장자일 때와 비교해 설명해 보세요.

문제 3. robots.txt가 공격자에게 오히려 도움이 되는 이유와, 그렇다면 robots.txt를 어떻게 써야 하는지 말해 보세요.

문제 4. 탐색 결과에서 403 응답을 "발견"으로 기록해야 하는 이유를 말해 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

유출 기록의 예 (2026-09-09 실측): .git/config에서 자격증명 포함 원격 주소(dev:hunter2@...), config.php.bak에서 DB 비밀번호, app.py에서 비밀번호와 플래그 — 세 건 모두 링크 없이 주소 추측만으로 획득했습니다.

TOP 5 문서의 예:

1. .git/ 노출        — 소스 전체 + 커밋 이력 속 옛 비밀번호
2. .bak / ~ 백업 파일 — 실행되지 않는 소스 원문, DB 비밀번호
3. robots.txt        — 숨은 경로 목록 (Disallow = 지도)
4. 디렉토리 리스팅    — 폴더 속 파일명 전부
5. admin/, setup.php  — 관리·설치 페이지 잔존

검증하는 법: ① 링크 부재를 먼저 확인한 기록이 있는가. ② 유출된 비밀을 실제 값으로 적었는가. ③ gobuster 결과가 상태 코드별로 분류됐는가. ④ TOP 5에 "무엇이 새는가"가 붙어 있는가.

연습문제 해답

문제 1 해답. 첫째, .git에는 현재 소스뿐 아니라 전체 커밋 이력이 있어 복원 도구로 프로젝트 전체를 받을 수 있습니다. 둘째, 이력에는 나중에 삭제한 비밀번호·키도 그대로 남아 있어, 지금 소스에는 없는 비밀까지 털립니다. config의 원격 주소처럼 자격증명이 직접 새기도 합니다.

문제 2 해답. 웹서버는 확장자로 "실행할지, 텍스트로 내려 줄지"를 정합니다. .php는 PHP 엔진이 실행해 결과만 나가지만, .bak는 알 수 없는 확장자라 원문 텍스트가 그대로 응답됩니다. 백업 파일이 소스 유출 통로인 이유입니다.

문제 3 해답. robots.txt는 누구나 읽을 수 있는 공개 파일이라, Disallow 목록이 곧 "숨기고 싶은 경로 목록"이 됩니다. 대응: robots.txt는 검색엔진 제어용으로만 쓰고, 민감 경로는 여기 적지 말고 인증·접근 제어로 막아야 합니다. 숨김은 방어가 아닙니다.

문제 4 해답. 404는 "없다"지만 403은 "있지만 금지"입니다 — 경로의 존재 자체가 확인된 것입니다. 존재가 확인된 경로는 이후 우회 시도(헤더 조작, 경로 변형, 다른 HTTP 메서드)의 후보가 되므로, 탐색 기록에서 404와 구분해 모아 두어야 합니다.

완료 기준 체크리스트

  • [ ] 노출 사이트를 만들고 링크 없이 주소만으로 .git/config를 읽었다
  • [ ] 백업 파일(.bak)에서 원문 소스가 유출됨을 확인했다
  • [ ] 디렉토리 리스팅 화면("Directory listing for …")을 직접 봤다
  • [ ] robots.txt의 Disallow를 공격자 시선으로 해석했다
  • [ ] gobuster 결과를 200/301/403으로 분류해 기록했다
  • [ ] 노출 실수 TOP 5 문서를 작성했다

6. 흔한 실수와 해결

벽 1. /.git/config를 요청했는데 404가 나온다

증상: 분명히 만들어 둔 .git인데 없다고 합니다.
원인: 서버를 띄운 폴더가 웹루트입니다. exposed_site가 아니라 그 상위에서 서버를 켰거나, 경로 오타일 수 있습니다.
해결: 먼저 curl http://127.0.0.1:8082/로 홈페이지가 열리는지 확인하고, 서버 실행 위치를 점검하세요. 일부 서버는 .로 시작하는 경로를 기본 차단하기도 합니다 — 그 자체가 방어 설정입니다.

벽 2. gobuster가 너무 느리다

증상: 큰 워드리스트를 돌렸더니 랩 서버가 버티지 못합니다.
원인: 수만 개 요청은 가벼운 랩 서버에도 부하입니다.
해결: 랩에서는 common.txt(수천 개)로 충분합니다. 원리는 작은 사전으로 익히고, -t 옵션으로 스레드 수도 조절해 보세요.

벽 3. 301만 잔뜩 나오고 200이 없다

증상: 결과가 전부 301 리다이렉트입니다.
원인: 디렉토리 후보들이 존재하는 것인데, 뒤에 /가 붙는 이사 안내라 본문이 안 따라올 수 있습니다.
해결: 301도 "디렉토리 있다"는 발견입니다. curl -L로 따라가 보거나, gobuster의 -f(슬래시 추가) 옵션을 확인하세요.

벽 4. 백업 파일을 찾았는데 다운로드만 되고 안 보인다

증상: 브라우저가 .bak를 화면에 안 띄우고 저장합니다.
원인: Content-Type 문제일 뿐, 유출 자체는 일어났습니다.
해결: curl로 받으면 원문이 보입니다. 화면에 안 뜬다고 안전한 게 아님을 구분하세요.

벽 5. "이건 우리가 일부러 만든 거잖아"라는 의문

증상: 실습이 너무 잘 돼서 실전성이 의심됩니다.
원인: 실제 사고 통계를 보면 .git 노출과 백업 파일은 지금도 버그바운티의 단골 제보 유형입니다.
해결: 실습의 요점은 "왜 새는가"의 구조 파악입니다. 방어자가 되어 3-1 사이트의 노출을 하나씩 막아 보는 것(리스팅 끄기, .git 차단 규칙)이 최고의 복습입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
강제 브라우징 사전으로 경로를 두드려 링크 없는 리소스를 찾는 기법
.git 노출 저장소 폴더가 웹에 공개된 사고 — 소스+이력 통째 유출
백업 파일(.bak, ~) 실행되지 않고 원문이 새는 소스 사본
.DS_Store macOS 메타 파일 — 폴더 속 파일명 목록이 유출
robots.txt 공개된 "숨은 경로 목록" — 방어가 아니라 지도
403의 정보 가치 "있지만 금지" — 존재 확인 신호
디렉토리 리스팅 index 파일 없는 폴더의 목록이 통째로 보이는 설정

오늘의 명령어

명령 하는 일
gobuster dir -u 주소 -w common.txt 숨은 경로 사전 탐색
-x php,txt,bak 확장자 조합으로 백업 파일까지 탐색
curl 주소/.git/config 저장소 노출 여부 확인
curl 주소/config.php.bak 백업 파일 원문 확인
curl -L 주소/경로 301을 따라가 최종 응답 확인

명령어보다 중요한 감각

오늘 본 모든 유출의 공통점은 "지우지 않은 것"입니다. 배포에 .git이 따라갔고, 수정 전 사본이 .bak로 남았고, 개발 중이던 관리 페이지가 그대로였습니다. 공격 기술보다 앞서는 진리 — 공격자는 여러분이 배포한 것이 아니라 여러분이 남긴 것을 노립니다.

방어자의 체크리스트는 오늘 미션의 TOP 5를 뒤집은 것입니다. 배포 전 숨은 파일을 치우고, 웹 서버에서 . 경로와 리스팅을 막고, robots.txt에 민감 경로를 적지 않는 것. 화려한 공격 도구 앞에서 이런 소박한 설정이 진짜 성벽입니다.


전부 체크되면 Step 145 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.