카테고리 없음

[토스뱅크 부트캠프] 팀프로젝트 Interrupt 6주차

yejiamoe 2026. 1. 28. 14:19

  1. 전체적인 흐름
    • 공격 수집 : Falco / FIM /SCA 로그 들어옴
    • IP 식별
      • IP있으면 해당 IP tkdyd
      • IP 없으면 PID/Container ID를 캐시에서 조회하여 매칭하는 IP를 찾아오기
    • 점수 합산 : Get Cache로 해당 IP의 기존 점수를 가져와서 새 로그의 위험도를 더한 뒤 Set Cache로 다시 저장
    • 임계치 대응 : 합산된 점수가 임계치(예 : 80점)을 넘으면 차단 및 알림

로그 수신 및 상태 로드 (Nodes 1~4)

  1. webhook
  2. : 설정 완료
  3. Get Storage (by Agent ID)
    • Key: agent_score_{{ $exec.all_fields.agent.id }}
    • Technical Context: 만약 데이터가 없다면(신규 에이전트), {"total_score": 0, "sca_applied": false}라는 기본값을 반환하도록 설정
  4. Filter (RS인지 아닌지)
    • Condition: {{ $exec.all_fields.rule.id }} 가 STAGE_1으로 시작하는가?
    항목 이름 의미 예시 (3번 노드 설정값)
    Input list 어떤 데이터를 가져와 검사할 것인가? {{ $exec }}
    Field 로그 데이터 중 어떤 항목을 볼 것인가? all_fields.rule.id
    Check 어떤 기준으로 비교할 것인가? Starts with
    Value 비교할 실제 값은 무엇인가? STAGE_1
  5. Set Storage
    • 노드 이름: Prepare_Reset_Data (자유롭게 지정 / 추후 이어짐)
    • 예시 코드
    • import json def handler(event, context): # 1. 이전 노드($exec)로부터 agent_id 추출 agent_id = event.get("all_fields", {}).get("agent", {}).get("id", "default_agent") # 2. 저장할 때 사용할 Key와 초기화할 Value 정의 return { "storage_key": f"score_{agent_id}", "reset_value": { "total_score": 0, "sca_applied": False } }
    4.2. set cache value
    • Key: {{ $Prepare_Reset_Data.storage_key }}
    • Value: {{ $Prepare_Reset_Data.reset_value }}
  6. 4.1. execute python

공격 분류 (Nodes 5)

  1. 로그 분류
    • 노드 이름 : Log_Classifier (”%)
    • 단순히 문자열 태그를 생성
    • 예시 코드
    • def handler(event, context): all_fields = event.get("all_fields", {}) # 1. 데이터 추출 remote_ip = all_fields.get("data", {}).get("fd", {}).get("rip") rule_groups = all_fields.get("rule", {}).get("groups", []) # 2. 분류 로직 # ① IP 정보가 있고 Falco 관련 로그일 때 if remote_ip and any("falco" in g.lower() for g in rule_groups): category = "IP_Falco" # ② IP는 없지만 Falco 관련 로그일 때 (프로세스/명령어 기반) elif any("falco" in g.lower() for g in rule_groups): category = "Process_Falco" # ③ 파일 무결성 감시(FIM) 관련 로그일 때 elif any("syscheck" in g.lower() for g in rule_groups) or "syscheck" in all_fields.get("rule", {}).get("id", ""): category = "FIM" else: category = "Unknown" return { "category": category, "remote_ip": remote_ip }
    5.2. filter항목 의미 예시
    Input List 어떤 노드의 데이터를 가져올 것인가? {{ $Log_Classifier }}
    Field 가져온 데이터 중 어떤 항목을 비교할 것인가? category
    Check 어떤 기준으로 판별할 것인가? (연산자) EQUALS (일치함)
    Value 비교 대상이 되는 실제 값은 무엇인가? IP_Falco (또는 FIM 등)
  2. 5.1. execute python

