Post

[Security] Nuclei 기본 사용법부터 대량 스캔 및 Confluence 자동화까지

[Security] Nuclei 기본 사용법부터 대량 스캔 및 Confluence 자동화까지

안녕하세요! 오늘은 강력한 템플릿 기반 취약점 스캐너인 Nuclei의 기초 사용법부터, 40여 개의 대량 타겟을 한 번에 진단하고 그 결과를 Atlassian Confluence에 자동 등록하는 고도화된 방법까지 알아보겠습니다. 추가로 파이프라인 상에서 다른 보안 도구들과 연동하는 방법도 함께 가이드합니다.

1. Nuclei란? 및 기본 사용법

Nuclei는 ProjectDiscovery에서 개발한 빠르고 확장 가능한 취약점 스캐너입니다. YAML 기반의 템플릿을 사용하여 다양한 프로토콜(HTTP, DNS, TCP 등)에 대한 광범위한 취약점 스캐닝을 지원합니다.

설치 및 필수 업데이트

1
2
3
4
5
# Go를 이용한 설치
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

# 템플릿 업데이트 (최초 실행 혹은 정기적으로 필수 항목)
nuclei -ut

기본 스캔 명령어

단일 타겟에 대해 스캔을 수행하는 기본 명령어입니다.

1
nuclei -u https://example.com -t cves/ -t exposed-panels/

2. 대량 40개 대상 진단하기

40개 규모의 다수 웹사이트나 인프라를 스캔할 때는 타겟 목록 파일을 생성하고 동시성(Concurrency) 옵션을 튜닝하여 빠르게 점검할 수 있습니다.

  1. 타겟 리스트 작성 (targets.txt)
    1
    2
    3
    4
    
    https://target1.com
    https://target2.com
    ...
    https://target40.com
    
  2. 대량 스캔 실행 대량 스캔 시에는 서버에 과도한 부하를 주지 않으면서 처리 속도를 높이기 위해 Rate Limit(-rl) 및 Concurrency(-c, -bs)를 조절합니다.

    1
    
    nuclei -l targets.txt -t cves/ -t vulnerabilities/ -c 50 -bs 40 -jle nuclei_results.json
    
    • -l targets.txt: 타겟 목록 파일 지정
    • -c 50: 템플릿 실행 동시성
    • -bs 40: 타겟 동시성 (Bulk Size)
    • -jle nuclei_results.json: 런타임 결과를 JSON Lines 형식으로 저장 (이후 Confluence 연동을 위해 필요)

3. 타겟별 진단 결과를 Atlassian Confluence에 각각 작성하기

Nuclei 자체로는 Jira 통합(-rc 옵션)을 지원하지만, Confluence로 40개의 대상 각각의 개별 페이지를 자동 생성하는 네이티브 기능은 없습니다. 따라서 앞서 생성한 JSON 결과 파일을 파싱하여 Confluence REST API를 통해 40개 대상 각각의 결과 페이지를 생성하는 자동화 스크립트를 작성해야 합니다.

Python을 활용한 Confluence 자동화 스크립트 예시

먼저 파이썬 Confluence API 라이브러리를 설치합니다.

1
pip install atlassian-python-api

nuclei_to_confluence.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import json
from atlassian import Confluence
from collections import defaultdict

# Confluence 접속 정보 설정
CONFLUENCE_URL = "https://your-domain.atlassian.net/wiki"
USERNAME = "your_email@example.com"
API_TOKEN = "your_confluence_api_token"
SPACE_KEY = "SEC" # 기록할 스페이스 키
PARENT_PAGE_ID = "12345678" # 결과들을 하위 페이지로 묶을 상위 페이지 ID

confluence = Confluence(
    url=CONFLUENCE_URL,
    username=USERNAME,
    password=API_TOKEN,
    cloud=True
)

# 타겟별로 결과를 묶기 위한 딕셔너리
results_by_target = defaultdict(list)

# Nuclei JSONL 결과 파일 파싱
with open('nuclei_results.json', 'r') as f:
    for line in f:
        data = json.loads(line.strip())
        target = data.get('host', 'unknown')
        results_by_target[target].append(data)

