본문 바로가기

서브도메인 열거

지난 장에서 만든 포트 스캐너는 IP 주소 하나를 입력으로 받았습니다. 하지만 실제 침투 테스트는 대부분 "이 회사의 공개 도메인은 example.com"이라는 한 개의 문자열에서 출발합니다. example.com이 가리키는 IP는 하나지만, mail.example.com·vpn.example.com·dev.example.com처럼 숨어 있는 서브도메인이 있을 수 있고, 각각은 다른 서버·다른 공격 표면입니다.

이번 장에서는 DNS 동작 원리를 정리한 뒤, 파이썬 dnspython으로 레코드를 직접 조회하고, 워드리스트 기반 서브도메인 열거기를 만들며, Whois·Shodan 같은 OSINT(Open-Source Intelligence, 공개 출처 정보) 소스를 엮어 "도메인 → 서브도메인 → IP → 서비스"로 이어지는 정보 수집 파이프라인을 완성합니다.

1. DNS는 왜 공격자에게 좋은 친구인가

DNS는 "도메인 이름 → IP 주소"를 알려주는 인터넷의 전화번호부입니다. 공개된 전화번호부죠. 운영자가 인증된 사용자에게만 알리고 싶은 서브도메인일지라도, DNS에 A 레코드를 올리는 순간 세상 누구나 조회할 수 있는 상태가 됩니다.

공격자가 DNS를 가장 먼저 뒤지는 이유는 세 가지입니다.

  1. 조회 자체가 정상 트래픽 — 방화벽·WAF가 차단할 명분이 없습니다. 일상 브라우저가 매초 수행하는 동작과 동일합니다.
  2. 메인 서비스보다 방어가 약한 호스트가 잘 숨어 있음 — www.example.com은 잘 지켜지지만 staging.example.com, old-admin.example.com은 잊혀져 방치된 경우가 잦습니다.
  3. 조직 구조가 힌트로 남음 — jenkins., gitlab., grafana. 같은 이름은 "우리는 이 스택을 씁니다"라는 자백이나 다름없습니다.

법·윤리 주의 — DNS 조회와 Whois 조회는 공개된 정보를 읽는 행위라 대부분의 나라에서 합법입니다. 다만 그 뒤에 붙는 "포트 스캔·취약점 스캔"은 법이 달라집니다. 이번 장의 실습은 본인 소유 도메인과 학습용으로 공개된 scanme.nmap.org, hackthebox.com, example.com의 DNS 조회까지만 수행합니다. 조회로 얻은 IP에 스캐너를 돌리는 것은 사전에 허가를 받은 경우에만입니다.

2. DNS 조회 원리

2.1 재귀 조회의 흐름

브라우저가 blog.example.com을 물어볼 때 내부적으로는 이런 일이 벌어집니다.

클라이언트는 보통 리커시브 리졸버 한 곳에만 물어보고, 나머지 "Root → TLD → 권한서버" 릴레이는 리졸버가 대신 돌아줍니다. 결과는 TTL(Time To Live) 초 동안 리졸버에 캐시되어 같은 도메인을 다시 물어보는 사람에게 빠르게 돌려집니다.

2.2 주요 레코드 타입

타입내용보안 도구가 보는 이유
A도메인 → IPv4 주소가장 기본, 서브도메인 열거의 성공 판정 기준
AAAA도메인 → IPv6 주소IPv4에만 방화벽을 걸어둔 조직이 의외로 많음
CNAME별칭(Canonical Name)어디에 호스팅됐는지 드러남 (예: *.cloudfront.net, *.github.io)
MX메일 교환 서버피싱 인프라 설계, SPF·DMARC 검증
NS이 도메인의 권한서버영역 이관 시도(AXFR) 대상
TXT자유 텍스트 (SPF, DKIM, 도메인 소유 증명 등)민감 정보 잔존 빈발 — SaaS 연동 흔적이 그대로 노출됨
PTRIP → 도메인 (역방향)한 IP에 묶인 다른 도메인 추정
SOA영역 메타정보시리얼 번호로 영역 변경 시점 추적

CNAME 서브도메인 탈취(Subdomain Takeover) — blog.example.com이 example-blog.herokuapp.com을 CNAME으로 가리켜뒀는데, 정작 Heroku 쪽 앱을 삭제했다면 어떻게 될까요? 공격자가 example-blog.herokuapp.com을 자기 이름으로 다시 등록하면, 그 순간부터 피해 조직의 서브도메인으로 공격자 콘텐츠가 서빙됩니다. CNAME 레코드 자체가 이 공격의 단서이고, 서브도메인 열거 결과에 CNAME을 같이 기록해두는 이유가 여기에 있습니다.

3. dnspython으로 직접 조회