misp 조회 및 wazuh 조회 (Nodes 6~8)

  1. misp 조회
    • 입력값항목 (Field) 설정해야 할 값 의미
      Value {{ 조회할 대상 데이터 }} 가장 중요합니다. 실제 IP나 Hash 값이 들어가는 곳입니다.
      Type (조회 대상에 맞게 선택) IP면 ip-dst, 해시면 sha256 등을 선택하여 검색 속도를 높입니다.
      Returnformat json 셔플 다음 노드(점수 합산)에서 데이터를 처리하기 가장 쉬운 형식입니다.
      EnforceWarninglist True 구글, 클라우드플레어 등 안전한 IP를 악성으로 오탐하는 것을 방지합니다.
      • 기타 옵션 (추가하고 싶으면)필드 이름 추천 설정값 의미 및 설정 이유
        Enforcewarninglist True (필수 권장) Google, Cloudflare 등 '안전한 IP'가 MISP 블랙리스트에 실수로 등록된 경우 이를 결과에서 제외합니다. 오탐(False Positive)을 획기적으로 줄여줍니다.
        IncludeContext True 위협 정보만 가져오는 게 아니라, 해당 위협이 어떤 공격 그룹(APT 등)과 관련 있는지 등의 맥락 정보를 함께 가져옵니다. 나중에 보고서(Slack)를 쓸 때 풍부한 내용을 담을 수 있습니다.
        Returnformat json (필수) 셔플의 다음 노드(Python 점수 합산)에서 데이터를 구조적으로 읽으려면 반드시 JSON 형식이어야 합니다.
        To IDS True 질문자님이 언급하신 대로, 탐지 시스템에서 유의미하다고 판단된(중요도가 높은) 속성들만 필터링해서 조회합니다.
        Limit 10 결과가 너무 많으면 워크플로우 속도가 느려질 수 있습니다.
        Published True 이미 발행된 공식 위협 정보만 조회하겠다는 뜻입니다.
      6.1. ip 대조
      • Value: {{ $exec.all_fields.data.fd.rip }}
      • Type: ip-dst 또는 ip-src
      6.2.1. 백도어 hash 대조
      • Value: {{ $exec.all_fields.data.sha256 }}
      • Type: sha256
      6.3. FIM hash 대조
      • Value: {{ $exec.all_fields.syscheck.sha256_after }}
      • Type: sha256
    1. wazuh api로 FIM 통계 → wazuh 앱 말고 http앱 이용
      • http 앱 사용시 권장사항 (표에 일괄정리)
        • URL 구조: 파라미터(예: ?status=failed)는 Url 칸 끝에 직접 붙여서 입력하는 것이 가장 확실합니다.
        • Headers 입력 형식: Key: Value 형식으로 입력해야 합니다. (Wazuh 앱에서는 =를 썼지만, 일반 HTTP 앱은 :를 사용하는 경우가 많으니 주의하세요.)
        • Verify (SSL): 내부망에서 사용하는 Wazuh 서버라면 보안 인증서가 공인되지 않았을 확률이 높으므로, 반드시 false로 설정해야 'SSL Certificate Verify Failed' 에러를 피할 수 있습니다.
        • Timeout: 네트워크 환경에 따라 API 응답이 느릴 수 있으므로, timeout 칸에 10 정도를 입력해 두는 것이 좋습니다.
      항목 (Field) 입력할 내용 (Value) 의미
      Method GET (앱 이름 아래 Action 선택에서 GET 선택)
      Url https://<WAZUH_IP>:55000/syscheck/{{ $exec.all_fields.agent.id }} <WAZUH_IP>는 실제 Wazuh 매니저 IP로 변경하세요.
      Headers Content-Type: application/json  
      Authorization: Bearer {{ $Wazuh_Auth.token }} 인증 토큰을 포함합니다. (줄바꿈으로 구분)  
      Verify false 사설 SSL 인증서 사용 시 체크를 해제(false)해야 연결됩니다.
      Username / Password (비워둠) Bearer 토큰 방식을 쓰므로 입력하지 않습니다.
      http_proxy (비워둠) 셔플 서버와 Wazuh 매니저 사이에 별도의 프록시 서버(대리 서버)를 거쳐야 하는 특수한 보안 환경이 아니라면 필요 없습니다.
      timeout 10 (입력 권장) 비워두면 무한정 대기하거나 시스템 기본값에 따릅니다. API 응답이 늦어질 경우 워크플로우 전체가 멈추는 걸 방지하기 위해 10(초) 정도 적어주는 것이 좋습니다.
      to_file false (또는 비워둠) 중요: 이 값을 true로 하면 API 결과가 파일 형태로 저장되어 나중에 파이썬 노드에서 텍스트로 읽기가 매우 까다로워집니다. 점수 계산을 위해 데이터를 직접 처리해야 하므로 반드시 false여야 합니다.
    2. wazuh api로 SCA 통계 → wazuh 앱 말고 http앱 이용항목 (Field) 입력할 내용 (Value) 의미
      Method GET 데이터를 가져오는 조회 방식입니다.
      Url https://<WAZUH_IP>:55000/sca/{{ $exec.all_fields.agent.id }}/checks?status=failed ?status=failed를 끝에 붙여 취약 항목만 가져옵니다.
      Headers Content-Type: application/json  
      Authorization: Bearer {{ $Wazuh_Auth.token }} 인증 토큰을 포함합니다.  
      Verify false SSL 인증서 검증 건너뛰기 설정입니다.
      Username / Password (비워둠) 위와 동일하게 비워둡니다.
      http_proxy (비워둠) 셔플 서버와 Wazuh 매니저 사이에 별도의 프록시 서버(대리 서버)를 거쳐야 하는 특수한 보안 환경이 아니라면 필요 없습니다.
      timeout 10 (입력 권장) 비워두면 무한정 대기하거나 시스템 기본값에 따릅니다. API 응답이 늦어질 경우 워크플로우 전체가 멈추는 걸 방지하기 위해 10(초) 정도 적어주는 것이 좋습니다.
      to_file false (또는 비워둠) 중요: 이 값을 true로 하면 API 결과가 파일 형태로 저장되어 나중에 파이썬 노드에서 텍스트로 읽기가 매우 까다로워집니다. 점수 계산을 위해 데이터를 직접 처리해야 하므로 반드시 false여야 합니다.