# 각 타겟별로 독립된 Confluence 페이지 생성 로직
for target, findings in results_by_target.items():
    page_title = f"[Nuclei Scan] {target} 진단 결과"
    
    # HTML 본문(테이블 포맷) 생성
    body_html = f"<h2>타겟 도메인/IP: {target}</h2>"
    body_html += "<table border='1'><tr><th>Severity</th><th>Vulnerability Name</th><th>Template ID</th><th>Matched URL</th></tr>"
    
    for finding in findings:
        severity = finding.get('info', {}).get('severity', 'info')
        name = finding.get('info', {}).get('name', 'N/A')
        template_id = finding.get('template-id', 'N/A')
        matched_at = finding.get('matched-at', 'N/A')
        
        # 위험도(Severity) 색상 강조 등을 넣을 수도 있습니다.
        body_html += f"<tr><td><b>{severity.upper()}</b></td><td>{name}</td><td>{template_id}</td><td><a href='{matched_at}'>{matched_at}</a></td></tr>"
        
    body_html += "</table>"

    # 페이지가 이미 존재하면 내용을 업데이트, 없으면 신규 생성 처리
    confluence.update_or_create(
        parent_id=PARENT_PAGE_ID,
        title=page_title,
        body=body_html,
        representation='storage',
        full_width=True
    )
    print(f"✅ Created/Updated Confluence page for {target}")

이 파이썬 스크립트를 실행하면 targets.txt에 있었던 40개의 타겟 도메인별로 Confluence에 표 형태의 깔끔한 취약점 보고서 하위 페이지가 각각 자동 생성 및 갱신됩니다.


4. 다른 스캐닝 도구와 연동한 파이프라인 자동화 가이드

보안 진단을 더욱 강력하게 만들기 위해, 자산 탐지(Reconnaissance) 도구나 다른 스캐너들과 연동해 파이프라인을 구축할 수 있습니다.

1) 서브도메인 수집부터 취약점 스캔까지의 파이프라인 (CLI 기반 연동)

Subfinder(서브도메인 탐색)와 httpx(활성 웹서버 탐지 도구)를 Nuclei와 파이프라인(|)으로 연결하여 자동 실행 환경을 구축합니다.

1
2
# 1. 서브도메인 탐색 -> 2. 활성 웹서버 필터링 -> 3. 대상에 대해 바로 Nuclei 스캔
subfinder -d example.com -silent | httpx -silent | nuclei -t cves/ -o full_scan_result.txt

2) CI/CD (GitHub Actions / GitLab CI) 자동화 연동

항상 최신 상태를 점검하기 위해 스캐닝을 주기적으로 자동화하려면 CI/CD 파이프라인에 이식하는 것이 권장됩니다. 동일한 원리로 Trivy와 같은 도구를 앞단에서 컨테이너 스캔용으로 사용한 뒤, 배포된 엔드포인트에 대해 Nuclei를 수행하도록 구성할 수 있습니다.

GitHub Actions 예시 (.github/workflows/nuclei-scan.yml):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
name: Nuclei Automated Scan
on:
  schedule:
    - cron: '0 0 * * *' # 매일 자정 실행
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Nuclei Scan
        uses: projectdiscovery/nuclei-action@main
        with:
          target: "targets.txt"
          output: "nuclei_results.sarif"
          format: "sarif"
      - name: GitHub Security Dashboard에 SARIF 연동
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: nuclei_results.sarif

위와 같이 구성하면 스캔 결과가 GitHub Security 리포트 탭에 바로 통합됩니다.

3) 취약점 통합 관리 플랫폼 연동 (DefectDojo 사용)

Nuclei, Trivy, SonarQube 등 다양한 종류의 스캐닝 도구의 결과를 하나로 모아서 일괄 관리하고 싶다면 DefectDojo 같은 취약점 통합 플랫폼과 연동해 구축합니다.

  • 스캔 수행 후 결과를 SARIF 혹은 JSON 포맷으로 출력합니다 (-se 또는 -je 옵션).
  • CI/CD 파이프라인의 마지막 단계에서 DefectDojo API(또는 전용 CLI)를 통해 각 테스트 도구의 결과를 업로드하여(Import Scan) 대시보드에서 일원화하고, Jira 티켓팅 및 메신저(Slack, Teams 등) 알림 발송까지 종합적으로 자동화할 수 있습니다.

5. Gemini AI를 활용한 정오탐(False Positive) 판별 자동화

Nuclei 스캔 결과에는 종종 정오탐(False Positive)이 포함될 수 있습니다. 이를 관리자가 일일이 확인하는 것은 대규모 진단에서 비효율적이므로, Google Gemini API를 연동하여 결과의 유효성을 AI가 1차적으로 판별하게 할 수 있습니다.

