SOC Prime Bias: 중간

06 1월 2026 18:50

코드에서 커버리지까지 (2부): 공백의 악몽

Author Photo
Ruslan Mikhalov Chief of Threat Research at SOC Prime linkedin icon 팔로우
코드에서 커버리지까지 (2부): 공백의 악몽
shield icon

Detection stack

  • AIDR
  • Alert
  • ETL
  • Query

요약

이 글은 Impacket 도구에 의해 생성된 LDAP 필터가 Active Directory에 의해 어떻게 일관되지 않은 공백을 도입하는 방식으로 정규화되는지를 설명합니다. 이벤트 ID 1644 로그. 이러한 형식 변경은 기본 필터 논리가 동일하더라도 정확한 문자열 매칭에 의존하는 탐지를 깨뜨릴 수 있습니다. 저자는 기본 “포함” 논리가 얼마나 취약한지를 보여주고, 공백 차이, 대소문자의 변화 및 연산자 변동을 허용하는 정규 표현식을 사용하여 견고한 시그마 탐지를 구축하는 과정을 안내합니다.

조사

문제를 검증하기 위해, 저자는 운영 중인 실제 이벤트 1644 기록을 검토하고 동일한 비트 연산 LDAP 필터의 여러 공백 조합(예: 다른 공백, 괄호 배치 및 형식을 가진 userAccountControl&524288의 변형)을 문서화했습니다. 여러 탐지 접근 방식이 시도되었으며, 정적 문자열 검사를 시작으로 점점 더 유연한 정규식 패턴으로 발전하여, 모든 관측된 표현을 과발화 없이 일관되게 일치시키는 규칙을 구현했습니다.

완화

선택적 공백을 허용하고, 대소문자 구분 없는 매칭을 지원하며, AND/OR 연산자 스타일을 고려하는 정규식 기반의 탐지를 채택하세요. 성능을 적정하게 유지하기 위해, 더 비용이 많이 드는 정규식 논리를 적용하기 전에 대상 속성 이름에서 사전 필터링을 수행하세요. 마지막으로, 현장에서 목격된 모든 공백 및 형식 변형을 포착한 엄선된 ‘부끄러움의 벽’ 본문과 탐지를 지속적으로 검증하세요.

대응

의심스러운 LDAP 필터 매칭이 트리거되면, SOC에 통지하여 잠재적인 권한-탐색 또는 권한 상승 관련 열거 활동을 평가하도록 하세요. 의도를 파악하고 범위를 결정하기 위해 이벤트를 소스 호스트/IP, 요청 사용자 및 기타 LDAP 속성과 연결하세요. 오탐이 발생할 경우, 이미 알려진 형식 변형에 대한 커버리지를 유지하면서 임계값과 규칙 조건을 조정하세요.

공격 흐름

이 부분을 아직 업데이트 중입니다. 알림을 받으려면 가입하세요.

알림 설정

시뮬레이션 실행

전제 조건: Telemetry & Baseline 사전 점검이 통과해야 합니다.

논리: 이 섹션은 탐지 규칙을 트리거하기 위해 설계된 공격자 기술(TTP)의 정밀한 실행을 상세히 설명합니다. 명령과 설명은 식별된 TTP를 직접 반영하고, 탐지 논리가 예상하는 정확한 원격 측정을 생성해야 합니다.

  • 공격 내러티브 및 명령:
    낮은 권한의 도메인 자격 증명을 가진 공격자가 측면 이동을 위한 권한 계정을 찾고자 합니다. 그들은 userAccountControl 을 비트 연산 AND하여 관리자 플래그(값 524288)를 가진 계정을 격리하는 LDAP 필터를 작성합니다. 간단한 문자열 매칭을 피하기 위해, 그들은 Sigma 규칙이 예상하는 대로 정확히 여분의 공백과 괄호를 추가합니다.

    1. 공백 변형을 사용하여 LDAP 필터 문자열을 만듭니다.
    2. 도메인 컨트롤러에 대해 ldapsearch (PowerShell 통해)를 사용하여 LDAP 검색을 실행하세요.
    3. 보안 이벤트 로그가 작성한 필터를 포함하는 이벤트 1644를 기록하고 있는지 확인합니다.
  • 회귀 테스트 스크립트:

    # --------------------------------------------------------------
    # Sigma 규칙을 트리거해야 하는 LDAP 비트 연산 필터 시뮬레이션
    # --------------------------------------------------------------
    
    # 매개 변수
    $DomainController = "dc01.example.com"
    $BaseDN = "DC=example,DC=com"
    $Filter = "(   userAccountControl   &   524288   )"
    $Attributes = "distinguishedName,samAccountName,userAccountControl"
    
    # LDAP 쿼리 실행
    try {
        $result = [ADSI]"LDAP://$DomainController/$BaseDN"
        $searcher = New-Object System.DirectoryServices.DirectorySearcher($result)
        $searcher.Filter = $Filter
        $searcher.PropertiesToLoad.AddRange($Attributes.Split(','))
        $searcher.PageSize = 1000
    
        $entries = $searcher.FindAll()
        foreach ($entry in $entries) {
            $dn = $entry.Properties["distinguishedname"][0]
            $sam = $entry.Properties["samaccountname"][0]
            $uac = $entry.Properties["useraccountcontrol"][0]
            Write-Output "Found: $sam ($dn) – UAC=$uac"
        }
    } catch {
        Write-Error "LDAP 쿼리 실패: $_"
    }
  • 정리 명령:

    # 지속적인 변경은 없었습니다; ADSI 연결만 닫으세요.
    Write-Output "정리 완료."