가중치 합산 및 저장(Nodes 9~10)

  1. 점수 합산 (execute python)
    • 예시 파이썬 코드
    • import json def handler(event, context): # 1. 이전 단계 데이터 가져오기 (노드 이름 확인 필수) # Get_Cache_2: 2번 Get Storage 결과 # MISP_Search: 7번 MISP 결과 # Wazuh_FIM: 8번 FIM 통계 결과 # Wazuh_SCA: 9번 SCA 결과 current_cache = event.get("Get_Cache_2", {}) misp_data = event.get("MISP_Search", {}) fim_data = event.get("Wazuh_FIM", {}) sca_data = event.get("Wazuh_SCA", {}) total_score = current_cache.get("total_score", 0) sca_applied = current_cache.get("sca_applied", False) # 2. MISP 점수 가산 (블랙리스트 일치 시 +30) if misp_data and len(misp_data.get("Attribute", [])) > 0: total_score += 30 # 3. FIM 점수 가산 (최근 변경 이력 존재 시 +20) if fim_data and fim_data.get("affected_items"): total_score += 20 # 4. SCA 점수 가산 (중복 합산 방지 로직) if not sca_applied: failed_count = sca_data.get("total_failed", 0) if failed_count > 0: total_score += min(failed_count * 5, 40) # 최대 40점 제한 sca_applied = True # 5. 최종 결과 반환 return { "total_score": total_score, "sca_applied": sca_applied, "agent_id": event.get("all_fields", {}).get("agent", {}).get("id"), "raw_log": event.get("all_fields") }
  2. Set cache value (데이터 업데이트)
    • Key: score_{{ $10_Execute_Python.agent_id }}
    • Value: {{ $10_Execute_Python }} (JSON 전체 저장)