Gemini 판별 로직 추가하기

앞서 작성한 nuclei_to_confluence.py 스크립트에 Gemini API를 통합하여, Confluence에 결과를 올리기 전에 정오탐 여부를 분석하는 단계를 추가합니다.

  1. Gemini SDK 설치
    1
    
    pip install google-generativeai
    
  2. 스크립트에 Gemini 연동 코드 반영

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    import json
    import google.generativeai as genai
    from atlassian import Confluence
    from collections import defaultdict
    import time
    
    # --- 설정 영역 ---
    GENAI_API_KEY = "your_gemini_api_key"
    genai.configure(api_key=GENAI_API_KEY)
    # 최신 텍스트 모델 사용 (필요에 따라 변경)
    model = genai.GenerativeModel('gemini-1.5-pro-latest') 
    
    CONFLUENCE_URL = "https://your-domain.atlassian.net/wiki"
    # ... (Confluence 설정은 기존과 동일) ...
    
    # --- AI 판별 함수 ---
    def analyze_false_positive(finding_data):
        """
        취약점 데이터를 Gemini에게 전달하여 정탐/오탐 가능성을 묻습니다.
        """
        prompt = f"""
        당신은 보안 전문가입니다. 다음은 Nuclei 보안 스캐너의 결과입니다.
        이 결과가 실제 취약점(정탐, True Positive)인지 아니면 오탐(False Positive)인지 분석해 주세요.
        응답은 반드시 '판별: [정탐/오탐]', '이유: [간략한 이유]' 형식으로만 답변해 주세요.
           
        [취약점 정보]
        - 취약점 이름: {finding_data.get('info', {}).get('name')}
        - 심각도: {finding_data.get('info', {}).get('severity')}
        - 매칭된 URL: {finding_data.get('matched-at')}
        - 추출된 데이터(Extracted): {finding_data.get('extracted-results', '없음')}
        - 요청/응답(일부): {str(finding_data)[:500]} ...
        """
        try:
            response = model.generate_content(prompt)
            return response.text.strip()
        except Exception as e:
            return f"판별 실패: API 에러 ({str(e)})"
    
    # ... (JSONL 파싱 및 타겟별 그룹화 로직은 기존과 동일) ...
    
    for target, findings in results_by_target.items():
        page_title = f"[Nuclei Scan] {target} 진단 결과 (AI 분석 포함)"
           
        body_html = f"<h2>타겟 도메인/IP: {target}</h2>"
        body_html += "<table border='1'><tr><th>Severity</th><th>Vulnerability Name</th><th>AI Analysis (Gemini)</th><th>Matched URL</th></tr>"
           
        for finding in findings:
            severity = finding.get('info', {}).get('severity', 'info')
            name = finding.get('info', {}).get('name', 'N/A')
            matched_at = finding.get('matched-at', 'N/A')
               
            # Gemini API 호출 (API Rate limit 고려하여 약간의 딜레이 추가)
            print(f"[{target}] {name} - AI 판별 중...")
            ai_analysis = analyze_false_positive(finding)
            time.sleep(1) # API 호출 제한 방지
               
            body_html += f"<tr><td><b>{severity.upper()}</b></td><td>{name}</td><td>{ai_analysis}</td><td><a href='{matched_at}'>{matched_at}</a></td></tr>"
               
        body_html += "</table>"
    
        # ... (Confluence 업로드 로직은 동일) ...
    

이 방식을 사용하면 Confluence 페이지의 테이블에 AI Analysis (Gemini) 열이 추가됩니다. 보안 담당자는 40여 개의 타겟에 대한 모든 알럿을 수동으로 검증할 필요 없이, Gemini가 “오탐 가능성이 높다”고 판단한 항목은 후순위로 미루고 “정탐(True Positive)”으로 강하게 의심되는 항목부터 우선적으로 조치할 수 있어 시간을 크게 절약할 수 있습니다.


마무리

앞서 설명해 드린 내용을 종합하면, [다중 타겟 자산 수집] -> [Nuclei 스캔 및 결과 JSON 추출] -> [Gemini AI를 통한 정오탐 1차 식별] -> [Python을 이용한 Confluence 페이지 개별 동기화] 형태로 강력한 대량 진단 파이프라인을 구축할 수 있습니다.

This post is licensed under CC BY 4.0 by the author.