파이썬 표준 라이브러리에도 socket.gethostbyname 같은 간단한 함수가 있지만, 레코드 타입별 조회나 특정 리졸버 지정은 표준 라이브러리만으로 불편합니다. 실무에서는 dnspython을 씁니다.

pip install dnspython

3.1 도메인 정보 한눈에 보기

# dns_lookup.py
import dns.resolver
import dns.exception

def lookup(domain: str, rdtype: str) -> list[str]:
    try:
        answers = dns.resolver.resolve(domain, rdtype, lifetime=3.0)
        return [r.to_text() for r in answers]
    except dns.resolver.NXDOMAIN:
        return []   # 도메인 자체가 없음
    except dns.resolver.NoAnswer:
        return []   # 도메인은 있지만 해당 타입 레코드가 없음
    except dns.exception.Timeout:
        return ["<timeout>"]

if __name__ == "__main__":
    domain = "example.com"
    for rdtype in ("A", "AAAA", "CNAME", "MX", "NS", "TXT"):
        records = lookup(domain, rdtype)
        print(f"[{rdtype}] {domain}")
        for r in records:
            print(f"   - {r}")
        print()

실행해보면 example.com의 메일 교환 서버, 권한서버, SPF 설정 등이 한 화면에 떠오릅니다. NXDOMAIN과 NoAnswer를 구분해서 기록해두면 다음 단계에서 훨씬 더 풍부한 판정이 가능합니다.

결과의미서브도메인 열거의 해석
NXDOMAIN그런 이름 자체가 없음"존재하지 않는 서브도메인"
NoAnswer이름은 있는데 해당 타입 레코드가 없음"존재함 — 다른 타입으로 파볼 가치 있음"
정상 응답레코드 반환"hit — 파이프라인의 다음 단계로"

4. 서브도메인 열거 원리

4.1 열거 기반 탐색

DNS의 단점(공격자 입장에선 장점)은 "이 도메인에 어떤 서브도메인이 있는지"를 한 번에 돌려주는 질의가 표준에는 없다는 것입니다. 대안은 세 가지입니다.

방법설명성공률
AXFR (영역 이관)권한서버에게 영역 전체를 달라는 질의거의 차단됨 — 성공 시 대박
워드리스트 기반 열거후보 이름(admin, api, vpn 등)을 일일이 A 레코드 조회가장 보편적, 대상 방어 무관
패시브 열거 (CT 로그 등)인증서 투명성(Certificate Transparency) 로그에서 과거 발급 이력 수집조용함, 운영 중인지는 별도 확인 필요

이 장에서는 워드리스트 기반을 중심으로 구현하고, CT 로그를 간단히 연동해 하이브리드 방식을 만듭니다. AXFR은 시도는 해보되, 실패를 전제로 둡니다.

5. 도메인 소유자 탐색

Whois는 도메인 등록 정보를 돌려주는 오래된 TCP 프로토콜입니다. 조직명·등록일·만료일·네임서버·담당자 연락처(요즘은 개인정보 보호로 가려지는 경우가 많음)가 들어있습니다.

pip install python-whois
# whois_lookup.py
import whois

def whois_summary(domain: str) -> dict:
    w = whois.whois(domain)
    return {
        "domain": domain,
        "registrar": w.registrar,
        "creation_date": str(w.creation_date),
        "expiration_date": str(w.expiration_date),
        "name_servers": list(w.name_servers) if w.name_servers else [],
        "org": w.org,
    }

if __name__ == "__main__":
    import json
    print(json.dumps(whois_summary("example.com"), ensure_ascii=False, indent=2))

Whois 결과에서 특히 보안 도구가 눈여겨보는 것은 두 가지입니다.

  • 만료일 — 임박한 도메인은 탈취(dangling) 위험이 있습니다. 조직이 잊고 갱신하지 않으면 제3자가 등록해 신뢰받는 도메인으로 피싱에 쓸 수 있습니다.
  • 등록 조직명 — 같은 조직명으로 등록된 다른 도메인(예: example.co.kr, example.io)을 찾을 수 있습니다. 공격 표면이 한 번에 넓어집니다.

6. 과제

# 서브 도메인 열거 기반 탐색 과제

## 과제 설명
도메인을 입력하면 도메인 정보(소유자, 등록일, 만료일 등), 열린 포트(예: 22, 80), 서브 도메인(열거 기반으로 탐색)을 CSV로 저장하는 프로그램을 작성하세요.

## 요구 사항
* "scanme.nmap.org" 도메인을 대상으로 프로그램을 작성하세요.
* 도메인 정보, 열린 포트, 서브 도메인을 CSV 파일로 저장하세요.
* 프로그램은 Python으로 작성되어야 합니다.

## 고도화 과제
* 정보보안 팀이 기능적으로 더 필요한 요구사항이 있다고 판단되는 기능을 함께 구현해보세요.
서브도메인 열거 - AI 네이티브 모의해킹 베이스캠프 | 위니버시티