임계치 판단 및 자동대응(Nodes 11~12)

  1. 임계치 판단 (execute python)
  2. def handler(event, context): # 1. 11번(또는 10번) 노드에서 계산된 최종 데이터 가져오기 # 노드 이름이 'Set_Cache_Update'라고 가정할 때의 예시입니다. final_data = event.get("Set_Cache_Update", {}) score = final_data.get("total_score", 0) agent_id = final_data.get("agent_id", "unknown") # 공격자 IP 추출 (로그 구조에 따라 경로 확인 필요) attacker_ip = final_data.get("raw_log", {}).get("data", {}).get("fd", {}).get("rip", "unknown") # 2. 결과 저장을 위한 기본 구조 action_plan = { "score": score, "agent_id": agent_id, "attacker_ip": attacker_ip, "severity": "", "should_isolate": False, "should_block_ip": False, "should_notify_slack": False, "should_log_internally": False } # 3. 임계치 판단 로직 (12단계) if score >= 90: action_plan["severity"] = "Critical" action_plan["should_isolate"] = True action_plan["should_block_ip"] = True action_plan["should_notify_slack"] = True elif score >= 80: action_plan["severity"] = "High" action_plan["should_block_ip"] = True action_plan["should_notify_slack"] = True elif score >= 60: action_plan["severity"] = "Medium" action_plan["should_notify_slack"] = True else: action_plan["severity"] = "Low" action_plan["should_log_internally"] = True return action_plan
  3. 자동 대응12.1.1. filter 노드
    • Input list: {{ $Action_Planner }}
    • Field: should_isolate
    • Check: Equals
    • Value: true (소문자로 입력)
    • Opposite: False
    12.1.2. wazuh 노드 이용
    • Action 선택: Run Command
    • Custom Path: (?PUT)/active-response/{{ $Action_Planner.agent_id }}
    • Headers:
    • Content-Type: application/json Authorization: Bearer {{ $Wazuh_Auth.token }}
    • Body (JSON):
    • Content-Type: application/json Authorization: Bearer {{ $Wazuh_Auth.token }}
    12.2. IP 차단
    • Input list: {{ $Action_Planner }}
    • Field: should_block_ip
    • Check: Equals
    • Value: true
    • Opposite: False
    12.2.2. firewall 노드 이용 → cisco나 fortinet등
    • Action 선택: Block IP (또는 Deny IP)
    • IP Address: {{ $Action_Planner.attacker_ip }}
    • Reason: Shuffle Auto-Block (Score: {{ $Action_Planner.score }})
    12.3. slack 알림 전송
    • Input list: {{ $Action_Planner }}
    • Field: should_notify_slack
    • Check: Equals
    • Value: true
    • Opposite: False
    12.3.2. slack 노드 이용
    • Action 선택: Send Message
    • Channel ID: 알림을 받을 채널 이름 (예: #soc-alerts)
    • Text (본문):
    • 🚨 [{{ $Action_Planner.severity }}] 등급 위협 감지 및 자동 대응 보고위협 점수: {{ $Action_Planner.score }}점대상 Agent ID: {{ $Action_Planner.agent_id }}공격자 IP: {{ $Action_Planner.attacker_ip }}현재 시스템에 의해 자동 격리/차단 조치가 수행되었습니다.
    12.4. DB로깅
    • Input list: {{ $Action_Planner }}
    • Field: should_log_internally
    • Check: Equals
    • Value: true
    • Opposite: False
    12.4.2. 생각해봐야함
  4. 12.4.1. filter 노드
  5. 12.3.1. filter 노드
  6. 12.2.1. filter 노드
  7. 12.1. 네트워크 격리